Tuesday, July 27, 2010

Licensing Service Technology Highlights

We�ve just announced the introduction of a licensing server for Android Market. This should address one of the concerns we�ve heard repeatedly from the Android developer community.

The impact and intent, as outlined in the announcement, are straightforward. If you want to enable your app to use the licensing server, there�s no substitute for reading the authoritative documentation: Licensing Your Applications. Here are some technical highlights.

  • This capability has been in the Android Market client app since 1.5, so you don�t have to be running the latest Android flavor to use it.

  • It�s secure, based on a public/private key pair. Your requests to the server are signed with the public key and the responses from the server with the private key. There�s one key pair per publisher account.

  • Your app doesn�t talk directly to the licensing server; it IPCs to the Android Market client, which in turn takes care of talking to the server.

  • There�s a substantial tool-set that will ship with the SDK, the License Verification Library (LVL). It provides straightforward entry points for querying the server and handling results. Also, it includes modules that you can use to implement certain licensing policies that we expect to be popular.

  • LVL is provided in source form as an Android Library project. It also comes with a testing framework.

  • There�s a Web UI on the publisher-facing part of the Market�s Web site for key management; it includes setup for production and testing.

  • Obviously, you can�t call out to the server when the device is off-network. In this situation you have to decide what to do; one option is to cache licensing status, and LVL includes prebuilt modules to support that.

We think this is a major improvement over the copy-protection option we�ve offered up to this point, and look forward to feedback from developers.

Licensing Service For Android Applications

[This post is by Eric Chu, Android Developer Ecosystem. � Tim Bray]

In my conversations with Android developers, I often hear that you�d like better protection against unauthorized use of your applications. So today, I�m pleased to announce the release of a licensing service for applications in Android Market.

This simple and free service provides a secure mechanism to manage access to all Android Market paid applications targeting Android 1.5 or higher. At run time, with the inclusion of a set of libraries provided by us, your application can query the Android Market licensing server to determine the license status of your users. It returns information on whether your users are authorized to use the app based on stored sales records.

This licensing service operating real time over the network provides more flexibility in choosing license-enforcement strategies, and a more secure approach in protecting your applications from unauthorized use, than copy protection.

The licensing service is available now; our plan is for it to replace the current Android Market copy-protection mechanism over the next few months. I encourage you to check out the Licensing Your Applications section of our Developer Guide and the Android Market Help Center to learn how you can take advantage of this new service immediately.

Friday, July 23, 2010

Adjustment to Market Legals

Please note that we have updated the Android Market Developer Distribution Agreement (DDA). This is in preparation for some work we�re doing on introducing new payment options, which we think developers will like.

In the spirit of transparency, we wanted to highlight the changes:

  • In Section 13.1, �authorized carriers� have been added as an indemnified party.

  • Section 13.2 is new in its entirety, covering indemnity for payment processors for claims related to tax accrual.

These new terms apply immediately to anyone joining Android Market as a new publisher. Existing publishers have been notified of this change via email; they have up to 30 days to sign into the Android Market developer console to accept the new terms.

Thursday, July 15, 2010

Market Statistics Adjustments

If you look closely today, you'll notice that some per-app Android Market statistics have lower values; not big differences, but noticeable in a few cases. We discovered last week that, starting in early June, certain events had been double-counted: installs, uninstalls, impressions, and so on. The most obvious symptom was (for paid apps) a discrepancy between the number of installs and the number of reported sales through Checkout.

The underlying problem has been corrected and following data repair, the reported statistics should now be accurate. Our apologies for the glitch.

Android Market Welcomes Korea!

As of today, Android Market is open for business to application buyers in the Republic of Korea. We hope that this will make the outstanding Android devices now available in that nation even more useful and fun. We welcome the people of Korea, acknowledged everywhere as one of the world's most-wired societies, to the world of Android.

Monday, July 12, 2010

How to have your (Cup)cake and eat it too

[This post is by Adam Powell, his second touchy-feely outing in just a few weeks. I asked him to send me a better picture than we ran last time, and got this in response. Photo by our own Romain Guy. � Tim Bray]

