The following is the first few sections of a chapter from The Busy Coder's Guide to Android Development, plus headings for the remaining major sections, to give you an idea about the content of the chapter.


Advanced Manifest Tips

If you have been diligent about reading this book (versus having randomly jumped to this chapter), you will already have done a fair number of things with your project’s AndroidManifest.xml file:

  1. Used it to define components, like activities, services, content providers, and manifest-registered broadcast receivers
  2. Used it to declare permissions your application requires, or possibly to define permissions that other applications need in order to integrate with your application
  3. Used it to define what SDK level, screen sizes, and other device capabilities your application requires

In this chapter, we continue looking at things the manifest offers you, starting with a discussion of controlling where your application gets installed on a device, and wrapping up with a bit of information about activity aliases.

Prerequisites

Understanding this chapter requires that you have read the core chapters of this book.

Just Looking For Some Elbow Room

On October 22, 2008, the HTC Dream was released, under the moniker of “T-Mobile G1”, as the first production Android device.

Complaints about the lack of available storage space for applications probably started on October 23rd.

The Dream, while a solid first Android device, offered only 70MB of on-board flash for application storage. This storage had to include:

  1. The Android application (APK) file
  2. Any local files or databases the application created, particularly those deemed unsafe to put on the SD card (e.g., privacy)
  3. Extra copies of some portions of the APK file, such as the compiled Dalvik bytecode, which get unpacked on installation for speed of access

It would not take long for a user to fill up 70MB of space, then have to start removing some applications to be able to try others.

Users and developers alike could not quite understand why the Dream had so little space compared to the available iPhone models, and they begged to at least allow applications to install to the SD card, where there would be more room. This, however, was not easy to implement in a secure fashion, and it took until Android 2.2 for the feature to become officially available.

If your app’s android:minSdkVersion is 11 or higher, you can probably ignore all of this. At that time, what the Android SDK refers to as “internal storage” and “external storage” were moved to be part of one filesystem partition, and so there is no artificial division of space between the two.

But, if you are still supporting Android 2.2 and 2.3, you may wish to consider supporting having your app be installed to, or moved to, external storage.

Configuring Your App to Reside on External Storage

Indicating to Android that your application can reside on the SD card is easy… and necessary, if you want the feature. If you do not tell Android this is allowed, Android will not install your application to the SD card, nor allow the user to move the application to the SD card.

All you need to do is add an android:installLocation attribute to the root <manifest> element of your AndroidManifest.xml file. There are three possible values for this attribute:

If you use preferExternal, then your application will be initially installed on the SD card in most cases. Android reserves the right to still install your application on internal storage in cases where that makes too much sense, such as there not being an SD card installed at the time.

If you use auto, then Android will make the decision as to the installation location, based on a variety of factors. In effect, this means that auto and preferExternal are functionally very similar – all you are doing with preferExternal is giving Android a hint as to your desired installation destination.

Because Android decides where your application is initially installed, and because the user has the option to move your application between the SD card and on-board flash, you cannot assume any given installation spot. The exception is if you choose internalOnly, in which case Android will honor your request, at the potential cost of not allowing the installation at all if there is no more room in on-board flash.

For example, here is the manifest from the SMS/Sender sample project, profiled in another chapter, showing the use of preferExternal:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.commonsware.android.sms.sender"
  android:installLocation="preferExternal"
  android:versionCode="1"
  android:versionName="1.0">

  <uses-permission android:name="android.permission.READ_CONTACTS"/>
  <uses-permission android:name="android.permission.SEND_SMS"/>

  <uses-sdk
    android:minSdkVersion="7"
    android:targetSdkVersion="11"/>

  <supports-screens
    android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="false"/>

  <application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name">
    <activity
      android:name="Sender"
      android:label="@string/app_name">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>

        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>
  </application>

</manifest>

Since this feature only became available in Android 2.2, to support older versions of Android, just have your build tools target API level 8 (e.g., compileSdkVersion of 8 or higher in build.gradle for Android Studio users) while having your minSdkVersion attribute in the manifest state the lowest Android version your application supports overall. Older versions of Android will ignore the android:installLocation attribute. So, for example, in the above manifest, the Sender application supports API level 4 and above (Android 1.6 and newer), but still can use android:installLocation="preferExternal", because the build tools are targeting API level 8.

What the User Sees

On newer devices, such as those running Android 4.2, the user will see nothing different. That is because internal and external storage share a common pool of space, and therefore there is no advantage in having your application installed to external storage.

However, on, say, Android 2.3, you will see a difference in behavior.

For an application that wound up on external storage, courtesy of your choice of preferExternal or auto, the user will have an option to move it to the phone’s internal storage. This can be done by choosing the application in the Manage Applications list in the Settings application, then clicking the “Move to phone” button.

Conversely, if your application is installed in on-board flash, and it is movable to external storage, they will be given that option with a “Move to SD card” button.

What the Pirate Sees

Ideally, the pirate sees nothing at all.

One of the major concerns with installing applications to the SD card is that the SD card is usually formatted FAT32 (vfat), offering no protection from prying eyes. The concern was that pirates could then just pluck the APK file off external storage and distribute it, even for paid apps from the Play Store.

Apparently, they solved this problem.

To quote the Android developer documentation:

The unique container in which your application is stored is encrypted with a randomly generated key that can be decrypted only by the device that originally installed it. Thus, an application installed on an SD card works for only one device.

