<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Boundary Devices &#187; Blog</title>
	<atom:link href="http://boundarydevices.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://boundarydevices.com</link>
	<description>ARM Single Board Computers</description>
	<lastBuildDate>Thu, 17 May 2012 22:35:07 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
<xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" />
		<item>
		<title>Git repositories have moved to GitHub</title>
		<link>http://boundarydevices.com/git-repositories-have-moved-to-github/</link>
		<comments>http://boundarydevices.com/git-repositories-have-moved-to-github/#comments</comments>
		<pubDate>Thu, 17 May 2012 00:28:45 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://boundarydevices.com/?p=1205</guid>
		<description><![CDATA[As a part of our web-site redesign, we&#8217;ve moved our git repositories to GitHub. You can see all of the repositories using this link: http://github.com/boundarydevices The most active at the moment are: Linux on i.MX6, branch boundary-imx-android-r13.2. U-Boot on i.MX6, branch boundary-imx-android-r13.2 As you can probably guess from the branch names, these were based on [...]]]></description>
			<content:encoded><![CDATA[<p>As a part of our web-site redesign, we&#8217;ve moved our git repositories to GitHub.</p>
<p>You can see all of the repositories using this link:<br />
         <a href="http://github.com/boundarydevices" title="Boundary Devices' Open Source">http://github.com/boundarydevices</a></p>
<p>The most active at the moment are:</p>
<p><a href="https://github.com/boundarydevices/linux-imx6/tree/boundary-imx-android-r13.2" title="Linux on i.MX6">Linux on i.MX6, branch boundary-imx-android-r13.2.</a><br /> </p>
<br />
<p><a href="https://github.com/boundarydevices/u-boot-2009-08/tree/boundary-imx-android-r13.2" title="U-Boot on i.MX6">U-Boot on i.MX6, branch boundary-imx-android-r13.2</a><br /></p>
<p>As you can probably guess from the branch names, these were based on Freescale&#8217;s Android R13.2 release. 
We hope to be moving to main-line U-Boot shortly.</p>
]]></content:encoded>
			<wfw:commentRss>http://boundarydevices.com/git-repositories-have-moved-to-github/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Welcome to our redesigned website!</title>
		<link>http://boundarydevices.com/welcome-to-our-redesigned-website/</link>
		<comments>http://boundarydevices.com/welcome-to-our-redesigned-website/#comments</comments>
		<pubDate>Mon, 14 May 2012 18:31:39 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://boundarydevices.com/?p=1145</guid>
		<description><![CDATA[Welcome to our redesigned website.  We hope you will enjoy all of the new features that have been added.  Over the next few weeks, we will be adding more content so check back soon.]]></description>
			<content:encoded><![CDATA[<p>Welcome to our redesigned website.  We hope you will enjoy all of the new features that have been added.  Over the next few weeks, we will be adding more content so check back soon.</p>]]></content:encoded>
			<wfw:commentRss>http://boundarydevices.com/welcome-to-our-redesigned-website/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>U-Boot conventions for i.MX6 (Nitrogen6X and SabreLite)</title>
		<link>http://boundarydevices.com/u-boot-conventions-for-i-mx6-nitrogen6x-and-sabrelite/</link>
		<comments>http://boundarydevices.com/u-boot-conventions-for-i-mx6-nitrogen6x-and-sabrelite/#comments</comments>
		<pubDate>Mon, 02 Apr 2012 18:11:09 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Miscellaneous]]></category>

		<guid isPermaLink="false">http://boundarydevices.com/blogs/?p=905</guid>
		<description><![CDATA[A little convention make things easy to use, but only if you know the convention. In this blog post, we&#8217;ll explain three key environment variable conventions used on the Nitrogen6X (and Sabre Lite before it). For the impatient: Both of these devices ship with U-Boot configured to load and run a boot script named 6q_upgrade [...]]]></description>
			<content:encoded><![CDATA[<p>A little convention make things easy to use, but only if you know the convention.</p>
<p>In this blog post, we&#8217;ll explain three key environment variable conventions used on the Nitrogen6X (and Sabre Lite before it).</p>
<h2>For the impatient:</h2>
<ul>
<li>Both of these devices ship with U-Boot configured to load and run a boot script named <tt>6q_upgrade</tt> from the root of either SD card.</li>
<li>A pre-configured command <tt>upgradeu</tt> can be used to upgrade U-Boot in serial flash </li>
<li>Another pre-configured command <tt>clearenv</tt> will erase the environment variables in serial flash.</li>
</ul>
<h2>For those that want an explanation:</h2>
<p>Both of these boards boot to SPI EEPROM (serial flash), and both U-Boot and its&#8217; environment variables are stored there. <br/></p>
<p>Convention for many modern ARM boards, including i.MX in recent years has been to boot directly to SD card, either by using unformatted areas of the disk or carefully placing special files in prescribed areas of a filesystem. <br/></p>
<p>We jokingly refer to this as a game of &#8220;Hide the U-Boot&#8221;, and don&#8217;t want to play. <br/></p>
<p>Instead, we added a serial flash chip and store the boot loader there. The flash device size is 2MB, which is plenty of space for the boot loader and environment, but not enough for a complete O/S, and we defer that to SD card, which brings us to the first environment variable:</p>
<h2>bootcmd</h2>
<p>We&#8217;ve pre-configured the <tt>bootcmd</tt> environment variable to load and launch a boot script from the root directory of either SD card slot by using the looping features of the Hush parser:</p>
<pre class="brush: shell">
for disk in 0 1 ; do
	mmc dev ${disk} ;
        for fs in fat ext2 ; do
                ${fs}load mmc ${disk}:1 10008000 /6q_bootscript &#038;&#038;
                source 10008000 ;
        done ;
done
</pre>
<p>If you&#8217;re not familiar with U-Boot boot scripts, we wrote a backgrounder <a href="http://boundarydevices.com/boot-scripts-for-nitrogen-i-mx51" title="U-Boot script backgrounder">in this blog post</a>.</p>
<p>To make compiling boot scripts a bit easier, we created a <a href="http://git.boundarydevices.com/bootscript.php" title="Web tool to compile boot scripts">web-based tool</a> to compile them for you. There&#8217;s also a <a href="http://boundarydevices.com/boot-scripts-for-nitrogen-i-mx51#comment-43" title="Bash function to compile a boot script">bash snippet</a> to make this easy from the command-line.</p>
<h2>upgradeu</h2>
<p>Once you&#8217;re running U-Boot from serial EEPROM, the question is how do you upgrade it?<br/><br />
The easy answer is that you ask U-Boot to do it:</p>
<pre class="brush: shell">
MX6Q SABRELITE U-Boot > run upgradeu
...bunch of debug spew
## Executing script at 10008000
check U-Boot
Loading file "u-boot.bin" from mmc device 1:1 (xxb1)
179632 bytes read
...more debug spew...
SUCCESS
Total of 179632 bytes were the same
------- U-Boot versions match
</pre>
<p>If it&#8217;s not entirely obvious to you what this example illustrated, let&#8217;s dig a little deeper. <br/><br />
If you run <tt>print</tt> at the U-Boot prompt, you&#8217;ll see that <tt>upgradeu</tt> is defined to contain some U-Boot commands:</p>
<pre class="brush:shell">
MX6Q SABRELITE U-Boot > print
bootdelay=3
...
upgradeu=for disk in 0 1 ; do mmc dev ${disk} ;for fs in fat ext2 ; do ${fs}load mmc ${disk}:1 10008000 /6q_upgrade &#038;&#038; source 10008000 ; done ; ...
Environment size: 908/8188 bytes
</pre>
<p>That&#8217;s a pretty long command-line, but is essentially the same loop as we described in the <tt>bootcmd</tt> section of this post. It will try to read a script named <tt>6q_upgrade</tt> from either SD card using either the FAT or ext2/3/4 filesystem and <tt>source</tt> it. The <tt>source</tt> command simply executes a boot script.</p>
<p>If you&#8217;ve followed along this far, you may have just realized that we haven&#8217;t really followed either the full boot path or the upgrade path because we&#8217;ve waved our hands over what the script does. We&#8217;ll address that in a <a href="#scriptcontent" title="Script content">later section</a>. <br/></p>
<h2>clearenv</h2>
<p>The final convention may be the most useful for the programmer. If a Sabre Lite or Nitrogen6X board spends much time on your desk, you&#8217;ll invariably set something in the U-Boot environment. Perhaps you&#8217;re bypassing all of these conventions and booting over NFS (the topic of another blog post). Maybe you&#8217;re booting to a SATA drive or a USB stick or tweaking some kernel parameters.</p>
<p>If so, you may ultimately want to put things back the way you found them before you hand the device off to someone else. If you know the layout of the serial EEPROM, you can simply erase the sector(s) containing the environment variables and go back to the compiled-in defaults. </br></p>
<p>I&#8217;m assuming you don&#8217;t (know or remember the flash layout) because I don&#8217;t and I helped come up with it. This is where the <tt>clearenv</tt> variable or command comes into play. Going back to the <tt>print</tt> example will tell you:</p>
<pre class="brush:shell">
MX6Q SABRELITE U-Boot > print
bootdelay=3
...
clearenv=sf probe 1 &#038;&#038; sf erase 0xc0000 0x2000 &#038;&#038; echo restored environment to factory default
...
Environment size: 908/8188 bytes
</pre>
<p>Aha! The environment is stored at offset <tt>0xc0000</tt> (768k) and is <tt>0x2000</tt> (8192) bytes long!</p>
<p>And if you want to clear it, you can simply run <tt>run clearenv</tt> from the shell.</p>
<p><a name="scriptcontent"></p>
<h2>The gory details</h2>
<p></a><br />
Unless you&#8217;re as much of a U-Boot geek as I am, you&#8217;re probably wishing you&#8217;d stopped at the <b>for the impatient</b> section, but you may someday need the details, so I&#8217;m going to describe a handful of additional things that will help in the use and deployment of boot scripts for your use.<br/></p>
<p>To begin with, I promised the details of the boot script itself, but I lied. <br/></p>
<p>I can&#8217;t really give that to you because the things needed really depend on your environment. If you&#8217;ve spent time with various embedded Linux distributions, you&#8217;ll find that many of them require different sorts of kernel command-line parameters. Also, some distributions, like Android require the use of a RAM disk, but others don&#8217;t. These differences (additions) are best expressed in the actual userspace image, so we add them there. <br/></p>
<p>Here&#8217;s an example of what we shipped on SabreLite boards with Ubuntu:</p>
<h3>Ubuntu boot script</h3>
<pre class="brush: shell">
set bootargs $bootargs root=/dev/mmcblk0p1 rootwait fixrtc ;
ext2load mmc ${disk}:1 10800000 /boot/uImage &#038;&#038; bootm 10800000 ;
echo "Error loading kernel image"
</pre>
<p>If you look closely, you&#8217;ll see that it&#8217;s adding a couple of things to the <tt>bootargs</tt> environment variable, then loading the kernel from <tt>/boot/uImage</tt> and booting it using <tt>bootm</tt> and that it&#8217;s printing an error message in case something fails. The <tt>bootm</tt> command will normally not return.</p>
<p>Also note that this script uses the <tt>disk</tt> variable that was defined in the loop from which it was called, so the boot script must reside on the same disk as the kernel.</p>
<h3>Android boot script</h3>
<p>Here&#8217;s another example of loading Android ICS on an i.MX6:</p>
<pre class="brush: shell">
${fs}load mmc ${disk}:1 10800000 uimage &#038;&#038;
${fs}load mmc ${disk}:1 12800000 uramdisk.img &#038;&#038;
bootm 10800000 12800000 ;
echo "Error loading kernel image"
</pre>
<p>This one doesn&#8217;t add anything to the <tt>bootargs</tt>, and uses the <tt>&amp;&amp;</tt> operators liberally, but the primary thing it does differently from the Ubuntu script is load a ram disk (<tt>uramdisk.img</tt>). It also uses the <tt>fs</tt> parameter from the calling loop in the same way the Ubuntu script used the <tt>disk</tt> variable.</p>
<p>A lot more could be done inside this boot script. The Android boot script could (and probably should) handle <tt>recovery mode</tt>. Both of them should probably loop in the case of errors, although we don&#8217;t yet have display support in i.MX6 U-Boot.</p>
<p>We should also add support for <tt>SATA</tt> into our default environment, but those are topics for another day and perhaps another blog post.</p>
<h3>Upgrade script</h3>
<p>The content of the <tt>6q_upgrade</tt> script is a little more complex, but shouldn&#8217;t be too surprising. It tries to load a file named <tt>u-boot.bin</tt> from the same partition in which the script was found and compares it against the content of the serial EEPROM. If the two are different, it will wait for 10 seconds before (re)programming the flash to match the content of the SD card. It will also perform a verification pass before telling you that things are good:</p>
<pre class="brush: shell">
echo "check U-Boot" ;
if ${fs}load mmc ${disk}:1 12000000 u-boot.bin ; then
      echo "read $filesize bytes from SD card" ;
      if sf probe 1 27000000 ; then
	   echo "probed SPI ROM" ;
           if sf read 0x12400000 0 $filesize ; then
               if cmp.b 0x12000000 0x12400000 $filesize ; then
                   echo "------- U-Boot versions match" ;
               else
                   echo "Need U-Boot upgrade" ;
                   echo "Program in 10 seconds" ;
                   for n in 9 8 7 6 5 4 3 2 1 0 ; do
                        echo $n ;
                        sleep 1 ;
                   done
		   echo "erasing" ;
                   sf erase 0 0x40000 ;
		   # two steps to prevent bricking
		   echo "programming" ;
                   sf write 0x12000000 0 $filesize ;
		   echo "verifying" ;
                   if sf read 0x12400000 0 $filesize ; then
                       if cmp.b 0x12000000 0x12400000 $filesize ; then
                           while echo "---- U-Boot upgraded. reset" ; do
				sleep 120
			   done
                       else
                           echo "Read verification error" ;
                       fi
                   else
                        echo "Error re-reading EEPROM" ;
                   fi
               fi
           else
               echo "Error reading boot loader from EEPROM" ;
           fi
      else
           echo "Error initializing EEPROM" ;
      fi ;
else
     echo "No U-Boot image found on SD card" ;
fi
</pre>
<h2>Recap and best practices</h2>
<p>I should probably move this section to the top of the document, but here&#8217;s some additional rationale for how and why we use the boot script scheme.<br/></p>
<h3>Two-file U-Boot upgrades</h3>
<p>To build an SD card to upgrade U-Boot, you can copy just two files (<tt>6q_upgrade</tt> and <tt>u-boot.bin</tt>)to a fresh, out-of-the-box SD card (which tend to come formatted as FAT32), insert the card and run <tt>run upgradeu</tt>.</p>
<h3>Single partition Ubuntu</h3>
<p>To run Ubuntu, LTIB or Timesys single-partition images, you can create an SD card by simply formatting an <tt>ext2/3/4</tt> partition and copy your filesystem image, boot script and kernel using <tt>cp</tt>. There&#8217;s no need to use <tt>dd</tt> or any other special formatting commands.</p>
<h3>Windows CE, too</h3>
<p>I guess I should say <i>Windows Embedded 7</i>. As the name implies, U-Boot <i>is</i> the <b>Universal Boot Loader</b>, and it can load and launch Windows Embedded using a boot script in a very similar manner as Linux. In this environment, you shouldn&#8217;t need to format the SD card at all: FAT32 should work.</p>
<h3>And NFS</h3>
<p>It may not be clear, but the use of a boot script on SD can also be useful in booting over NFS. This may sound like an oxymoron, but it&#8217;s often more convenient to write a network load script and copy it to an SD card than it is to hook up a serial port and tweak settings there.<br/></p>
<p>This is especially true for those that infrequently use a serial port or when devices are enclosed in a manner that makes access to the serial port difficult.</p>
<h3>Best practices</h3>
<p>If U-Boot and environment variables are all stored on SD card, you don&#8217;t have much choice about what goes where, and environment variables are often used and changed to reflect the combination of distribution-related variables like whether or not a RAM disk is loaded and machine-specific variables like ethernet addresses or display settings.</p>
<p>When the two are separated as they are in SabreLite and Nitrogen6 boards, you should try to use environment variables for only those things which are tied to the board, such as the type of display. Any distribution-related <tt>bootargs</tt> should be added by the boot script.</p>
<p>If we haven&#8217;t yet convinced you that this is a good idea, I hope that at least you&#8217;ll understand what you&#8217;re seeing when you receive a board on your desk. At that point, I&#8217;ll refer you to <a href="https://wiki.linaro.org/Boards/MX6QSabreLite" title="The Linaro way">the Linaro way</a>, which allows you to override all of this and play &#8220;Hide the U-Boot&#8221; using a small shim in SPI ROM.</p>
<p>For what it&#8217;s worth, you can load and program that using the <tt>upgradeu</tt> command and the <tt>6q_upgrade</tt> script by naming the shim <tt>u-boot.bin</tt>.</p>
]]></content:encoded>
			<wfw:commentRss>http://boundarydevices.com/u-boot-conventions-for-i-mx6-nitrogen6x-and-sabrelite/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Revisiting LTIB and Qt on i.MX51 and i.MX53</title>
		<link>http://boundarydevices.com/revisiting-ltib-and-qt-on-i-mx51-and-i-mx53/</link>
		<comments>http://boundarydevices.com/revisiting-ltib-and-qt-on-i-mx51-and-i-mx53/#comments</comments>
		<pubDate>Sun, 11 Dec 2011 19:45:20 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Miscellaneous]]></category>
		<category><![CDATA[i.MX51]]></category>
		<category><![CDATA[i.MX53]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[LTIB]]></category>
		<category><![CDATA[Qt]]></category>
		<category><![CDATA[webkit]]></category>

		<guid isPermaLink="false">http://boundarydevices.com/blogs/?p=846</guid>
		<description><![CDATA[In a previous post, we walked through the process of building Qt using the LTIB tool, but we were a bit cavalier about the steps involved. Since that time, we&#8217;ve been shipping demo software based on LTIB and Qt with our Nitrogen53 and Nitrogenboards, and the lack of documentation shows. In order to remedy that [...]]]></description>
			<content:encoded><![CDATA[<p>In <a href="http://boundarydevices.com/ltib-and-qt-on-i-mx51" title="a previous post">a previous post</a>, we walked through the process of building Qt using the LTIB tool, but we were a bit cavalier about the steps involved.</p>
<p>Since that time, we&#8217;ve been shipping demo software based on LTIB and Qt with our <a href="/products-2/nitrogen53-board-imx53-arm-cortex-a8-sbc/" title="Nitrogen53">Nitrogen53</a> and <a href="/products-2/nitrogen-e-board-imx51-power-over-ethernet-sbc/" title="Nitrogen">Nitrogen</a>boards, and the lack of documentation shows.</p>
<p>In order to remedy that and allow our customers to replicate our results, this post will walk through the steps to build an Ubuntu-based VirtualBox image that contains LTIB and Qt. A set of future blog posts will detail the steps we&#8217;ve used to translate that build into our standard Qt demo package.</p>
<p>Since developer machines come and go, the VM image is a good way to control our build process and minimize the number of unknowns when deploying systems.</p>
<h3>Step 1: Build the VM</h3>
<p>We built the VirtualBox image by starting with the <a href="https://help.ubuntu.com/community/Installation/MinimalCD" title="Ubuntu Minimal CD">Ubuntu Minimal CD</a> to try and keep things small. Our purpose is setting up a build machine, so we started with the &#8220;OpenSSH Server&#8221; profile, which allows us to fork the VM off into a second workspace and primarily access the device over SSH.</p>
<p>We chose the 32-bit PC (x86) distribution and used 10.04 &#8220;Lucid Lynx&#8221;.</p>
<p>We created a single user: <code>boundary</code>, password <code>boundary</code>.</p>
<h3>Step 2: Configure with pre-requisites</h3>
<p>In order to simplify things, we captured the full set of Ubuntu packages included on the build machine and placed on our web-site at<br />
<a href="http://boundarydevices.com/ltib-installed-packages" title="ltib-installed-packages">http://boundarydevices.com/ltib-installed-packages</a>. You can copy these by piping them into <code>apt-get install</code> like so:</p>
<pre class="brush: shell">
boundary@ubuntu:~$ wget http://boundarydevices.com/ltib-installed-packages
--2011-12-10 18:10:08--  http://boundarydevices.com/ltib-installed-packages
Resolving boundarydevices.com... 69.89.31.84
Connecting to boundarydevices.com|69.89.31.84|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 6093 (6.0K) 
Saving to: `ltib-installed-packages'

100%[======================================>] 6,093       --.-K/s   in 0.04s

2011-12-10 18:10:08 (144 KB/s) - `ltib-installed-packages' saved [6093/6093]

boundary@ubuntu:~$ cat ltib-installed-packages | xargs sudo apt-get install
[sudo] password for boundary:
Reading package lists... Done
Building dependency tree
Reading state information... Done
adduser is already the newest version.
apparmor is already the newest version.
...
</pre>
<h3>Step 3: Install LTIB</h3>
<p>In this distribution, we started with the package <a href="http://www.freescale.com/webapp/sps/site/homepage.jsp?code=IMXSW_HOME" title="L2.6.35_11.09.01_ER_source_bundle.tar.gz">L2.6.35_11.09.01_ER_source_bundle.tar.gz</a> from Freescale, and the <code>L2.6.35_11.09.01_ER_source.tar.gz</code> tar-ball within it.</p>
<p>After extracting, we installed LTIB in the normal way into <code>/home/boundary/ltib</code>:</p>
<pre class="brush: shell">
boundary@ltib-qt-4:~$ sudo mkdir -p /opt/freescale/
boundary@ltib-qt-4:~$ sudo chown boundary.boundary /opt/freescale
boundary@ltib-qt-4:~$ cd L2.6.35_11.09.01_ER_source/
boundary@ltib-qt-4:~/L2.6.35_11.09.01_ER_source$ ls
EULA     ltib.tar.gz           pkgs                tftp.zip
install  package_manifest.txt  redboot_201003.zip
boundary@ltib-qt-4:~/L2.6.35_11.09.01_ER_source$ ./install

You are about to install the LTIB (GNU/Linux Target Image Builder)

Before installing LTIB, you must read and accept the EULA
(End User License Agreement) which will be presented next.

Do you want to continue ? Y|n
Y

              FREESCALE SEMICONDUCTOR SOFTWARE LICENSE AGREEMENT

IMPORTANT. Read the following Freescale Semiconductor Software License Agreement
("Agreement") completely. By selecting the "I Accept" button at the end of this
page, you indicate that you accept the terms of this Agreement. You may then
download the file.

...

I have read and accept the EULA (yes|no):
yes

The LTIB files are extracted from a tar file which includes the
prefix ltib.  After installation you will find LTIB in:
/home/boundary/L2.6.35_11.09.01_ER_source/ltib

Where do you want to install LTIB ? (/home/boundary/L2.6.35_11.09.01_ER_source)
/home/boundary
Making target directory /home/boundary/ltib
Installing LTIB to /home/boundary/ltib
ltib/
ltib/hash
ltib/doc/
...
</pre>
<p>Following the LTIB directions, we also added this line to <code>/etc/sudoers</code>:</p>
<pre class="brush: shell">
boundary ALL= NOPASSWD: /usr/bin/rpm, /opt/freescale/ltib/usr/bin/rpm
</pre>
<p>And edited <code>ltib</code> as <a href="ltib-quick-start#no_sudo">described here</a>.</p>
<pre class="brush: shell">    no_sudo_check => 0,  # fc9 work-around</pre>
<h3>Step 4: Run LTIB</h3>
<p>In the earlier posts on bringing up LTIB, I described running <code>ltib -m config</code> to configure the toolchain and packages for LTIB. In other words, I described an interactive process.</p>
<p>For production use, it&#8217;s better to simply save and restore the configuration file produced by the U/I.</p>
<p>The LTIB configuration used in this VirtualBox build is available <a href="http://boundarydevices.com/ltib_11.03.config" title="LTIB configuration" target="_blank">here</a>. In order to restore it, use the <code>ltib --selecttype</code> option as shown below. <br/></p>
<pre class="brush: shell">
boundary@ubuntu:~$ wget http://boundarydevices.com/ltib_11.03.config
--2011-12-10 19:44:54--  http://boundarydevices.com/ltib_11.03.config
Resolving boundarydevices.com... 69.89.31.84
Connecting to boundarydevices.com|69.89.31.84|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1675 (1.6K) 
Saving to: `ltib_11.03.config.3'

100%[======================================>] 1,675       --.-K/s   in 0s

2011-12-10 19:44:54 (173 GB/s) - `ltib_11.03.config.3' saved [1675/1675]

boundary@ubuntu:~$ cd ltib &#038;&#038; ./ltib --selectype
</pre>
<p><img src="/ltib_load_11.03.png" alt="LTIB configuration screenshot"></p>
<p>After running <code>selectype</code>, LTIB will run for a while. Go get a cup of coffee and check in with it periodically. <br/></p>
<p>Note that SSH is up and running now, and if you configure the VM with a bridged network adapter, you can run <code>ifconfig eth0</code> in the virtual machine to find its&#8217; IP address and use SSH to access it. Because you can run this in a console window on your development machine, <i>Cut and Paste</i> and screen scraping becomes much easier. Once things are configured and I know the IP address of the VM, I typically stash the VM console window on an unused Workspace on my Ubuntu development machine.</p>
<p>If everything proceeds as planned, your LTIB build will complete with this happy banner:</p>
<pre class="brush: shell">
Started: Sun Dec 11 11:42:07 2011
Ended:   Sun Dec 11 11:42:21 2011
Elapsed: 14 seconds

Build Succeeded
</pre>
<p>The output of the build is placed in <code>/home/boundary/ltib/rootfs</code>. The size is fairly<br />
large, but can be trimmed down later (a topic that deserves more than one blog post of its own).</p>
<pre class="brush: shell">
boundary@ltib-qt:~$ du -hs /home/boundary/ltib/rootfs
663M	/home/boundary/ltib/rootfs
</pre>
<h3>Step 5: Set up symlink for Qt</h3>
<p>If you&#8217;ve looked at the other post on building Qt, or if you&#8217;ve taken a look at the <code>qmake.conf</code> file, you should notice that the include and linker specs are specified using the path <code>/tftpboot/ltib</code>. This is done primarily to avoid having references to home directories and to allow multiple instances of LTIB to be placed on a development machine.</p>
<p>To set this up, you should simply create a <code>/tftpboot</code> directory and a symlink from <code>/tftpboot/ltib</code> to the LTIB root filesystem. In this post, that directory is <code>/home/boundary/ltib/rootfs</code>.</p>
<pre class="brush: shell">
boundary@ltib-qt:~$ sudo mkdir /tftpboot
boundary@ltib-qt:~$ sudo ln -sf /home/boundary/ltib/rootfs /tftpboot/ltib
</pre>
<h3>Step 6: Configure Qt</h3>
<p>Now that LTIB is installed, you can move on to configuring Qt.</p>
<p>To make things easy and reproducible, we placed some configuration files in <a href="http://boundarydevices.com/qt-config-mx5x.zip" title="Qt configuration for MX5x">this zip file</a>. It consists of a configuration shell script (<code>do_config_qt-4.7.1</code>) and the LTIB configuration directory <code>mkspecs/linux-mxc-g++/</code>.</p>
<p>You can grab it and the Qt sources themselves using <code>wget</code>.</p>
<pre class="brush: shell">
boundary@ltib-qt:~$ wget http://get.qt.nokia.com/qt/source/qt-everywhere-opensource-src-4.7.1.tar.gz
--2011-12-11 11:51:59--  http://get.qt.nokia.com/qt/source/qt-everywhere-opensource-src-4.7.1.tar.gz
Resolving get.qt.nokia.com... 208.111.145.207, 69.28.182.56
Connecting to get.qt.nokia.com|208.111.145.207|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 211768512 (202M) [application/octet-stream]
Saving to: `qt-everywhere-opensource-src-4.7.1.tar.gz'

 2% [                                       ] 4,239,502    416K/s  eta 8m 45s

boundary@ltib-qt:~$ tar zxvf qt-everywhere-opensource-src-4.7.1.tar.gz
qt-everywhere-opensource-src-4.7.1/
qt-everywhere-opensource-src-4.7.1/qmake/
qt-everywhere-opensource-src-4.7.1/qmake/property.cpp
qt-everywhere-opensource-src-4.7.1/qmake/meta.h
qt-everywhere-opensource-src-4.7.1/qmake/Makefile.win32-g++
...
boundary@ltib-qt:~$ cd qt-everywhere-opensource-src-4.7.1
boundary@ltib-qt:~/qt-everywhere-opensource-src-4.7.1$ wget http://boundarydevices.com/qt-config-mx5x.zip
--2011-12-11 12:01:46--  http://boundarydevices.com/qt-config-mx5x.zip
Resolving boundarydevices.com... 69.89.31.84
Connecting to boundarydevices.com|69.89.31.84|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3324 (3.2K) [application/zip]
Saving to: `qt-config-mx5x.zip'

100%[======================================>] 3,324       --.-K/s   in 0.001s

2011-12-11 12:01:47 (3.45 MB/s) - `qt-config-mx5x.zip' saved [3324/3324]

boundary@ltib-qt:~/qt-everywhere-opensource-src-4.7.1$ unzip qt-config-mx5x.zip
Archive:  qt-config-mx5x.zip
  inflating: do_config_qt-4.7.1
   creating: mkspecs/linux-mxc-g++/
  inflating: mkspecs/linux-mxc-g++/qplatformdefs.h
  inflating: mkspecs/linux-mxc-g++/qmake.conf
</pre>
<p>If you take a look at the file <a href="/do_config_qt-4.7.1" title="Qt configuration script">do_config_qt-4.7.1</a>, you&#8217;ll see that it sets a few environment variables, so you&#8217;ll want to run it with the <code>source</code> or <code>dot</code> operator:</p>
<pre class="brush: shell">
boundary@ltib-qt:~/qt-everywhere-opensource-src-4.7.1$ . ./do_config_qt-4.7.1
Determining system architecture... (Linux:2.6.32-36-generic:i686)
    32-bit Intel 80x86 (i386)
    'arm' is supported
    'i386' is supported
System architecture: 'arm'
Host architecture: 'i386'

You have asked to use pkg-config and are cross-compiling.
Please make sure you have a correctly set-up pkg-config
environment!

... lots of spew here...

Qt is now configured for building. Just run 'make'.
Once everything is built, you must run 'make install'.
Qt will be installed into /usr/local/Trolltech/Qt-4.7.1/
</pre>
<h3>Step 7: Build and install Qt</h3>
<p>Compared with the steps above, this step is really easy:</p>
<pre class="brush: shell">
boundary@ltib-qt:~/qt-everywhere-opensource-src-4.7.1$ make
cd src/tools/bootstrap/ &#038;&#038; make -f Makefile
make[1]: Entering directory `/home/boundary/qt-everywhere-opensource-src-4.7.1/src/tools/bootstrap'
...
make[3]: Leaving directory `/home/boundary/qt-everywhere-opensource-src-4.7.1/demos/spectrum/app'
make[2]: Leaving directory `/home/boundary/qt-everywhere-opensource-src-4.7.1/demos/spectrum'
make[1]: Leaving directory `/home/boundary/qt-everywhere-opensource-src-4.7.1/demos'
boundary@ltib-qt:~/qt-everywhere-opensource-src-4.7.1$ sudo make INSTALL_ROOT=~/ltib/rootfs/ install
[sudo] password for boundary:
cd src/tools/bootstrap/ &#038;&#038; make -f Makefile install
make[1]: Entering directory `/home/boundary/qt-everywhere-opensource-src-4.7.1/src/tools/bootstrap'
...
cp -f -r /home/boundary/qt-everywhere-opensource-src-4.7.1/mkspecs/wincewm60standard-msvc2008 /home/boundary/ltib/rootfs//usr/local/Trolltech/Qt-4.7.1//mkspecs/
cp -f -r /home/boundary/qt-everywhere-opensource-src-4.7.1/mkspecs/wincewm65professional-msvc2005 /home/boundary/ltib/rootfs//usr/local/Trolltech/Qt-4.7.1//mkspecs/
cp -f -r /home/boundary/qt-everywhere-opensource-src-4.7.1/mkspecs/wincewm65professional-msvc2008 /home/boundary/ltib/rootfs//usr/local/Trolltech/Qt-4.7.1//mkspecs/
boundary@ltib-qt:~/qt-everywhere-opensource-src-4.7.1$
</pre>
<p>As shown above, this configuration installed Qt libraries into <code>/home/boundary/ltib/rootfs</code>, and<br />
specifically, into the <code>usr/local/</code> tree within it.</p>
<p>If you&#8217;ve followed this post to this point, congratulations! You now have a usable build of<br />
Linux and Qt. I&#8217;ll follow up in future posts with how you can create a production-ready distribution<br />
based on this core.</p>
<h3>Wrapping it up</h3>
<p>This VirtualBox image is available <a href="http://commondatastorage.googleapis.com/boundarydevices.com/ltib_qt_4.7.1.vdi.gz" title="LTIB and Qt VirtualBox image">here</a>.</p>
<p>You can use it in a number of different ways:</p>
<ol>
<li>You can install the <code>nfs-kernel-server</code> package, export it and boot a Nitrogen53 directly to the VM image.</li>
<li>You can grab the <code>/opt/freescale</code> and <code>/home/boundary/ltib</code> directories and use them to compile and link your applications.</li>
<li>You can use it as a quick-start to cross-compilation without going through all of these steps yourself if you&#8217;re evaluating a Nitrogen or Nitrogen53 board.</li>
<li>You can use it for comparison with your own build.</li>
</ol>
<p>The last of these is really the intent of this post.</p>
]]></content:encoded>
			<wfw:commentRss>http://boundarydevices.com/revisiting-ltib-and-qt-on-i-mx51-and-i-mx53/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Booting into your own application on Android</title>
		<link>http://boundarydevices.com/booting-into-your-own-application-on-android/</link>
		<comments>http://boundarydevices.com/booting-into-your-own-application-on-android/#comments</comments>
		<pubDate>Fri, 03 Jun 2011 20:54:45 +0000</pubDate>
		<dc:creator>ryan</dc:creator>
				<category><![CDATA[Miscellaneous]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[i.MX51]]></category>
		<category><![CDATA[i.MX53]]></category>
		<category><![CDATA[linux]]></category>

		<guid isPermaLink="false">http://boundarydevices.com/blogs/?p=833</guid>
		<description><![CDATA[Here&#8217;s another favorite for fixed-point Android users.  How do we skip the home screen and go directly into our own custom application on system start?  Once again, this is an easy change to make, and we really don&#8217;t even have to mess with the framework code to make it work. If you have any Android [...]]]></description>
			<content:encoded><![CDATA[Here&#8217;s another favorite for fixed-point Android users.  How do we skip the home screen and go directly into our own custom application on system start?  Once again, this is an easy change to make, and we really don&#8217;t even have to mess with the framework code to make it work.

If you have any Android development experience, you already know how <a href="http://developer.android.com/guide/topics/intents/intents-filters.html#ifs">intents and intent filters</a> work.  Intents are Android&#8217;s way of calling up new activities, whether it&#8217;s a new screen within your own app or some other app that does something we need &#8212; say, send a text message, even though Joe User might have one of many default SMS apps installed on his device.  The point is, you don&#8217;t need to know the name of the app you want to start as long as you know what, generally, you want it to do.

Intent filters are Android&#8217;s way of resolving the slightly vague &#8220;I want to do this, and I don&#8217;t care what app does it&#8221; intents.  Say you wrote a special new browser app &#8212; you could specify a <code>BROWSABLE </code>category filter in the AndroidManifest.xml file.  That way, when your user opens a URL, they can be given the option to use your browser app instead of the default.

Let&#8217;s get to the point.  The &#8216;home screen&#8217; you&#8217;re used to seeing on your Android phone is an app just like any of the others you&#8217;ve installed.  When the system server is done initializing everything and knows it&#8217;s ready to start, it sends out an intent that tells Android to start the home activity.  For our Gingerbread build, the default one is Launcher2. Look in the <a href="http://android.git.kernel.org/?p=platform/packages/apps/Launcher2.git;a=blob;f=AndroidManifest.xml;h=84ee59909541068198ea3137bf5fc286dd36562f;hb=refs/heads/gingerbread-release">manifest file</a> and you&#8217;ll see this block of code (or something that looks remarkably similar):
<pre class="brush: xml">&lt;intent-filter&gt;
   &lt;action android:name="android.intent.action.MAIN" /&gt;
   &lt;category android:name="android.intent.category.HOME" /&gt;
   &lt;category android:name="android.intent.category.DEFAULT" /&gt;
   &lt;category android:name="android.intent.category.MONKEY"/&gt;
&lt;/intent-filter&gt;</pre>
This is the intent filter for Launcher2&#8242;s main activity. The key here is the category filter <code>android.intent.category.HOME</code>.  Android looks for application manifests that have this filter registered when it starts up &#8212; this tells it that this activity is a home activity that it can boot into.  So, copy that line into the intent filter for your own app&#8217;s main activity in the manifest file.  Reinstall your app.

If you did it correctly, on your next startup you should see the dialog box that lets you choose between Launcher2 and the application you just modified.  Success! At this point, the dialog should give you the option to set this app as the default home application &#8212; or, if you prefer, you can simply remove Launcher2 from your build altogether by removing Launcher2.apk from your device&#8217;s <code>system/apps/</code> folder.]]></content:encoded>
			<wfw:commentRss>http://boundarydevices.com/booting-into-your-own-application-on-android/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>i2c-tools under Android</title>
		<link>http://boundarydevices.com/i2c-tools-under-android/</link>
		<comments>http://boundarydevices.com/i2c-tools-under-android/#comments</comments>
		<pubDate>Fri, 27 May 2011 19:22:47 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Miscellaneous]]></category>

		<guid isPermaLink="false">http://boundarydevices.com/blogs/?p=829</guid>
		<description><![CDATA[While working with a multi-touch display under Android recently, I had some questions about what data was being received from the touch screen, which is connected to our Nitrogen53 board over i2c. My early debug steps involved instrumenting the kernel, but I really wanted the quicker cycle time of the i2c-tools package. It turns out [...]]]></description>
			<content:encoded><![CDATA[<p>While working with a multi-touch display under Android recently, I had some questions about what data was being received from the touch screen, which is connected to our <a href="/products-2/nitrogen53-board-imx53-arm-cortex-a8-sbc/">Nitrogen53 board</a> over i2c.</p>
<p>My early debug steps involved instrumenting the kernel, but I really wanted the quicker cycle time of the <code>i2c-tools</code> package. It turns out that this was trivially easy to do. The <code>i2c-tools</code> package is very clean and the Android build system makes generating a makefile simple.</p>
<p>I simply copied my source tree into <code>hardware/i2c-tools-3.0.2</code>, added the following Android.mk, and it worked! The following is the content of Android.mk that I came up with by a quick scan of the Makefile.</p>
<pre class="brush: shell">
LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/$(KERNEL_DIR)/include
LOCAL_SRC_FILES := tools/i2cbusses.c tools/util.c
LOCAL_MODULE := i2c-tools
include $(BUILD_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES:=tools/i2cdetect.c
LOCAL_MODULE:=i2cdetect
LOCAL_CPPFLAGS += -DANDROID
LOCAL_SHARED_LIBRARIES:=libc
LOCAL_STATIC_LIBRARIES := i2c-tools
LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/$(KERNEL_DIR)/include
include $(BUILD_EXECUTABLE)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES:=tools/i2cget.c
LOCAL_MODULE:=i2cget
LOCAL_CPPFLAGS += -DANDROID
LOCAL_SHARED_LIBRARIES:=libc
LOCAL_STATIC_LIBRARIES := i2c-tools
LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/$(KERNEL_DIR)/include
include $(BUILD_EXECUTABLE)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES:=tools/i2cset.c
LOCAL_MODULE:=i2cset
LOCAL_CPPFLAGS += -DANDROID
LOCAL_SHARED_LIBRARIES:=libc
LOCAL_STATIC_LIBRARIES := i2c-tools
LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/$(KERNEL_DIR)/include
include $(BUILD_EXECUTABLE)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES:=tools/i2cdump.c
LOCAL_MODULE:=i2cdump
LOCAL_CPPFLAGS += -DANDROID
LOCAL_SHARED_LIBRARIES:=libc
LOCAL_STATIC_LIBRARIES := i2c-tools
LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/$(KERNEL_DIR)/include
include $(BUILD_EXECUTABLE)
</pre>
]]></content:encoded>
			<wfw:commentRss>http://boundarydevices.com/i2c-tools-under-android/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Disabling sleep mode on Android</title>
		<link>http://boundarydevices.com/disabling-sleep-mode-on-android/</link>
		<comments>http://boundarydevices.com/disabling-sleep-mode-on-android/#comments</comments>
		<pubDate>Thu, 05 May 2011 18:34:26 +0000</pubDate>
		<dc:creator>ryan</dc:creator>
				<category><![CDATA[Miscellaneous]]></category>
		<category><![CDATA[Android]]></category>

		<guid isPermaLink="false">http://boundarydevices.com/blogs/?p=793</guid>
		<description><![CDATA[Android&#8217;s user-focused, touchscreen-driven interface lends itself well to fixed-point applications such as kiosks and control panels.  However, since it was originally designed for mobile devices, there are a few features that need to be tweaked. For example, Android puts itself to sleep after a certain amount of inactivity. You may not want your device to [...]]]></description>
			<content:encoded><![CDATA[Android&#8217;s user-focused, touchscreen-driven interface lends itself well to fixed-point applications such as kiosks and control panels.  However, since it was originally designed for mobile devices, there are a few features that need to be tweaked. For example, Android puts itself to sleep after a certain amount of inactivity. You may not want your device to sleep at all &#8212; in fact, if you haven&#8217;t hooked up Android buttons somewhere on your device, you might not be able to wake it up when it does so.

Fortunately, this is a quick fix. Let&#8217;s talk about power management a little first, though.

Apps that need to keep the screen on, such as a video player, can acquire a wake lock to instruct the system to temporarily change its power management policy.  Read up on the <a href="http://developer.android.com/reference/android/os/PowerManager.html">PowerManager</a> class for details on how to do this in your own application.  So long as a wake lock is held by an application, the system has to stay awake to the degree specified by the wake lock.

So, one easy way to keep the system awake would be to insert a wake lock into the home application (in our case, Launcher2) that gets held throughout its lifetime.  Here&#8217;s a patch Eric wrote for Launcher2 that demonstrates this (the root directory, btw, is my-android/packages/apps/Launcher2):
<pre class="brush: diff">diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 606f6f8..84ee599 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -50,7 +50,6 @@
         android:label="@string/permlab_write_settings"
         android:description="@string/permdesc_write_settings"/&gt;

-

diff --git a/src/com/android/launcher2/LauncherApplication.java b/src/com/androi
index 85a9b9d..eda92d9 100644
--- a/src/com/android/launcher2/LauncherApplication.java
+++ b/src/com/android/launcher2/LauncherApplication.java
@@ -23,16 +23,10 @@ import android.content.IntentFilter;
 import android.database.ContentObserver;
 import android.os.Handler;
 import dalvik.system.VMRuntime;
-import android.os.PowerManager;
-import android.app.Activity;
-import android.content.Context;
-import android.content.Context;
-import android.content.Context;
--- a/AndroidManifest.xml
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 606f6f8..84ee599 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -50,7 +50,6 @@
         android:label="@string/permlab_write_settings"
         android:description="@string/permdesc_write_settings"/&gt;

-

diff --git a/src/com/android/launcher2/LauncherApplication.java b/src/com/android/launcher2/LauncherApplication.java
index 85a9b9d..eda92d9 100644
--- a/src/com/android/launcher2/LauncherApplication.java
+++ b/src/com/android/launcher2/LauncherApplication.java
@@ -23,16 +23,10 @@ import android.content.IntentFilter;
 import android.database.ContentObserver;
 import android.os.Handler;
 import dalvik.system.VMRuntime;
-import android.os.PowerManager;
-import android.app.Activity;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.PowerManager;

 public class LauncherApplication extends Application {
     public LauncherModel mModel;
     public IconCache mIconCache;
-    protected PowerManager.WakeLock mWakeLock;

     @Override
     public void onCreate() {
@@ -58,10 +52,6 @@ public class LauncherApplication extends Application {
         ContentResolver resolver = getContentResolver();
         resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
                 mFavoritesObserver);
-
-        final PowerManager pm = (PowerManager) getSystemService(Context.POWER_S
-        this.mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
-        this.mWakeLock.acquire();
     }

     /**</pre>
A slightly hackier way to do this would be to go into the framework and &#8216;cut the cord&#8217;, so to speak, that forces the system to sleep.

Wake locks and other power management-related activities are managed by the <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=services/java/com/android/server/PowerManagerService.java;h=a6daaefe710296b3c50a5a0fe6fcba302454f75d;hb=refs/heads/gingerbread-release">PowerManagerService</a>.  This class sits directly on top of the native hardware management code and converts requests by the upper layers into actual power state change commands. If you run Android with <code>PowerManagerService.mSpew</code> enabled, you&#8217;ll see a heap of messages from PowerManagerService while you&#8217;re interacting with the device that clues you in to how much work the service actually performs.

The first place I looked for a solution was in <code>setPowerState()</code>, which is matched by a native method in the hardware abstraction layer. There is a state variable in there that holds a bitmask of various power state flags, and it gets updated every time <code>setPowerState()</code> gets called.  It&#8217;d be easy enough to toss in something like
<pre class="brush: java">if ((mPowerState &amp; (SCREEN_ON_BIT | SCREEN_BRIGHT)) != 0) {
    Slog.d(TAG, "No sleep till Brooklyn!");
    return;
}</pre>
&#8230; at the beginning of the method, and that would keep the entire method from ever turning off the display.  This falls squarely in the category of &#8216;bandaid&#8217;, though, which you&#8217;ll see if you keep your eye on the system log.

<code>setPowerState()</code> gets called on a timer, <code>setTimeoutLocked()</code>, which is set when the system detects a <code>userActivity()</code>, in other words, when you touch the screen.  If we modify <code>setPowerState()</code>, the timer will still get set with every touch event, but nothing will happen when it expires.  So, the cleaner way to do this is to stop the timer from getting set in the first place.
<pre class="brush: java">    private void userActivity(long time, long timeoutOverride, boolean noChangeLights,
            int eventType, boolean force) {

        ...

        synchronized (mLocks) { 

            ...

            if (mLastEventTime &lt;= time || force) {
                mLastEventTime = time;
                if ((mUserActivityAllowed &amp;&amp; !mProximitySensorActive) || force) {
                    // Only turn on button backlights if a button was pressed
                    // and auto brightness is disabled
                    if (eventType == BUTTON_EVENT &amp;&amp; !mUseSoftwareAutoBrightness) {
                        mUserState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT);
                    } else {
                        // don't clear button/keyboard backlights when the screen is touched.
                        mUserState |= SCREEN_BRIGHT;
                    }

                    int uid = Binder.getCallingUid();
                    long ident = Binder.clearCallingIdentity();
                    try {
                        mBatteryStats.noteUserActivity(uid, eventType);
                    } catch (RemoteException e) {
                        // Ignore
                    } finally {
                        Binder.restoreCallingIdentity(ident);
                    }
                    mWakeLockState = mLocks.reactivateScreenLocksLocked();
                    setPowerState(mUserState | mWakeLockState, noChangeLights,
                            WindowManagerPolicy.OFF_BECAUSE_OF_USER);
                    setTimeoutLocked(time, timeoutOverride, SCREEN_BRIGHT);
                }
            }
        }

        if (mPolicy != null) {
            mPolicy.userActivity();
        }
    }</pre>
Comment out the <code>setTimeoutLocked()</code> call in line 44 of the above example, recompile and run.  PowerManagerService will still pick up on touch events and pass them to the battery management subsystem, but without the timer, <code>setPowerState()</code> doesn&#8217;t get called until you tell it to.  Congratulations, you now have an insomniac Android!]]></content:encoded>
			<wfw:commentRss>http://boundarydevices.com/disabling-sleep-mode-on-android/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Set Android default orientation to portrait mode</title>
		<link>http://boundarydevices.com/set-android-default-orientation-to-portrait-mode/</link>
		<comments>http://boundarydevices.com/set-android-default-orientation-to-portrait-mode/#comments</comments>
		<pubDate>Tue, 19 Apr 2011 15:54:26 +0000</pubDate>
		<dc:creator>ryan</dc:creator>
				<category><![CDATA[Miscellaneous]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[i.MX51]]></category>

		<guid isPermaLink="false">http://boundarydevices.com/blogs/?p=770</guid>
		<description><![CDATA[The default screen orientation for the Android-ready Nitrogen and Nitrogen53 is landscape. That&#8217;s fine for most people, but say you want it in portrait. Or upside down, or whatever. That&#8217;s a problem, since Android usually takes rotation instructions from code that expects motion sensor input, and since most Nitrogen use cases don&#8217;t need the board [...]]]></description>
			<content:encoded><![CDATA[<p>The default screen orientation for the Android-ready <a href="/products-2/nitrogen-e-board-imx51-power-over-ethernet-sbc/">Nitrogen</a> and <a href="/products-2/nitrogen53-board-imx53-arm-cortex-a8-sbc/">Nitrogen53</a> is landscape.  That&#8217;s fine for most people, but say you want it in portrait.  Or upside down, or whatever.  That&#8217;s a problem, since Android usually takes rotation instructions from code that expects motion sensor input, and since most Nitrogen use cases don&#8217;t need the board to move during normal operation (sorry, retail kiosks, no &#8216;shake&#8217; function for you), there&#8217;s no sensor to read from.</p>
<p>Well, on an app-by-app basis, you could always update the <a href="http://developer.android.com/guide/topics/manifest/manifest-intro.html">manifest file</a>.  There&#8217;s a <a href="http://developer.android.com/guide/topics/manifest/activity-element.html#screen">tag</a> in there that defines the activity&#8217;s requested orientation.  This isn&#8217;t really the best way to go about this, though, especially if you&#8217;re planning on using lots of apps this way.</p>
<p>Credit goes to <a href="http://groups.google.com/group/android-porting/browse_thread/thread/fcf593ce9348dddd/f0dbfd66bff3a175">android-porting</a> group for establishing the right direction for people in our situation.  The answer is in frameworks/base/policy/src/com/internal/policy/impl/<a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=policy/src/com/android/internal/policy/impl/PhoneWindowManager.java;h=f934e6b894f774495145fa0a79b9ee9c61b276d9;hb=refs/heads/gingerbread-release">PhoneWindowManager.java</a> (whew).  Among other things, this file provides rotation settings to the rest of the system.  Check out <code>rotationForOrientationLw()</code>:</p>
<pre class="brush: java">    public int rotationForOrientationLw(int orientation, int lastRotation,
            boolean displayEnabled) {

        if (mPortraitRotation < 0) {             // Initialize the rotation angles for each orientation once.
            Display d = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
                .getDefaultDisplay();
            if (d.getWidth() > d.getHeight()) {
                mPortraitRotation = Surface.ROTATION_90;
                mLandscapeRotation = Surface.ROTATION_0;
                mUpsideDownRotation = Surface.ROTATION_270;
                mSeascapeRotation = Surface.ROTATION_180;
            } else {
                mPortraitRotation = Surface.ROTATION_0;
                mLandscapeRotation = Surface.ROTATION_90;
                mUpsideDownRotation = Surface.ROTATION_180;
                mSeascapeRotation = Surface.ROTATION_270;
            }
        }

        synchronized (mLock) {
            switch (orientation) {
                case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
                    //always return portrait if orientation set to portrait
                    return mPortraitRotation;
                case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
                    //always return landscape if orientation set to landscape
                    return mLandscapeRotation;
                case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
                    //always return portrait if orientation set to portrait
                    return mUpsideDownRotation;
                case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
                    //always return seascape if orientation set to reverse landscape
                    return mSeascapeRotation;
                case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
                    //return either landscape rotation based on the sensor
                    mOrientationListener.setAllow180Rotation(false);
                    return getCurrentLandscapeRotation(lastRotation);
                case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
                    mOrientationListener.setAllow180Rotation(true);
                    return getCurrentPortraitRotation(lastRotation);
            }

            mOrientationListener.setAllow180Rotation(
                    orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);

            // case for nosensor meaning ignore sensor and consider only lid
            // or orientation sensor disabled
            //or case.unspecified
            if (mLidOpen) {
                return mLidOpenRotation;
            } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR &amp;&amp; mCarDockRotation >= 0) {
                return mCarDockRotation;
            } else if (mDockMode == Intent.EXTRA_DOCK_STATE_DESK &amp;&amp; mDeskDockRotation >= 0) {
                return mDeskDockRotation;
            } else {
                if (useSensorForOrientationLp(orientation)) {
                    return mOrientationListener.getCurrentRotation(lastRotation);
                }
                return Surface.ROTATION_0;
            }
        }
    }</pre>
<p>The first block defines the rotation angles, and then the rest administrates them on request.  Most utility apps don&#8217;t request a particular rotation, however; instead, they leave it up to the motion sensor.  So, look in the last block, which dictates behavior when the orientation sensor is disabled.  Since we aren&#8217;t worried about dock mode, car mode, or open lid, the last line is what gets called.  That last line (line 59 in the snippet) is what ends up determining the rotation that gets dictated to most apps in our situation.  Try changing ROTATION_0 to ROTATION_270, rebuild and see what happens.</p>
<div id="attachment_791" class="wp-caption aligncenter" style="width: 310px"><a href="http://boundarydevices.com/wp-content/uploads/2011/04/2011-04-26-11.14.49.jpg"><img class="size-medium wp-image-791" title="android-portrait" src="http://boundarydevices.com/wp-content/uploads/2011/04/2011-04-26-11.14.49-300x225.jpg" alt="Portrait mode!" width="300" height="225" /></a><p class="wp-caption-text">Cool! Portrait mode!</p></div>
<p>EDIT: One thing I found out while working on an upcoming post&#8230; The screen orientation defined in Launcher2&#8242;s manifest file is &#8220;nosensor&#8221;, which is why the process I described above works.  This sets the behavior for activities opened on top of Launcher &#8212; most of the default apps don&#8217;t even have a defined orientation.  If you are set up to open an app other than Launcher on boot, you will need to define the screenOrientation in their manifest, otherwise you&#8217;ll get the default landscape orientation. If you made the above changes, &#8220;nosensor&#8221; should work fine.</p>
]]></content:encoded>
			<wfw:commentRss>http://boundarydevices.com/set-android-default-orientation-to-portrait-mode/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to write an Android camera app</title>
		<link>http://boundarydevices.com/how-to-write-an-android-camera-app/</link>
		<comments>http://boundarydevices.com/how-to-write-an-android-camera-app/#comments</comments>
		<pubDate>Fri, 15 Apr 2011 18:18:41 +0000</pubDate>
		<dc:creator>ryan</dc:creator>
				<category><![CDATA[Miscellaneous]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[camera]]></category>

		<guid isPermaLink="false">http://boundarydevices.com/blogs/?p=723</guid>
		<description><![CDATA[Now that we&#8217;ve covered the internals, we can talk a little about how the surface application should look. As a reference we&#8217;ll use the AOSP&#8217;s own default Camera app; you can find the source here. It&#8217;s a lot of code, so we&#8217;ll do a lot of skipping through looking for the important stuff that you [...]]]></description>
			<content:encoded><![CDATA[<p><!-- 		@page { margin: 0.79in } 		P { margin-bottom: 0.08in } -->Now that we&#8217;ve <a title="Camera subsystem overview for i.MX Gingerbread" href="/camera-subsystem-overview-for-i-mx-gingerbread">covered the internals</a>, we can talk a little about how the surface application should look.  As a reference we&#8217;ll use the AOSP&#8217;s own default Camera app; you can find the source <a href="http://android.git.kernel.org/?p=platform/packages/apps/Camera.git;a=tree;hb=refs/heads/gingerbread-release">here</a>.  It&#8217;s a lot of code, so we&#8217;ll do a lot of skipping through looking for the important stuff that you can use in your own code.</p>
<p>Before we start, you&#8217;ll want to look at the javadoc for the <a href="http://developer.android.com/reference/android/hardware/Camera.html">Camera</a> and <a href="http://developer.android.com/reference/android/media/MediaRecorder.html">MediaRecorder</a> classes.  You&#8217;ll see step-by-step instructions for using each in your application.  What follows provides context, so you can see how they are used, but ultimately, your app might look different.  So, check out the above links first and you&#8217;ll know what to look for in the code snippets below.</p>
<p>First, a few notes about structure. The main activities are <a href="http://android.git.kernel.org/?p=platform/packages/apps/Camera.git;a=blob;f=src/com/android/camera/Camera.java;h=8b4de05be137c737bd63bb62748ae7c22c20212d;hb=refs/heads/gingerbread-release">Camera</a> and <a href="http://android.git.kernel.org/?p=platform/packages/apps/Camera.git;a=blob;f=src/com/android/camera/VideoCamera.java;h=ee05f9e22327906bb7c5fe2a8deecb05181e8c18;hb=refs/heads/gingerbread-release">VideoCamera</a>. The actual camera device is wrapped up in a <a href="http://android.git.kernel.org/?p=platform/packages/apps/Camera.git;a=blob;f=src/com/android/camera/CameraHolder.java;h=a1c1fb80d2fcdea825e247f35d121f3861831a1e;hb=refs/heads/gingerbread-release">CameraHolder</a> class which is designed to alleviate the performance impact from having to juggle repeated calls to <code>Camera.open()</code> and <code>Camera.release()</code> as the user switches between the video and still camera activities.  There is also a ImageCapture class (a subclass of Camera) which abstracts the entire process of taking a still picture and saving it locally.  So an ImageCapture object represents the picture you are taking or have just finished taking.</p>
<p>By the way, the methods presented in the code snippets below don&#8217;t necessarily appear in that order in the source code.  Here, they are arranged for clarity, with methods that call preceding the methods they are calling.</p>
<h3>Initialization</h3>
<pre class="brush: java">    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        setContentView(R.layout.camera);
        mSurfaceView = (SurfaceView) findViewById(R.id.camera_preview);
        ...</pre>
<p>In the <code>onCreate()</code> method, which is called when the activity starts, you can see that it immediately goes and finds the SurfaceView it will be posting previews to.  The activity itself implements SurfaceHolder.Callback, meaning that the SurfaceHolder held by that View gets handed back to the app via the <code>surfaceChanged()</code> callback.  The important part is that there&#8217;s a reference to it for later.</p>
<p>Then it gets some settings from the framework, and spins off a preview thread.  Inside the preview thread, the <code>startPreview()</code> method is called.</p>
<pre class="brush: java">    private void startPreview() throws CameraHardwareException {
        if (mPausing || isFinishing()) return;

        ensureCameraDevice();

        // If we're previewing already, stop the preview first (this will blank
        // the screen).
        if (mPreviewing) stopPreview();

        setPreviewDisplay(mSurfaceHolder);
        Util.setCameraDisplayOrientation(this, mCameraId, mCameraDevice);
        setCameraParameters(UPDATE_PARAM_ALL);

        mCameraDevice.setErrorCallback(mErrorCallback);

        try {
            Log.v(TAG, "startPreview");
            mCameraDevice.startPreview();
        } catch (Throwable ex) {
            closeCamera();
            throw new RuntimeException("startPreview failed", ex);
        }
        mPreviewing = true;
        mZoomState = ZOOM_STOPPED;
        mStatus = IDLE;
    }

    private void ensureCameraDevice() throws CameraHardwareException {
        if (mCameraDevice == null) {
            mCameraDevice = CameraHolder.instance().open(mCameraId);
            mInitialParams = mCameraDevice.getParameters();
        }
    }

    private void setPreviewDisplay(SurfaceHolder holder) {
        try {
            mCameraDevice.setPreviewDisplay(holder);
        } catch (Throwable ex) {
            closeCamera();
            throw new RuntimeException("setPreviewDisplay failed", ex);
        }
    }</pre>
<p>CameraHolder&#8217;s <code>open()</code> method returns an instance of android.hardware.Camera, which is saved locally.  It sets the Camera&#8217;s preview display.  Finally, it calls the device&#8217;s <code>startPreview()</code> method (and by the way, the Camera can only take pictures once <code>startPreview()</code> returns successfully).  At this point, the camera preview picture should show up on the app&#8217;s UI.  Easy!</p>
<h3>Taking a picture</h3>
<p>The code to actually take a picture is in the <code>doSnap()</code> method, called from callbacks associated with the shutter button and the UI capture button.  It calls the <code>onSnap()</code> method of a previously instantiated ImageCapture object, which in turn calls its initiate() method, which then calls its <code>capture()</code> method.  The <code>capture()</code> method sets some parameters such as orientation and GPS information (for geotagging), then finally calls the <code>takePicture()</code> method of the Camera object.</p>
<pre class="brush: java">    private class ImageCapture {
        ...
        public void onSnap() {
            // If we are already in the middle of taking a snapshot then ignore.
            if (mPausing || mStatus == SNAPSHOT_IN_PROGRESS) {
                return;
            }
            mCaptureStartTime = System.currentTimeMillis();
            mPostViewPictureCallbackTime = 0;
            mHeadUpDisplay.setEnabled(false);
            mStatus = SNAPSHOT_IN_PROGRESS;

            mImageCapture.initiate();
        }

        public void initiate() {
            if (mCameraDevice == null) {
                return;
            }

            capture();
        }

        private void capture() {
            mCaptureOnlyData = null;

            // See android.hardware.Camera.Parameters.setRotation for
            // documentation.
            int rotation = 0;
            if (mOrientation != OrientationEventListener.ORIENTATION_UNKNOWN) {
                CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
                if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
                    rotation = (info.orientation - mOrientation + 360) % 360;
                } else {  // back-facing camera
                    rotation = (info.orientation + mOrientation) % 360;
                }
            }
            mParameters.setRotation(rotation);

            // Clear previous GPS location from the parameters.
            mParameters.removeGpsData();

            // We always encode GpsTimeStamp
            mParameters.setGpsTimestamp(System.currentTimeMillis() / 1000);

            // Set GPS location.
            Location loc = mRecordLocation ? getCurrentLocation() : null;
            if (loc != null) {
                double lat = loc.getLatitude();
                double lon = loc.getLongitude();
                boolean hasLatLon = (lat != 0.0d) || (lon != 0.0d);

                if (hasLatLon) {
                    mParameters.setGpsLatitude(lat);
                    mParameters.setGpsLongitude(lon);
                    mParameters.setGpsProcessingMethod(loc.getProvider().toUpperCase());
                    if (loc.hasAltitude()) {
                        mParameters.setGpsAltitude(loc.getAltitude());
                    } else {
                        // for NETWORK_PROVIDER location provider, we may have
                        // no altitude information, but the driver needs it, so
                        // we fake one.
                        mParameters.setGpsAltitude(0);
                    }
                    if (loc.getTime() != 0) {
                        // Location.getTime() is UTC in milliseconds.
                        // gps-timestamp is UTC in seconds.
                        long utcTimeSeconds = loc.getTime() / 1000;
                        mParameters.setGpsTimestamp(utcTimeSeconds);
                    }
                } else {
                    loc = null;
                }
            }

            mCameraDevice.setParameters(mParameters);

            mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback,
                    mPostViewPictureCallback, new JpegPictureCallback(loc));
            mPreviewing = false;
        }</pre>
<p>Notice that it passes four objects as parameters, each of which wraps a callback method as defined in the Camera class: ShutterCallback, which is called immediately after the image capture is complete; RawPictureCallback, which is called as soon as uncompressed data is available; PostViewPictureCallback, as soon as a scaled postview image (i.e., the image you see after taking the picture, which you can choose to discard or save) is available, and JpegPictureCallback, when compressed JPEG image data is available.  In this case, the raw and postview callbacks only contain logging data, and the Shutter callback just tells the app that it doesn&#8217;t need to auto-focus anymore.  The JpegPictureCallback, however, is important.</p>
<pre class="brush: java">    private final class JpegPictureCallback implements PictureCallback {
        Location mLocation;

        public JpegPictureCallback(Location loc) {
            mLocation = loc;
        }

        public void onPictureTaken(
                final byte [] jpegData, final android.hardware.Camera camera) {
            if (mPausing) {
                return;
            }

            mJpegPictureCallbackTime = System.currentTimeMillis();
            // If postview callback has arrived, the captured image is displayed
            // in postview callback. If not, the captured image is displayed in
            // raw picture callback.
            if (mPostViewPictureCallbackTime != 0) {
                mShutterToPictureDisplayedTime =
                        mPostViewPictureCallbackTime - mShutterCallbackTime;
                mPictureDisplayedToJpegCallbackTime =
                        mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
            } else {
                mShutterToPictureDisplayedTime =
                        mRawPictureCallbackTime - mShutterCallbackTime;
                mPictureDisplayedToJpegCallbackTime =
                        mJpegPictureCallbackTime - mRawPictureCallbackTime;
            }
            Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
                    + mPictureDisplayedToJpegCallbackTime + "ms");
            mHeadUpDisplay.setEnabled(true);

            if (!mIsImageCaptureIntent) {
                // We want to show the taken picture for a while, so we wait
                // for at least 1.2 second before restarting the preview.
                long delay = 1200 - mPictureDisplayedToJpegCallbackTime;
                if (delay < 0) {
                    restartPreview();
                } else {
                    mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, delay);
                }
            }
            mImageCapture.storeImage(jpegData, camera, mLocation);
            ...
        }
    }</pre>
<p>The Jpeg callback instructs the app to leave the postview picture on the screen for a short time, and then calls the <code>ImageCapture.storeImage()</code> method.  This method passes all of the image data to the ImageManager class for storage, which simply writes the JPEG data straight to file.  It also adds an entry for it to Android's MediaStore provider, which keeps a list of all images and other media on the device – that way it can be viewed in the Gallery right away.</p>
<p>Auto-focus is managed by the Camera class too.  The app simply calls <code>autoFocus()</code> when it's ready to start focusing, and registers a callback for when focus is achieved.</p>
<pre class="brush: java">    private void autoFocus() {
        // Initiate autofocus only when preview is started and snapshot is not
        // in progress.
        if (canTakePicture()) {
            mHeadUpDisplay.setEnabled(false);
            Log.v(TAG, "Start autofocus.");
            mFocusStartTime = System.currentTimeMillis();
            mFocusState = FOCUSING;
            updateFocusIndicator();
            mCameraDevice.autoFocus(mAutoFocusCallback);
        }
    }</pre>
<p>It's also worth noting that there is a Camera.Parameters class (you probably noticed <code>mParameters</code> or <code>mInitialParams</code> in the code snippets) that bundles up all of the details, such as manual focus, zoom, antibanding, and basic photo effects.  The camera app keeps a Parameters instance locally for changes to be made easily, then passes them down to the Camera framework via <code>Camera.setParameters()</code>.</p>
<h3>Video capture</h3>
<p>There's a switch on the UI that lets the user pick whether they want to capture video or still images, and it simply switches between the Camera and VideoCamera activities.  So, let's look at the VideoCamera activity now.</p>
<p>The fundamental difference, obviously, is in what happens when you press the shutter button.  This calls the <code>startVideoRecording()</code> method.</p>
<pre class="brush: java">    private void startVideoRecording() {
        ...
        initializeRecorder();
        ...
        try {
            mMediaRecorder.start(); // Recording is now started
        } catch (RuntimeException e) {
            Log.e(TAG, "Could not start media recorder. ", e);
            releaseMediaRecorder();
            return;
        }
        ...
        keepScreenOn();
    }

    private void initializeRecorder() {
        ...
        mMediaRecorder = new MediaRecorder();

        // Unlock the camera object before passing it to media recorder.
        mCameraDevice.unlock();
        mMediaRecorder.setCamera(mCameraDevice);
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        mMediaRecorder.setProfile(mProfile);
        mMediaRecorder.setMaxDuration(mMaxVideoDurationInMs);

        // Set output file.
        if (mStorageStatus != STORAGE_STATUS_OK) {
            mMediaRecorder.setOutputFile("/dev/null");
        } else {
            // Try Uri in the intent first. If it doesn't exist, use our own
            // instead.
            if (mVideoFileDescriptor != null) {
                mMediaRecorder.setOutputFile(mVideoFileDescriptor.getFileDescriptor());
                try {
                    mVideoFileDescriptor.close();
                } catch (IOException e) {
                    Log.e(TAG, "Fail to close fd", e);
                }
            } else {
                createVideoPath();
                mMediaRecorder.setOutputFile(mVideoFilename);
            }
        }

        mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
        ...
        try {
            mMediaRecorder.setMaxFileSize(maxFileSize);
        } catch (RuntimeException exception) {
            // We are going to ignore failure of setMaxFileSize here, as
            // a) The composer selected may simply not support it, or
            // b) The underlying media framework may not handle 64-bit range
            // on the size restriction.
        }

        // See android.hardware.Camera.Parameters.setRotation for
        // documentation.
        int rotation = 0;
        if (mOrientation != OrientationEventListener.ORIENTATION_UNKNOWN) {
            CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
            if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
                rotation = (info.orientation - mOrientation + 360) % 360;
            } else {  // back-facing camera
                rotation = (info.orientation + mOrientation) % 360;
            }
        }
        mMediaRecorder.setOrientationHint(rotation);
        mOrientationHint = rotation;

        try {
            mMediaRecorder.prepare();
        } catch (IOException e) {
            Log.e(TAG, "prepare failed for " + mVideoFilename, e);
            releaseMediaRecorder();
            throw new RuntimeException(e);
        }

        mMediaRecorder.setOnErrorListener(this);
        mMediaRecorder.setOnInfoListener(this);
    }</pre>
<p>This first calls an <code>initializeRecorder()</code> method, which creates a MediaRecorder object,  and sets it up for camera recording.  For this to happen, it needs to know what the video and audio sources are, the details for file recording such as format and encoding, and the name of the output file.  Finally, its <code>prepare()</code> method is called, telling it to get ready to record – if this fails, the camera cannot record video.</p>
<p>Once the MediaRecorder is set up, its <code>start()</code> method is called, and recording begins.  Later on, when the activity is sent to the background or the user hits the shutter button again, the <code>stopVideoRecording()</code> method is called, which in turn calls the MediaRecorder's own <code>stop()</code> method.  It then goes and registers  the video with the content database, and finally releases the MediaRecorder object.</p>
<p>That pretty much covers the guts of your basic Camera app. There's more details, of course, but we could spend a lot of time going over every single option exposed by the Camera and MediaRecorder classes.  Armed with what we've already discussed and the source code for the Camera app, you should be able to roll your own with little effort. Good luck!</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://boundarydevices.com/how-to-write-an-android-camera-app/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Camera subsystem overview for i.MX Gingerbread</title>
		<link>http://boundarydevices.com/camera-subsystem-overview-for-i-mx-gingerbread/</link>
		<comments>http://boundarydevices.com/camera-subsystem-overview-for-i-mx-gingerbread/#comments</comments>
		<pubDate>Fri, 15 Apr 2011 18:14:21 +0000</pubDate>
		<dc:creator>ryan</dc:creator>
				<category><![CDATA[Miscellaneous]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[camera]]></category>
		<category><![CDATA[i.MX51]]></category>
		<category><![CDATA[linux]]></category>

		<guid isPermaLink="false">http://boundarydevices.com/blogs/?p=704</guid>
		<description><![CDATA[That&#8217;s kind of how I felt as a naive software intern thumbing through the vast amount of information in the Android source code. There&#8217;s a lot to take in: Android covers an enormous set of use cases, and it follows a secure and extensible model that requires function calls to dive through layer after layer [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_706" class="wp-caption aligncenter" style="width: 310px"><a href="/wp-content/uploads/2011/04/inception.jpg"><img class="size-medium wp-image-706 " title="inception" src="http://boundarydevices.com/wp-content/uploads/2011/04/inception-300x162.jpg" alt="" width="300" height="162" /></a><p class="wp-caption-text">&quot;We need to go deeper&quot;</p></div>
<p>That&#8217;s kind of how I felt as a naive software intern thumbing through the vast amount of information in the Android source code.  There&#8217;s a lot to take in: Android covers an enormous set of use cases, and it follows a secure and extensible model that requires function calls to dive through layer after layer of abstraction to reach the bottom.  Perhaps not so complicated as some heavyweight operating systems that we know of, but the user-focused nature of Android demands that the experience be seamless, so it&#8217;s important for engineers involved in porting to a new device to understand how it all fits together.</p>
<p>There&#8217;s documentation, of course, but most of Google&#8217;s material is focused on the applications-level developer.  Whether or not you&#8217;re interested in messing with the lower layers, it helps to have an understanding of what happens beneath the surface of your application.</p>
<p>In this post, we&#8217;re going to try and provide a brief overview of the Android 2.3 (aka “Gingerbread”) camera subsystem on the i.MX51, which we&#8217;re in the process of configuring for our Nitrogen boards.  If you don&#8217;t already have it, get it <a href="http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=IMX51ANDROID&amp;fpsp=1&amp;tab=Design_Tools_Tab" target="_blank">here</a>.</p>
<p>First, an architectural diagram:<br />
<embed type="image/svg+xml" width="750" height="950" src="http://boundarydevices.com/android_camera_imx51_sysarch.svg" pluginspage="http://www.adobe.com/svg/viewer/install/"></embed>﻿</p>
<p>It&#8217;s color-coded by origin, by the way.  Blue is Boundary Devices (either software we&#8217;ve written or pieces of the Nitrogen board itself), orange is Freescale, yellow is Linux, green is Google, and purple is alsa.</p>
<p>Anyway, now you have some idea of how it&#8217;s all laid out from a bird&#8217;s eye view.  Next we&#8217;ll go over the pieces.</p>
<h2>Application layer</h2>
<p>The heart of any camera app is the <a href="http://developer.android.com/reference/android/hardware/Camera.html" target="_blank">Camera</a> class.  This is an abstraction for the Camera device itself and everything that sits between it and the Android API.  It provides methods to set parameters, auto-focus, and, of course, take a picture.  When you initialize a Camera object, you&#8217;ll pass a <a href="http://developer.android.com/reference/android/view/SurfaceView.html" target="_blank">SurfaceView</a> that represents the area of the UI that will contain the camera preview image.  Then you&#8217;re ready to take pictures with the <code>takePicture()</code> method.  Pretty self-explanatory!</p>
<p>To make your app record video, you&#8217;ll need a <a href="http://developer.android.com/reference/android/media/MediaRecorder.html" target="_blank">MediaRecorder</a> object as well.  This hooks into the media framework (which we&#8217;ll discuss below) and abstracts the task of recording audio and video.  It expects a Camera object and a file descriptor for the output file, as well as a number of parameters, but again, using it is very simple.  When an application developer needs to use the Camera, he grabs a Camera object; when he needs to record something, he grabs a MediaRecorder object, and that&#8217;s that.  Works out well for an independent app developer working on his own time.</p>
<p>We&#8217;ll show some examples on how to use these classes in the next post.  What we&#8217;re really interested right now is what&#8217;s underneath. For brevity&#8217;s sake, we should mention that source files in the following sections (until we get to Freescale proprietary components) are listed are under <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=summary" target="_blank">frameworks/base/</a>, unless otherwise stated.  Also, if we link to the implementation but not the header, you can assume that the header is somewhere under <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=tree;f=include;h=0a3e1f1ebe1a2bfc4a4a5e943c173ddc9a74b9d1;hb=refs/heads/gingerbread-release" target="_blank">include</a>/.</p>
<h2>JNI layer</h2>
<p>The <a href="http://en.wikipedia.org/wiki/Java_Native_Interface" target="_blank">Java Native Interface</a> (JNI) is what allows Android classes to use the native C++ libraries from within the Dalvik virtual machine.  Look in frameworks/base/core/jni/ or frameworks/base/media/jni/ and you&#8217;ll see C++ implementation files corresponding to many of the Android-specific Java classes – for example, <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/jni/android_hardware_Camera.cpp;h=10fe58357d0d059f44de3f9bcd77ee8a4a7213ae;hb=refs/heads/gingerbread-release" target="_blank">android_hardware_camera.cpp</a>.  These contain methods for passing messages between their Java counterparts running inside a Dalvik virtual machine and a native C++ implementation of that class.</p>
<h2>Native layer glue classes</h2>
<p>Okay, here&#8217;s where things get really interesting.  Below the JNI layer, there is a complex set of processes that does the dirty work of the Android operating system as a whole.  You can actually see these processes if you open up an <code>adb</code> shell and run ps to find the list of running processes.</p>
<h3>Binder interface</h3>
<p>The way these processes communicate is through the Binder system.  The binder system was designed by Google as a custom inter-process communication system for Android.  With <code>adb</code>shell, you can see that there is a binder driver under /dev/.  This driver is sort of like a mailbox that every process hooks into, and is responsible for passing messages between service providers and service clients.</p>
<p>You will frequently see objects in the native libraries with names like ICamera, ICameraService, IMediaRecorder, and so on (look <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=libs/camera/ICamera.cpp;h=13673b53ea4cd5cefb6d20a47b01eb10901c850a;hb=refs/heads/gingerbread-release" target="_blank">here</a> for the ICamera example).  These are objects that implement the Binder interfaces and thus represent proxy objects that marshal data across process boundaries.  Each header/implementation pair usually contains an interface class, IObject as well as a BpObject class and a BnObject class.  Bp stands for binder proxy, the class that sits in the application process, and Bn stands for binder native, the class that sits in the remote process (such as the MediaServer or SurfaceFlinger) that is basically invisible to the end user.  Binder proxy objects call a <code>transact()</code> method which passes messages to the binder native object, which handles them with an <code>onTransact()</code> callback.  <code>transact()</code> never returns until <code>onTransact()</code> returns, so they are synchronous.</p>
<h3>Native proxies</h3>
<p>The Camera and MediaRecorder objects, as well as the Surface object that is held by the SurfaceHolder you may have passed to the Camera object, have native proxy objects with the same name sitting below the JNI layer; for example, the <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=libs/camera/Camera.cpp;h=743fbb21a79513d201aa6d693216a2a609e600ca;hb=refs/heads/gingerbread-release" target="_blank">Camera</a> object. One of these will be created for each Java object you instantiate on the top layer.  They implement the native methods in the Java object, but in this case it really means that they wrap a binder proxy object and call methods on it when told to by the JNI layer, which in turn become transactions across the process boundary to the binder native object.  Once the binder proxy receives a response from the other side, it passes it back up to the native proxy, which passes it back up through the JNI layer to you.</p>
<h2>MediaServer</h2>
<p>The MediaServer process is the heart of Android&#8217;s media framework.  It wraps up everything that is necessary to make media playback and recording possible, such as codecs, file authoring, and connections to hardware abstraction layers (HALs).</p>
<p>Upon startup, MediaServer launches server threads for each of its major functions, including the CameraService and the MediaPlayerService.  Within the media server, our binder native objects correspond to client objects, which connect to their corresponding services.</p>
<p>The camera service handles clients which sit on top of the hardware abstraction layer and correspond to Camera objects.  Actually, they sit on top of an interface which itself abstracts the HAL (since the HAL will be different depending on the target system&#8217;s own hardware).  Its primary purpose is to manage synchronization among clients – in other words, while several can be connected to the service, only one can actually use the camera at any given time.  The classes for both client and server are in the CameraService <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=services/camera/libcameraservice/CameraService.h;h=f09773d11a6cbf6b2230f37ba3cf3d1362343f6b;hb=refs/heads/gingerbread-release" target="_blank">header</a> and <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=services/camera/libcameraservice/CameraService.cpp;h=a64ddcfc118a5962a959ebcf419556f64eecf0bc;hb=refs/heads/gingerbread-release" target="_blank">implementation</a> under services/camera/libcameraservice/.</p>
<p>We&#8217;ll mention that MediaPlayerService actually creates clients that connect to MediaRecorders, but the MediaPlayerService doesn&#8217;t actually handle any of the recording duties.  That&#8217;s handled in the next layer within the MediaServer process.</p>
<h3>Stagefright, OpenCore, and OpenMAX</h3>
<p><a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=tree;f=media/libstagefright;h=e0c5ba24fb8a022fc8c2e46c99805033f35023ee;hb=refs/heads/gingerbread-release" target="_blank">Stagefright</a> is a new addition to the Android source – although pieces of it have been in the source since Eclair, it was only fully implemented in Gingerbread.  The job of Stagefright is to abstract (again with the abstraction!) the codec library.  There are a number of other interesting parts and pieces that are more or less outside of our scope here, but the idea is that Stagefright is where all the encoding and decoding takes place above the platform-specific level.  For some good block diagrams, detailing the Stagefright architecture, look <a href="http://freepine.blogspot.com/2010/01/overview-of-stagefrighter-player.html" target="_blank">here</a> and <a href="http://yueguc.iteye.com/blog/867129" target="_blank">here</a> (last one is in Chinese, so use Google Translate). Most of the Stagefright files are in media/libstagefright/ and include/media/stagefright/.</p>
<p>Prior to Gingerbread, the primary vehicle for encoding/decoding was PacketVideo&#8217;s OpenCORE framework.  Unlike Stagefright, it was particularly well-documented and if you need to know how it works, the information <a href="http://www.opencore.net/" target="_blank">isn&#8217;t nearly as difficult to find</a>.  Stagefright is basically Google&#8217;s in-house version of OpenCORE, created with help from PV.  It is not well-documented at all, mainly because the average app developer doesn&#8217;t really need to know how it works.  However, my understanding is that it is simpler and more extensible than OpenCORE, and with Google&#8217;s attention it will evolve quickly and efficiently.</p>
<p>By the way, it is possible to swap Stagefright or OpenCORE out for an alternate media framework, such as <a href="http://groups.google.com/group/android-porting/browse_thread/thread/b2946dea1f06fb07/118a27530d3b7d1c" target="_blank">Gstreamer</a>, which Eric has covered in <a href="http://boundarydevices.com/tag/gstreamer" target="_blank">previous blog posts</a>. That&#8217;s a serious undertaking, though, and definitely out of the scope of this post!</p>
<p>The central class in the recording subsystem is StagefrightRecorder (<a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=media/libmediaplayerservice/StagefrightRecorder.h;h=e42df2e82108cf2700cb033502fba710721da3b1;hb=refs/heads/gingerbread-release" target="_blank">header</a> and <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=media/libmediaplayerservice/StagefrightRecorder.cpp;h=3261fe6e543f2b2ddc7c41d04d14b9ed98b8b6ff;hb=refs/heads/gingerbread-release" target="_blank">implementation</a>, confusingly under media/libmediaplayerservice/).  A reference to a StagefrightRecorder object is bundled into initialized MediaRecorderClient objects.  The job of StagefrightRecorder is to take the short list of calls passed in from upper layers and dissect them.  Given an encoding format, output format, and list of parameters, it selects an encoder and a MediaWriter object, as well as MediaSources representing data sources like the camera and microphone, then manages them as the MediaRecorder object tells it to (or complains if a bad combination of codec and file format were supplied at any point in the call stack).</p>
<p><a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=include/media/stagefright/MediaWriter.h;h=5cc8dcffe0d14b2c876589ed72e5361eadcb4597;hb=refs/heads/gingerbread-release" target="_blank">MediaWriter</a> is actually an interface that we use as a simplification for a wide array of file authoring classes which implement it.  These classes call on the codec library to encode media coming in from the camera and microphone and then write it to file in a particular format.  There is an <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=media/libstagefright/MPEG4Writer.cpp;h=7eb7d460f644f917d2feff1d7f3bf5b16f8fd9ce;hb=refs/heads/gingerbread-release" target="_blank">MPEG4Writer</a>, an <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=media/libstagefright/AMRWriter.cpp;h=c0b1abe7564a8904db6336bf22ded23a4c2196ca;hb=refs/heads/gingerbread-release" target="_blank">AMRWriter</a>, an <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=media/libstagefright/rtsp/ARTPWriter.cpp;h=155fd96882dc39a894914691ab6b42edd9ba1a73;hb=refs/heads/gingerbread-release" target="_blank">ARTPWriter</a>, and so on with each of the implemented file formats. Notice that this is the endpoint for file authoring: each of these classes has all it needs to write a video file in the correct format and store it to the SD card.</p>
<p>The great thing about Stagefright (and OpenCORE, for that matter) is that it makes it easy for codecs to be implemented.  The codecs used by Stagefright are expected to adhere to the OpenMAX (OMX) standard, a system for developing codecs that are interchangeable between media frameworks.  So, in theory, if you have an OMX-compliant codec, you should be able to directly plug it into Stagefright and expect it to work.  More information about that <a href="http://www.khronos.org/openmax/spec/" target="_blank">here</a>.</p>
<h2>Freescale proprietary components</h2>
<p>Android is portable, so it doesn&#8217;t provide the actual glue to the hardware.  Instead, it has a handful of interfaces, which the hardware designer can write implementations for.  Notice on the diagram that none of the actual Google code interfaces directly with the kernel layer – this has to be done by Freescale-provided components. (by the way, the source for proprietary components isn&#8217;t available via AOSP&#8217;s gitweb, so follow along with your own local copy of the Freescale release).</p>
<p>The camera, for example, is hidden under a hardware abstraction layer, the CameraHal class (under hardware/mx5x/libcamera/).  The HAL actually contains the name of the camera driver (as well as other important system files it needs to query), and calls IOCTLs on it to set it up and use its functions.  It also performs other important functions, such as encoding the picture in JPEG format or converting between color schemes.</p>
<p>Then there&#8217;s the codec libraries.  If you look on your Android device in /system/lib/ you&#8217;ll see a pile of .so files. These are shared libraries, compiled when you first built Android for your device, and are referenced by running processes such as MediaServer.  You can look in /proc/maps to see which libraries a particular process is using. Among these are a handful of precompiled libraries provided by Freescale – back on your host machine, these are under device/fsl/proprietary/.  You won&#8217;t be able to see the source code for what&#8217;s in these libraries, because they&#8217;re closed-source; however, you can get an idea of who is calling on who with objdump.</p>
<p>Ultimately, each of the OMX codecs are linked within these libraries to a VPU codec, which itself connects to libvpu.so.  The codecs are hardware-accelerated, meaning that since they perform a complex job, it&#8217;s easier to offload it to the VPU to do it.  The proprietary code in libvpu does exactly that.  We&#8217;ll talk more about the VPU later.</p>
<h2>Audio subsystem</h2>
<p>Audio input is handled via an <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=media/libmedia/AudioRecord.cpp;h=a6c515c51051614282dc7dbcdfa3c00d6790e52a;hb=refs/heads/gingerbread-release" target="_blank">AudioRecord</a> object that is abstracted within Stagefright as an AudioSource object.  AudioRecord manages the buffers and parameters for recording on a single audio channel.</p>
<p>The system&#8217;s audio channels themselves are managed by AudioFlinger, which is another service exposed by the MediaServer.  AudioFlinger manages and mixes all of the audio channels in the system.  This is more important for other use cases, such as being able to listen for calls while streaming music, and games with complex, multi-track audio; the important part is that AudioFlinger acts as the interface between alsa and the rest of Android.</p>
<p><a href="http://www.alsa-project.org/" target="_blank">alsa</a>, if you aren&#8217;t aware, is the Advanced Linux Sound Architecture.  alsa provides the drivers and virtual devices necessary to handle quality sound on most Linux systems, and is part of the kernel.  The user-space alsa library is in external/alsa-lib.</p>
<h2>Overlays</h2>
<p>On the other side of the diagram is the Surface.  Android uses Surface objects as a wrapper for graphics buffers, and has a Surface for every UI window you see on your Android device.  You can directly manipulate a Surface from a topside application by using a SurfaceHolder, exposed to the UI via a SurfaceView object.  More important to this discussion is you pass a SurfaceHolder object to your Camera to tell it where to post preview frames within your app&#8217;s UI.</p>
<p>Below the surface, Surfaces are typically managed by SurfaceFlinger. SurfaceFlinger&#8217;s job is to organize all the Surfaces thrown at it by Android and organize them for display.  So, it figures out what parts of which windows are hidden by the one on top.  To SurfaceFlinger, our preview frame is just another UI element, which it happily sorts with all the other Surfaces and posts to the display hardware.</p>
<p>Actually, this isn&#8217;t what happens on the i.MX51.  Android also provides the pipework for surfaces to be posted as a video overlay.  Overlaying allows the Surfaces to be posted directly to the hardware, without having to go through SurfaceFlinger.  Since preview windows are constantly being updated with complex data, using overlays makes sense – less overhead, better performance, happier user.  There are three layers here: an Overlay class which abstracts the hardware from the rest of the system, an interface header (hardware/libhardware/include/hardware/overlay.h) which connects to the hardware, and then Freescale&#8217;s Overlay library in hardware/mx5x/liboverlay/.  The final layer connects to the overlay device, /dev/video16, and posts YUV-formatted overlays to it directly.</p>
<h2>Kernel components</h2>
<p>That about does it for the Android userland.  Whew.</p>
<p>Android runs on a Linux kernel, and follows most of the rules that apply to normal Linux systems.  So to communicate with the hardware, Android processes talk to device drivers, exposed as usual in /dev/.</p>
<p>First, you should know about <a href="http://www.thedirks.org/v4l2/" target="_blank">V4L2</a>.  It&#8217;s the official Linux API for developing video drivers and applications to work with Linux.  If you have any recent experience developing video applications for Linux then you&#8217;ve probably worked with V4L2.  It makes sense that Android would utilize it.  At any rate, both the camera and overlay drivers are V4L2-compliant.  The device drivers are exposed in the filesystem as /dev/video0 (for the camera) and /dev/video16 (for the overlay device).  /dev/fb0 is the actual display device driver.</p>
<p>The camera driver itself is in android/kernel_imx/drivers/media/video/boundary/.  It&#8217;s a V4L2 module that is designed to work with the OV5642 cameras that we use with our Nitrogen boards, and makes calls on the IPU driver to empty buffers that are filled by the camera.   Eric has already said a lot about it in previous posts <a href="http://boundarydevices.com/ov5642-camera-driver-under-android" target="_blank">here</a> and <a href="/omnivision-ov5642-camera-driver-for-linux" target="_blank">here</a>.</p>
<p>We&#8217;ve already mentioned the binder driver and ALSA.  You should also know that there is an mxc_vpu driver which corresponds to the VPU, and is responsible for filling and emptying buffers between the codec subsystem and the VPU itself.  The source is in <a href="http://boundarydevices.com/git?p=linux-bd.git;a=blob;f=drivers/mxc/vpu/mxc_vpu.c;h=67b9ebbdca42c31630252b6f2e23dbbfc8793491;hb=boundary-imx-android-r10">kernel_imx/drivers/mxc/vpu</a>. There is also an IPU driver in <a href="http://boundarydevices.com/git?p=linux-bd.git;a=tree;f=drivers/mxc/ipu;h=d8caae81b84a08a528279e391d032265ef3ab4b8;hb=boundary-imx-android-r10">kernel_imx/drivers/mxc/ipu</a>, which does essentially the same thing for the IPU.</p>
<h2>I.MX51 components</h2>
<p>It also helps to know a few things about the relevant i.MX51 subsystems that are involved in this process.</p>
<p>The Image Processing Unit is the component that connects to both the camera and display.  The IPU automates the process of managing the display.  It actually provides a lot of cool features such as hardware image conversion, but they aren&#8217;t really used by Freescale&#8217;s Android build.</p>
<p>More important to understand is the Video Processing Unit, which implements complex encoding and decoding algorithms in hardware, greatly speeding up the transcoding process. So, we say that the codecs are hardware-accelerated.   According to Freescale, all of the codecs offered by their Android build (RTP and HTTP streaming excepted) are VPU-accelerated, with H.263 and 264 being available for recording and a number of others for playback.  The VPU itself is capable of working with a lot more formats, but not all of them are supported under Freescale&#8217;s Android release yet.</p>
<p>You should know that there is documentation included with the Freescale Android release (look in android_source_folder/doc/SDK_UserGuides; unfortunately, they are not available on the web) that describes an API for the i.MX51 VPU and IPU. So, support is there should you ever find yourself needing to write new kernel code (such as a hardware-accelerated codec) to work with the i.MX51 media system.</p>
<h2>Camera</h2>
<p>Finally there&#8217;s the camera itself.  We use an Omnivision 5642 camera with our Nitrogen boards.  Again, most of what you need to know about this is covered in either the <a href="http://www.ovt.com/products/sensor.php?id=65" target="_blank">datasheet</a> or Eric&#8217;s <a href="http://boundarydevices.com/ov5642-camera-driver-under-android">previous posts</a>.</p>
<h2>Overview: Function call lifecycle</h2>
<p>So to recap, let&#8217;s follow a few of the important function calls through the flowchart above.</p>
<h4>Camera.takePicture(&#8230;)</h4>
<p>Callbacks are registered, and the <code>takePicture()</code> native method is called through the JNI interface.  The native Camera object then starts a transaction with its corresponding CameraService::Client through the Binder interface, notifying it that a takePicture request has been made.  CameraService::Client calls its <code>takePicture()</code> method, which in turn calls the <code>takePicture()</code> method of the CameraHardwareInterface.</p>
<p>At the hardware abstraction layer, the <code>cameraTakePicture()</code> method is called, which sets up a buffer and calls IOCTLs on the driver to fill it from memory held by the IPU&#8217;s DMA controller.  That data is converted to JPEG data and written to memory for later retrieval.  A status message is returned to each of the upper layers, up to the native layer, at which point the topmost function returns and you have your picture.</p>
<h4>MediaRecorder.start()</h4>
<p>Once again, the <code>start()</code> native method is called through the JNI interface, and the native MediaRecorder object starts a transaction with its corresponding MediaRecorderClient class, notifying it that a start request has been made.  The MediaRecorderClient calls its <code>start()</code> method, which in turn calls the <code>start()</code> method of the wrapped StagefrightRecorder class.  Supposing that we have chosen to record an MPEG4 video, the <code>startMPEG4Recording()</code> method is called, and a new MPEG4Writer object is created with the file descriptor we previously passed to the top-level MediaRecorder object.</p>
<p>The MPEG4Writer is set up with some initial parameters, including an encoder of some type, and then its own <code>start()</code> method is called.  The parameters are copied, some header information is written, and then a writer thread is started, which calls the <code>start()</code> methods of a number of Tracks which wrap the AudioSource and CameraSource which were passed in previously.  Each has its own <code>start()</code> method called – in the case of the CameraSource, it calls on the <code>startRecording()</code> method of the Camera object which it wraps, and that call proceeds down the chain as described above.  At the CameraHAL layer, buffers are set aside and are filled with preview frames.  The information is made available to the writer thread as a pointer to the memory where these frames can be found.</p>
<p>A discussion on how writing works, in detail, is probably beyond the scope of this article, since it is very complex, but the gist of it is that data comes in through the source pipes, gets directed to the VPU for hardware-accelerated encoding, and is written to file by the MPEG4Writer or whatever other Writer class is being employed.</p>
<p>That about covers it!  In the next post we&#8217;ll go over the AOSP Camera app in detail, to give you an idea of how to write your own.</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://boundarydevices.com/camera-subsystem-overview-for-i-mx-gingerbread/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