Android developers concerned with targeting every last device with their apps are no doubt familiar with this chart:

On July 1, 2010 this was the breakdown of active devices running different versions of the Android platform. With all of the new platform features added to the Android SDK in each version, this chart has many developers shouting the F-word when they are forced to choose between integrating newer platform features and providing their app to the widest possible audience.

Savvy Android developers already know that these two options aren�t really mutually exclusive, but that straddling between them can be painful. In this post I�m going to show you that it doesn�t have to be that way.

Several weeks ago we took a look at how to handle multitouch on Android 2.0 (Eclair) and above, and by the end we had a simple demo app. That app uses features exclusive to Android 2.2 (Froyo) which as of this writing hasn�t had a chance to reach many devices yet. In this post we�re going to refactor that demo to run on devices all the way back to Android 1.5 (Cupcake). If you�d like to follow along, start off by grabbing the code in the trunk of the android-touchexample project on Google Code.

The problem manifests

The uses-sdk tag in your AndroidManifest.xml can specify both a minSdkVersion and a targetSdkVersion. You can use this to declare that while your app is prepared to run on an older version of the platform, it knows about newer versions. Your app can now build against newer SDKs. However, if your code accesses newer platform functionality directly you will probably see something like this in the system log of devices running an older version of Android:

E/dalvikvm(  792): Could not find method android.view.MotionEvent.getX, referenced from method com.example.android.touchexample.TouchExampleView.onTouchEvent
W/dalvikvm( 792): VFY: unable to resolve virtual method 17: Landroid/view/MotionEvent;.getX (I)F
W/dalvikvm( 792): VFY: rejecting opcode 0x6e at 0x0006
W/dalvikvm( 792): VFY: rejected Lcom/example/android/touchexample/TouchExampleView;.onTouchEvent (Landroid/view/MotionEvent;)Z
W/dalvikvm( 792): Verifier rejected class Lcom/example/android/touchexample/TouchExampleView;
D/AndroidRuntime( 792): Shutting down VM
W/dalvikvm( 792): threadid=3: thread exiting with uncaught exception (group=0x4000fe70)

We broke the contract of minSdkVersion, and here is the result. When we build our app against SDK 8 (Froyo) but declare minSdkVersion="3" (Cupcake) we promise the system that we know what we�re doing and we won�t try to access anything that doesn�t exist. If we mess this up, we see the above, and our users see an ugly error message.

Cue a lot of frustrated users and one-star ratings on Market. We need a safe way of accessing newer platform functionality without making the verifier angry on older platform versions.

Stop and reflect

Many Android developers are already familiar with the practice of accomplishing this through reflection. Reflection lets your code interface with the runtime, detect when certain methods or classes are present, and invoke or instantiate them without touching them directly.

The prospect of querying each platform feature individually and conditionally invoking it using reflection isn�t pretty. It�s ugly. It�s slow. It�s cumbersome. Most of all, heavy use can turn your app�s codebase into an unmaintainable mess. What if I said there is a way to write Android apps that target Android 1.5 (Cupcake) through 2.2 (Froyo) and beyond with a single codebase and no reflection at all?

Lazy Loading

Computer science researcher Bill Pugh published and popularized a method of writing singletons in Java that takes advantage of the laziness of ClassLoaders. Wikipedia explains his solution further. The code looks like this:

public class Singleton {
// Private constructor prevents instantiation from other classes
private Singleton() {}

/**
* SingletonHolder is loaded on the first execution of Singleton.getInstance()
* or the first access to SingletonHolder.INSTANCE, not before.
*/
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}

public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

There is a very important guaranteed behavior at work here explained by the comment above SingletonHolder. Java classes are loaded and initialized on first access - instantiating the class or accessing one of its static fields or methods for the first time. This is relevant to us because classes are verified by the VM when they are loaded, not before. We now have everything we need to write Android apps that span versions without reflection.

Designing for compatibility