Moreover, this “unique container” is not normally mounted when the user mounts external storage on their host machine. The user mounts /mnt/sdcard; the “unique container” is /mnt/asec.

What Your App Sees… When External Storage is Inaccessible

So far, this has all seemed great for users and developers. Developers need to add a single attribute to the manifest, and Android 2.2+ users gain the flexibility of where the app gets stored.

Alas, there is a problem, and it is a big one: on Android 1.x and 2.x, either the host PC or the device can have access to the SD card, but not both. As a result, if the user makes the SD card available to the host PC, by plugging in the USB cable and mounting the SD card as a drive via a Notification or other means, that SD card becomes unavailable for running applications.

So, what happens?

  1. First, your application is terminated forcibly, as if your process was being closed due to low memory. Notably, your activities and services will not be called with onDestroy(), and instance state saved via onSaveInstanceState() is lost.
  2. Second, your application is unhooked from the system. Users will not see your application in the launcher, your AlarmManager alarms will be canceled, and so on.
  3. When the user makes external storage available to the phone again, your application will be hooked back into the system and will be once again available to the user (for example, your icon will reappear in the launcher)

The upshot: if your application is simply a collection of activities, otherwise not terribly connected to Android, the impact on your application is no different than if the user reboots the phone, kills your process via a so-called “task killer” application, etc. If, however, you are doing more than that, the impacts may be more dramatic.

Perhaps the most dramatic impact, from a user’s standpoint, will be if your application implements app widgets. If the user has your app widget on her home screen, that app widget will be removed when the SD card becomes unavailable to the phone. Worse, your app widget cannot be re-added to the home screen until the phone is rebooted (a limitation that hopefully will be lifted sometime after Android 2.2).

The user is warned about this happening, at least in general:

Warning when unmounting the SD card
Figure 886: Warning when unmounting the SD card

Two broadcast Intents are sent out related to this:

Note that the documentation is unclear as to whether your own application, that had been on the SD card, can receive ACTION_EXTERNAL_APPLICATIONS_AVAILABLE once the SD card is back in action.

Also note that all of these problems hold true for longer if the user physically removes the SD card from the device. If, for example, they replace the card with a different one — such as one with more space — your application will be largely lost. They will see a note in their applications list for your application, but the icon will indicate it is on external storage, and the only thing they can do is uninstall it:

The Manage Applications list, with an application shown from a removed SD card
Figure 887: The Manage Applications list, with an application shown from a removed SD card

Choosing Whether to Support External Storage

Given the huge problem from the previous section, the question of whether or not your application should support external storage is far from clear.

As the Android developer documentation states:

Large games are more commonly the types of applications that should allow installation on external storage, because games don’t typically provide additional services when inactive. When external storage becomes unavailable and a game process is killed, there should be no visible effect when the storage becomes available again and the user restarts the game (assuming that the game properly saved its state during the normal Activity lifecycle).

Conversely, if your application implements any of the following features, it may be best to not support external storage:

  1. Polling of Web services or other Internet resources via a scheduled alarm
  2. Account managers and their corresponding sync adapters, for custom sources of contact data
  3. App widgets, as noted in the previous section
  4. Device administration extensions
  5. Live folders
  6. Custom soft keyboards (“input method engines”)
  7. Live wallpapers
  8. Custom search providers

But, as noted earlier, this is not even usually necessary on API Level 11+ devices. Hence, even if your app would otherwise qualify for being installed to external storage, you may not wish to bother. If few devices (Android 2.2 and Android 2.3) might need the capability, it may not be worth the extra testing burden.

Android 6.0 and “Adoption” of Removable Storage

When Android 3.0 did away with the required separate partitions for internal storage and external storage, the android:installLocation option fell out of use, as there was no particular value in having the apps on external storage. For single-partition devices — meaning, for most devices — users did not even have the option for moving their apps to external storage.

However, android:installLocation is returning to relevance, once again courtesy of removable media.

On Android 6.0+, users with removable storage options, such as micro SD card slots, have the option of “adopting” those as an extension of the device’s internal storage. Once done, apps set with auto or preferExternal for android:installLocation can be moved to the removable media. However, there appears to be one key difference: not only is the APK on the removable media, but so is all of that app’s portion of internal storage. The removable media is encrypted, so the material copied to the removable media should remain fairly inaccessible.

From the user’s standpoint, for low-end devices with minimal on-board flash, they have additional storage space that they can use for apps.

However:

Android 6.0, Ejected Adopted Removable Media Notification
Figure 888: Android 6.0, Ejected Adopted Removable Media Notification

The user does have an “Erase & Format” option that will reformat the removable media and allow it to be permanently removed from the device. It does not appear that this will automatically move any apps back to internal storage. The users would need to move those apps back to internal storage by means of the Apps list in Settings.

Normally, it appears this system will be limited to internal card slots for things like micro SD cards. While USB On-The-Go (OTG) allows Android devices to access thumb drives, those are likely to be accidentally removed by the user (not to mention they usually tie up the charging port). However, for development testing purposes, you can run the adb shell sm set-force-adoptable true command to allow the device to adopt USB OTG drives. Note though that once you do this, the drive is more or less owned by that Android device until you “Erase & Format” it, and you will lose everything on the drive as part of this whole process.

Using an Alias

The preview of this section is in the process of being translated from its native Klingon.

Getting Meta (Data)

The preview of this section was traded for a bag of magic beans.