As it turns out this is fairly simple to apply. You generally will want your app to degrade gracefully on older platform versions, dropping features or providing alternate functionality when the platform support isn�t available. Since Android platform features are tied to the API level you have only one axis to consider when designing for compatibility.

In most cases this version support can be expressed as a simple class hierarchy. You can design your app to access version-sensitive functionality through a version-independent interface or abstract class. Subclasses of that interface intended to run on newer platform versions will support newer platform features, and subclasses intended for older versions might need to present alternate ways for your users to access app functionality.

Your app can use a factory method, abstract factory, or other object creation pattern to instantiate the proper subclass at runtime based on the information exposed by android.os.Build.VERSION. This last step insures that the system will never attempt to load a class it can�t verify, preserving compatibility.

The principle in practice

At the beginning of this post I said that we are going to refactor the touch example app from Making Sense of Multitouch to be compatible from API level 3 (Cupcake) on through API level 8 (Froyo). In that post I pointed out that GestureDetectors can be a useful pattern for abstracting the processing of touch events. At the time I didn�t realize how soon that statement would be put to the test. We can refactor the version-specific elements of the demo app�s touch handling into an abstract GestureDetector.

Before we begin the real work, we need to change our manifest to declare that we support API level 3 devices with minSdkVersion in the uses-sdk tag. Keep in mind that we�re still targeting SDK 8, both with targetSdkVersion in our manifest and in our project configuration. Our manifest now looks like this:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.touchexample"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".TouchExampleActivity"
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>
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="8" />
</manifest>

Our TouchExampleView class isn�t compatible with Android versions prior to Froyo thanks to its use of ScaleGestureDetector, and it isn�t compatible with versions prior to Eclair thanks to its use of the newer MotionEvent methods that return multitouch data. We need to abstract that functionality out into classes that will not be loaded on versions of the platform that don�t support it. To do this, we will create the abstract class VersionedGestureDetector.

The example app allows the user to perform two gestures, drag and scale. VersionedGestureDetector will therefore publish two events to an attached listener, onDrag and onScale. TouchExampleView will obtain a VersionedGestureDetector instance appropriate to the platform version, filter incoming touch events through it, and respond to the resulting onDrag and onScale events accordingly.

The first pass of VersionedGestureDetector looks like this:

public abstract class VersionedGestureDetector {
OnGestureListener mListener;

public abstract boolean onTouchEvent(MotionEvent ev);

public interface OnGestureListener {
public void onDrag(float dx, float dy);
public void onScale(float scaleFactor);
}
}

We�ll start with the simplest functionality first, the VersionedGestureDetector for Cupcake. For simplicity�s sake in this example we will implement each version as a private static inner class of VersionedGestureDetector. You can organize this however you please, of course, as long as you use the lazy loading technique shown above or some equivalent. Don�t touch any class that directly accesses functionality not supported by your platform version.

private static class CupcakeDetector extends VersionedGestureDetector {
float mLastTouchX;
float mLastTouchY;

@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
mLastTouchX = ev.getX();
mLastTouchY = ev.getY();
break;
}
case MotionEvent.ACTION_MOVE: {
final float x = ev.getX();
final float y = ev.getY();

mListener.onDrag(x - mLastTouchX, y - mLastTouchY);

mLastTouchX = x;
mLastTouchY = y;
break;
}
}
return true;
}
}

This simple implementation dispatches onDrag events whenever a pointer is dragged across the touchscreen. The values it passes are the X and Y distances traveled by the pointer.

In Eclair and later we will need to properly track pointer IDs during drags so that our draggable object doesn�t jump around as extra pointers enter and leave the touchscreen. The base implementation of onTouchEvent in CupcakeDetector can handle drag events for us with a few tweaks. We�ll add the methods getActiveX and getActiveY to fetch the appropriate touch coordinates and override them in EclairDetector to get the coordinates from the correct pointer:

private static class CupcakeDetector extends VersionedGestureDetector {
float mLastTouchX;
float mLastTouchY;

float getActiveX(MotionEvent ev) {
return ev.getX();
}

float getActiveY(MotionEvent ev) {
return ev.getY();
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
mLastTouchX = getActiveX(ev);
mLastTouchY = getActiveY(ev);
break;
}
case MotionEvent.ACTION_MOVE: {
final float x = getActiveX(ev);
final float y = getActiveY(ev);

mListener.onDrag(x - mLastTouchX, y - mLastTouchY);

mLastTouchX = x;
mLastTouchY = y;
break;
}
}
return true;
}
}

And now EclairDetector, overriding the new getActiveX and getActiveY methods. Most of this code should be familiar from the original touch example:

private static class EclairDetector extends CupcakeDetector {
private static final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
private int mActivePointerIndex = 0;

@Override
float getActiveX(MotionEvent ev) {
return ev.getX(mActivePointerIndex);
}

@Override
float getActiveY(MotionEvent ev) {
return ev.getY(mActivePointerIndex);
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = ev.getPointerId(0);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mActivePointerId = INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_POINTER_UP:
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mActivePointerId = ev.getPointerId(newPointerIndex);
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
}
break;
}

mActivePointerIndex = ev.findPointerIndex(mActivePointerId);
return super.onTouchEvent(ev);
}
}

EclairDetector calls super.onTouchEvent after determining the active pointer index and lets CupcakeDetector take care of dispatching the drag event. Supporting multiple platform versions doesn�t have to mean code duplication.

Finally, let�s add scale gesture support for Froyo devices that have ScaleGestureDetector. We�ll need a couple more changes to CupcakeDetector first; we don�t want to drag normally while scaling. Some devices have touchscreens that don�t deal well with it, and we would want to handle it differently on devices that do anyway. We�ll add a shouldDrag method to CupcakeDetector that we�ll check before dispatching onDrag events.

The final CupcakeDetector:

private static class CupcakeDetector extends VersionedGestureDetector {
float mLastTouchX;
float mLastTouchY;

float getActiveX(MotionEvent ev) {
return ev.getX();
}

float getActiveY(MotionEvent ev) {
return ev.getY();
}

boolean shouldDrag() {
return true;
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
mLastTouchX = getActiveX(ev);
mLastTouchY = getActiveY(ev);
break;
}
case MotionEvent.ACTION_MOVE: {
final float x = getActiveX(ev);
final float y = getActiveY(ev);

if (shouldDrag()) {
mListener.onDrag(x - mLastTouchX, y - mLastTouchY);
}

mLastTouchX = x;
mLastTouchY = y;
break;
}
}
return true;
}
}

EclairDetector remains unchanged. FroyoDetector is below. shouldDrag will return true as long as we do not have a scale gesture in progress:

private static class FroyoDetector extends EclairDetector {
private ScaleGestureDetector mDetector;

public FroyoDetector(Context context) {
mDetector = new ScaleGestureDetector(context,
new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override public boolean onScale(ScaleGestureDetector detector) {
mListener.onScale(detector.getScaleFactor());
return true;
}
});
}

@Override
boolean shouldDrag() {
return !mDetector.isInProgress();
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
mDetector.onTouchEvent(ev);
return super.onTouchEvent(ev);
}
}

Now that we have our detector implementations in order we need a way to create them. Let�s add a factory method to VersionedGestureDetector:

public static VersionedGestureDetector newInstance(Context context,
OnGestureListener listener) {
final int sdkVersion = Integer.parseInt(Build.VERSION.SDK);
VersionedGestureDetector detector = null;
if (sdkVersion < Build.VERSION_CODES.ECLAIR) {
detector = new CupcakeDetector();
} else if (sdkVersion < Build.VERSION_CODES.FROYO) {
detector = new EclairDetector();
} else {
detector = new FroyoDetector(context);
}

detector.mListener = listener;

return detector;
}

Since we�re targeting Cupcake, we don�t have access to Build.VERSION.SDK_INT yet. We have to parse the now-deprecated Build.VERSION.SDK instead. But why is accessing Build.VERSION_CODES.ECLAIR and Build.VERSION_CODES.FROYO safe? As primitive static final int constants, these are inlined by the compiler at build time.

Our VersionedGestureDetector is ready. Now we just need to hook it up to TouchExampleView, which has become considerably shorter:

public class TouchExampleView extends View {
private Drawable mIcon;
private float mPosX;
private float mPosY;

private VersionedGestureDetector mDetector;
private float mScaleFactor = 1.f;

public TouchExampleView(Context context) {
this(context, null, 0);
}

public TouchExampleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public TouchExampleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mIcon = context.getResources().getDrawable(R.drawable.icon);
mIcon.setBounds(0, 0, mIcon.getIntrinsicWidth(), mIcon.getIntrinsicHeight());

mDetector = VersionedGestureDetector.newInstance(context, new GestureCallback());
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
mDetector.onTouchEvent(ev);
return true;
}

@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);

canvas.save();
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
mIcon.draw(canvas);
canvas.restore();
}

private class GestureCallback implements VersionedGestureDetector.OnGestureListener {
public void onDrag(float dx, float dy) {
mPosX += dx;
mPosY += dy;
invalidate();
}

public void onScale(float scaleFactor) {
mScaleFactor *= scaleFactor;

// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));

invalidate();
}
}
}

Wrapping up

We�ve now adapted the touch example app to work from Android 1.5 on through the latest and greatest, taking advantage of newer platform features as available without a single reflective call. The same principles shown here can apply to any new Android feature that you want to use while still allowing your app to run on older platform versions:

  • The ClassLoader loads classes lazily and will only load and verify classes on first access.

  • Factor out app functionality that can differ between platform versions with a version-independent interface or abstract class.

  • Instantiate a version-dependent implementation of it based on the platform version detected at runtime. This keeps the ClassLoader from ever touching a class that it will not be able to verify.

To see the final cross-version touch example app, check out the �cupcake� branch of the android-touchexample project on Google Code.

Extra Credit

In this example we didn�t provide another way for pre-Froyo users to zoom since ScaleGestureDetector was only added as a public API for 2.2. For a real app we would want to offer some alternate affordance to users. Traditionally Android offers a set of small tappable zoom buttons along the bottom of the screen. The ZoomControls and ZoomButtonsController classes in the framework can help you present these controls to the user in a standard way. Implementing this is left as an exercise for the reader.

Thursday, July 8, 2010

Apps on SD Card: The Details

[This post is by Suchi Amalapurapu, an engineer who worked on this feature. � Tim Bray]

Android 2.2 supports application installation on external storage devices like the SD card. This should give users room for many more apps, and will also benefit certain categories, like games, that need huge assets.

(Note that not all of the contents of an �SD-card-resident� APK are actually on the card; the dex files, private data directories, and native shared libraries remain in internal storage.)

The �Manage Applications� screen in the Settings app now has an �On SD Card� tab. The sizes listed in Manage Applications only include the space taken by the application on internal storage.

The Application Info screen now has either a �move to SD card� or �move to phone� button, but this is often disabled. Copy-protected apps and updates to system apps can�t be moved to the SD card, nor can those which are don�t specify that they work on the SD card.

Controlling Installation Preference

SD-card installation is an optional feature on Android 2.2, is under the control of the developer not the user, and will not affect any applications built prior to Android 2.2.

Application developers can set the field android:installLocation in the root manifest element to one of these values:

  • internalOnly: Install the application on internal storage only. This will result in storage errors if the device runs low on internal storage.

  • preferExternal: The android system tries to install the application on external storage. If that is full, the application is installed on internal storage.

  • auto: Let the Android system decide the best install location for the application. The default system policy is to install the application on internal storage first. If the system is running low on storage, the application is then installed on external storage.

If the installLocation is not specified (as is the case for all applications prior to Android 2.2), the application is installed on internal storage. Application updates will by default try to retain their install location, but application developers may change the installLocation field in an update. Installing an application with this new attribute on older devices will not break compatibility and these applications will be installed on internal storage only.

Application developers can also explicitly install under-development code on external storage via adb install flags, which override the installLocation field: -f for internal storage, and -s for external storage. They can also override the default install location to verify and test application installation on SD card with an adb shell pm command:

adb shell pm setInstallLocation option

Where option is one of:

  • 0 [auto] Let the system decide.

  • 1 [internal only]

  • 2 [external]

The current install location can be retrieved via
adb shell pm getInstallLocation
Note that changing this default can cause applications to misbehave if they�re not prepared to live on the SD card.

USB Mass Storage interactions

The Android system stores the SD-card-resident applications� APKs in a secure application-specific container. A new flag FLAG_EXTERNAL_STORAGE in ApplicationInfo object indicates that an application is currently installed on the SD card. This storage is removed when an SD-card-resident app is uninstalled.

When an Android device�s SD card is unmounted, applications installed on it are no longer available. Their internal storage is still there and such apps can be uninstalled without replacing the SD card.

The Android framework provides several broadcast intents to support the (small) class of applications, such as launchers, that access other applications� resources:

  • When the SD card is unmounted, ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE with a list of the disabled applications and a list of corresponding application uid�s via extra attributes in the broadcast intent.

  • When the SD card is mounted, ACTION_EXTERNAL_APPLICATIONS_AVAILABLE with a list of enabled applications and a list of corresponding application uid�s via extra attributes in the broadcast intent.

Applications that handle broadcast intents like ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED may also want to handle these additional notifications.

When an application gets moved between internal to external storage and vice versa, the application is disabled first (which includes broadcasting intent ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE), the asset resources are copied, and then the application is enabled (which includes broadcasting intent ACTION_EXTERNAL_APPLICATIONS_AVAILABLE).

Security and Performance Implications

Applications on SD card are mounted via Linux�s loopback interface and encrypted via a device-specific key, so they cannot be decrypted on any other device. Note that this is security measure and does not provide copy protection; the apps are world readable just like the resources of normal applications installed on the device�s internal storage. Copy-protected applications cannot be installed on external media. In the interests of stability, updates to system applications also cannot be installed on the external media. The application resources stored on external storage are read-only and hence there are no performance issues with loading or launching applications on SD card.

Swapping or Transferring SD Card Contents

It has always been the case that when you swap SD cards on an Android device, if you physically copy the contents of the old card to the new one, the system will use the data on the new card as if nothing had changed. This is also true of apps which have been installed on the SD card.

When not to install on SD card?

The advantage of installing on SD card is easy to understand: contention for storage space is reduced. There are costs, the most obvious being that your app is disabled when the SD card is either removed or in USB Mass Storage mode; this includes running Services, not just interactive Activities. Aside from this, device removal disables an application�s Widgets, Input methods, Account Managers, Device administrators, Live wallpapers, and Live folders, and may require explicit user action to re-enable them.

Android 2.2 SDK refresh

As you may have noticed, the source code for Android 2.2, which we call Froyo, has been released.

The Android 2.2 SDK that was released at Google I/O contained a preview of the Froyo system image and today, we are releasing an update to bring it into sync with the system image pushed to Nexus One devices.

I encourage all developers to use the SDK manager to update to this version.

Wednesday, July 7, 2010

Android Love at OSCON

Android developers who aren't already going should take a moment to check out the OSCON 2010 schedule, and give serious thought to a trip to Portland in a couple of weeks. OSCON is one of the world's premiere events for those who care about Open Source, and one of my personal favorite conferences, with a powerful community vibe. And this year, the Android drums are sounding.

There are a bunch of mobile and Android-related sessions, both pure and extremely impure; for example, I bet both Steve Jobs and I are dubious about Cross-Compiling Android Applications to the iPhone. There's a full-dress Android for Java Developers tutorial. And (the one I helped cook up) there's the Android Hands-On; last I heard, registration for that is approaching 200 and, since O'Reilly found us a big room, it's not full up; but it will be.

On top of which, there are going to be a herd of Googlers at OSCON, and a sub-herd of Androiders, including Dan Morrill and Justin Mattson and me. This isn't surprising; what does surprise me is that OSCON hasn't previously been an Android love-fest. Because if you're interested in mobile devices and have a hankering for Open Source, Android is for you.