Merge "Media server death nodification"
diff --git a/Android.mk b/Android.mk
index ded8173..d6beac5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -75,6 +75,7 @@
 	core/java/android/app/IActivityWatcher.aidl \
 	core/java/android/app/IAlarmManager.aidl \
 	core/java/android/app/IBackupAgent.aidl \
+    core/java/android/app/IDevicePolicyManager.aidl \
 	core/java/android/app/IInstrumentationWatcher.aidl \
 	core/java/android/app/INotificationManager.aidl \
 	core/java/android/app/ISearchManager.aidl \
@@ -110,6 +111,7 @@
 	core/java/android/os/ICheckinService.aidl \
 	core/java/android/os/IMessenger.aidl \
 	core/java/android/os/IMountService.aidl \
+	core/java/android/os/INetworkManagementService.aidl \
 	core/java/android/os/INetStatService.aidl \
 	core/java/android/os/IParentalControlCallback.aidl \
 	core/java/android/os/IPermissionController.aidl \
diff --git a/api/current.xml b/api/current.xml
index b690e42..ee7d330 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -177,6 +177,17 @@
  visibility="public"
 >
 </field>
+<field name="BIND_DEVICE_ADMIN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.BIND_DEVICE_ADMIN&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="BIND_INPUT_METHOD"
  type="java.lang.String"
  transient="false"
@@ -188,6 +199,17 @@
  visibility="public"
 >
 </field>
+<field name="BIND_WALLPAPER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.BIND_WALLPAPER&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="BLUETOOTH"
  type="java.lang.String"
  transient="false"
@@ -19831,6 +19853,600 @@
 </parameter>
 </method>
 </interface>
+<class name="DeviceAdmin"
+ extends="android.content.BroadcastReceiver"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="DeviceAdmin"
+ type="android.app.DeviceAdmin"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getManager"
+ return="android.app.DevicePolicyManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="getWho"
+ return="android.content.ComponentName"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="onDisabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onPasswordChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onPasswordFailed"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onPasswordSucceeded"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onReceive"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<field name="ACTION_DEVICE_ADMIN_DISABLED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.DEVICE_ADMIN_DISABLED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DEVICE_ADMIN_ENABLED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.DEVICE_ADMIN_ENABLED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_PASSWORD_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.ACTION_PASSWORD_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_PASSWORD_FAILED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.ACTION_PASSWORD_FAILED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_PASSWORD_SUCCEEDED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.ACTION_PASSWORD_SUCCEEDED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DEVICE_ADMIN_META_DATA"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.device_admin&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DeviceAdminInfo"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="DeviceAdminInfo"
+ type="android.app.DeviceAdminInfo"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="receiver" type="android.content.pm.ResolveInfo">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+<exception name="XmlPullParserException" type="org.xmlpull.v1.XmlPullParserException">
+</exception>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="dump"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pw" type="android.util.Printer">
+</parameter>
+<parameter name="prefix" type="java.lang.String">
+</parameter>
+</method>
+<method name="getActivityInfo"
+ return="android.content.pm.ActivityInfo"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getComponent"
+ return="android.content.ComponentName"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPackageName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getReceiverName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="loadIcon"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pm" type="android.content.pm.PackageManager">
+</parameter>
+</method>
+<method name="loadLabel"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pm" type="android.content.pm.PackageManager">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="DevicePolicyManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getActiveMinimumPasswordLength"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getActivePasswordMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCurrentFailedPasswordAttempts"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMaximumTimeToLock"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMinimumPasswordLength"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPasswordMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isAdminActive"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="who" type="android.content.ComponentName">
+</parameter>
+</method>
+<method name="removeActiveAdmin"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="who" type="android.content.ComponentName">
+</parameter>
+</method>
+<method name="setMaximumTimeToLock"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="timeMs" type="long">
+</parameter>
+</method>
+<method name="setMinimumPasswordLength"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+</method>
+<method name="setPasswordMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="mode" type="int">
+</parameter>
+</method>
+<method name="wipeData"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="ACTION_ADD_DEVICE_ADMIN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.ADD_DEVICE_ADMIN&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_SET_NEW_PASSWORD"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.action.SET_NEW_PASSWORD&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_DEVICE_ADMIN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.app.extra.DEVICE_ADMIN&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PASSWORD_MODE_ALPHANUMERIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2000"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PASSWORD_MODE_NUMERIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1000"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PASSWORD_MODE_UNSPECIFIED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WIPE_EXTERNAL_STORAGE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WIPE_LOW_LEVEL_FORMAT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="Dialog"
  extends="java.lang.Object"
  abstract="false"
@@ -32576,6 +33192,17 @@
  visibility="public"
 >
 </field>
+<field name="DEVICE_POLICY_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;device_policy&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="DROPBOX_SERVICE"
  type="java.lang.String"
  transient="false"
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index ad6540a..e65cdf1 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -34,12 +34,14 @@
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/OMXCodec.h>
+#include <media/mediametadataretriever.h>
 
 using namespace android;
 
 static long gNumRepetitions;
 static long gMaxNumFrames;  // 0 means decode all available.
 static long gReproduceBug;  // if not -1.
+static bool gPreferSoftwareCodec;
 
 static int64_t getNowUs() {
     struct timeval tv;
@@ -59,7 +61,9 @@
         rawSource = source;
     } else {
         rawSource = OMXCodec::Create(
-            client->interface(), meta, false /* createEncoder */, source);
+            client->interface(), meta, false /* createEncoder */, source,
+            NULL /* matchComponentName */,
+            gPreferSoftwareCodec ? OMXCodec::kPreferSoftwareCodecs : 0);
 
         if (rawSource == NULL) {
             fprintf(stderr, "Failed to instantiate decoder for '%s'.\n", mime);
@@ -219,6 +223,8 @@
     fprintf(stderr, "       -m max-number-of-frames-to-decode in each pass\n");
     fprintf(stderr, "       -b bug to reproduce\n");
     fprintf(stderr, "       -p(rofiles) dump decoder profiles supported\n");
+    fprintf(stderr, "       -t(humbnail) extract video thumbnail\n");
+    fprintf(stderr, "       -s(oftware) prefer software codec\n");
 }
 
 int main(int argc, char **argv) {
@@ -227,12 +233,14 @@
     bool audioOnly = false;
     bool listComponents = false;
     bool dumpProfiles = false;
+    bool extractThumbnail = false;
     gNumRepetitions = 1;
     gMaxNumFrames = 0;
     gReproduceBug = -1;
+    gPreferSoftwareCodec = false;
 
     int res;
-    while ((res = getopt(argc, argv, "han:lm:b:p")) >= 0) {
+    while ((res = getopt(argc, argv, "han:lm:b:pts")) >= 0) {
         switch (res) {
             case 'a':
             {
@@ -274,6 +282,18 @@
                 break;
             }
 
+            case 't':
+            {
+                extractThumbnail = true;
+                break;
+            }
+
+            case 's':
+            {
+                gPreferSoftwareCodec = true;
+                break;
+            }
+
             case '?':
             case 'h':
             default:
@@ -288,6 +308,34 @@
     argc -= optind;
     argv += optind;
 
+    if (extractThumbnail) {
+        sp<IServiceManager> sm = defaultServiceManager();
+        sp<IBinder> binder = sm->getService(String16("media.player"));
+        sp<IMediaPlayerService> service =
+            interface_cast<IMediaPlayerService>(binder);
+
+        CHECK(service.get() != NULL);
+
+        sp<IMediaMetadataRetriever> retriever =
+            service->createMetadataRetriever(getpid());
+
+        CHECK(retriever != NULL);
+
+        for (int k = 0; k < argc; ++k) {
+            const char *filename = argv[k];
+
+            CHECK_EQ(retriever->setDataSource(filename), OK);
+            CHECK_EQ(retriever->setMode(METADATA_MODE_FRAME_CAPTURE_ONLY), OK);
+
+            sp<IMemory> mem = retriever->captureFrame();
+
+            printf("captureFrame(%s) => %s\n",
+                   filename, mem != NULL ? "OK" : "FAILED");
+        }
+
+        return 0;
+    }
+
     if (dumpProfiles) {
         sp<IServiceManager> sm = defaultServiceManager();
         sp<IBinder> binder = sm->getService(String16("media.player"));
@@ -389,7 +437,8 @@
             sp<MetaData> meta;
             size_t i;
             for (i = 0; i < numTracks; ++i) {
-                meta = extractor->getTrackMetaData(i);
+                meta = extractor->getTrackMetaData(
+                        i, MediaExtractor::kIncludeExtensiveMetaData);
 
                 const char *mime;
                 meta->findCString(kKeyMIMEType, &mime);
@@ -403,6 +452,12 @@
                 }
             }
 
+            int64_t thumbTimeUs;
+            if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
+                printf("thumbnailTime: %lld us (%.2f secs)\n",
+                       thumbTimeUs, thumbTimeUs / 1E6);
+            }
+
             mediaSource = extractor->getTrack(i);
         }
 
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index d89b877..fe05393 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -186,6 +186,7 @@
     private boolean mRestricted;
     private AccountManager mAccountManager; // protected by mSync
     private DropBoxManager mDropBoxManager = null;
+    private DevicePolicyManager mDevicePolicyManager = null;
 
     private final Object mSync = new Object();
 
@@ -895,6 +896,8 @@
             return getWallpaperManager();
         } else if (DROPBOX_SERVICE.equals(name)) {
             return getDropBoxManager();
+        } else if (DEVICE_POLICY_SERVICE.equals(name)) {
+            return getDevicePolicyManager();
         }
 
         return null;
@@ -1064,6 +1067,16 @@
         return mDropBoxManager;
     }
 
+    private DevicePolicyManager getDevicePolicyManager() {
+        synchronized (mSync) {
+            if (mDevicePolicyManager == null) {
+                mDevicePolicyManager = new DevicePolicyManager(this,
+                        mMainThread.getHandler());
+            }
+        }
+        return mDevicePolicyManager;
+    }
+
     @Override
     public int checkPermission(String permission, int pid, int uid) {
         if (permission == null) {
diff --git a/core/java/android/app/DeviceAdmin.java b/core/java/android/app/DeviceAdmin.java
new file mode 100644
index 0000000..4da3fee
--- /dev/null
+++ b/core/java/android/app/DeviceAdmin.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Base class for implementing a device administration component.  This
+ * class provides a convenience for interpreting the raw intent actions
+ * that are sent by the system.
+ * 
+ * <p>When publishing your DeviceAdmin subclass as a receiver, it must
+ * handle {@link #ACTION_DEVICE_ADMIN_ENABLED} and require the
+ * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission.  A typical
+ * manifest entry would look like:</p>
+ * 
+ * <pre>{@include development/samples/ApiDemos/AndroidManifest.xml
+ *   device_admin_declaration}</pre>
+ *   
+ * <p>The meta-data referenced here provides addition information specific
+ * to the device administrator, as parsed by the {@link DeviceAdminInfo} class.
+ * A typical file would be:</p>
+ * 
+ * <pre>{@include development/samples/ApiDemos/res/xml/sample_device_admin.xml
+ *   meta_data}</pre>
+ */
+public class DeviceAdmin extends BroadcastReceiver {
+    private static String TAG = "DevicePolicy";
+    private static boolean DEBUG = false;
+    private static boolean localLOGV = DEBUG || android.util.Config.LOGV;
+
+    /**
+     * This is the primary action that a device administrator must implement to be
+     * allowed to manage a device.  This will be set to the receiver
+     * when the user enables it for administration.  You will generally
+     * handle this in {@link DeviceAdmin#onEnabled(Context, Intent)}.  To be
+     * supported, the receiver must also require the
+     * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission so
+     * that other applications can not abuse it.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DEVICE_ADMIN_ENABLED
+            = "android.app.action.DEVICE_ADMIN_ENABLED";
+
+    /**
+     * Action sent to a device administrator when the user has disabled
+     * it.  Upon return, the application no longer has access to the
+     * protected device policy manager APIs.  You will generally
+     * handle this in {@link DeviceAdmin#onDisabled(Context, Intent)}.  Note
+     * that this action will be
+     * sent the receiver regardless of whether it is explicitly listed in
+     * its intent filter.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DEVICE_ADMIN_DISABLED
+            = "android.app.action.DEVICE_ADMIN_DISABLED";
+    
+    /**
+     * Action sent to a device administrator when the user has changed the
+     * password of their device.  You can at this point check the characteristics
+     * of the new password with {@link DevicePolicyManager#getActivePasswordMode()
+     * DevicePolicyManager.getActivePasswordMode()} and
+     * {@link DevicePolicyManager#getActiveMinimumPasswordLength()
+     * DevicePolicyManager.getActiveMinimumPasswordLength()}.  You will generally
+     * handle this in {@link DeviceAdmin#onPasswordChanged(Context, Intent)}.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PASSWORD_CHANGED
+            = "android.app.action.ACTION_PASSWORD_CHANGED";
+    
+    /**
+     * Action sent to a device administrator when the user has failed at
+     * attempted to enter the password.  You can at this point check the
+     * number of failed password attempts there have been with
+     * {@link DevicePolicyManager#getCurrentFailedPasswordAttempts()
+     * DevicePolicyManager.getCurrentFailedPasswordAttempts()}.  You will generally
+     * handle this in {@link DeviceAdmin#onPasswordFailed(Context, Intent)}.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PASSWORD_FAILED
+            = "android.app.action.ACTION_PASSWORD_FAILED";
+    
+    /**
+     * Action sent to a device administrator when the user has successfully
+     * entered their password, after failing one or more times.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PASSWORD_SUCCEEDED
+            = "android.app.action.ACTION_PASSWORD_SUCCEEDED";
+    
+    /**
+     * Name under which an DevicePolicy component publishes information
+     * about itself.  This meta-data must reference an XML resource containing
+     * a device-admin tag.  XXX TO DO: describe syntax.
+     */
+    public static final String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
+    
+    private DevicePolicyManager mManager;
+    private ComponentName mWho;
+    
+    /**
+     * Retrieve the DevicePolicyManager interface for this administrator to work
+     * with the system.
+     */
+    public DevicePolicyManager getManager(Context context) {
+        if (mManager != null) {
+            return mManager;
+        }
+        mManager = (DevicePolicyManager)context.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        return mManager;
+    }
+    
+    /**
+     * Retrieve the ComponentName describing who this device administrator is, for
+     * use in {@link DevicePolicyManager} APIs that require the administrator to
+     * identify itself.
+     */
+    public ComponentName getWho(Context context) {
+        if (mWho != null) {
+            return mWho;
+        }
+        mWho = new ComponentName(context, getClass());
+        return mWho;
+    }
+    
+    /**
+     * Called after the administrator is first enabled, as a result of
+     * receiving {@link #ACTION_DEVICE_ADMIN_ENABLED}.  At this point you
+     * can use {@link DevicePolicyManager} to set your desired policies.
+     * @param context The running context as per {@link #onReceive}.
+     * @param intent The received intent as per {@link #onReceive}.
+     */
+    public void onEnabled(Context context, Intent intent) {
+    }
+    
+    /**
+     * Called prior to the administrator being disabled, as a result of
+     * receiving {@link #ACTION_DEVICE_ADMIN_DISABLED}.  Upon return, you
+     * can no longer use the protected parts of the {@link DevicePolicyManager}
+     * API.
+     * @param context The running context as per {@link #onReceive}.
+     * @param intent The received intent as per {@link #onReceive}.
+     */
+    public void onDisabled(Context context, Intent intent) {
+    }
+    
+    /**
+     * Called after the user has changed their password, as a result of
+     * receiving {@link #ACTION_PASSWORD_CHANGED}.  At this point you
+     * can use {@link DevicePolicyManager#getCurrentFailedPasswordAttempts()
+     * DevicePolicyManager.getCurrentFailedPasswordAttempts()}
+     * to retrieve the active password characteristics.
+     * @param context The running context as per {@link #onReceive}.
+     * @param intent The received intent as per {@link #onReceive}.
+     */
+    public void onPasswordChanged(Context context, Intent intent) {
+    }
+    
+    /**
+     * Called after the user has failed at entering their current password, as a result of
+     * receiving {@link #ACTION_PASSWORD_FAILED}.  At this point you
+     * can use {@link DevicePolicyManager} to retrieve the number of failed
+     * password attempts.
+     * @param context The running context as per {@link #onReceive}.
+     * @param intent The received intent as per {@link #onReceive}.
+     */
+    public void onPasswordFailed(Context context, Intent intent) {
+    }
+    
+    /**
+     * Called after the user has succeeded at entering their current password,
+     * as a result of receiving {@link #ACTION_PASSWORD_SUCCEEDED}.  This will
+     * only be received the first time they succeed after having previously
+     * failed.
+     * @param context The running context as per {@link #onReceive}.
+     * @param intent The received intent as per {@link #onReceive}.
+     */
+    public void onPasswordSucceeded(Context context, Intent intent) {
+    }
+    
+    /**
+     * Intercept standard device administrator broadcasts.  Implementations
+     * should not override this method; it is better to implement the
+     * convenience callbacks for each action.
+     */
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        if (ACTION_PASSWORD_CHANGED.equals(action)) {
+            onPasswordChanged(context, intent);
+        } else if (ACTION_PASSWORD_FAILED.equals(action)) {
+            onPasswordFailed(context, intent);
+        } else if (ACTION_PASSWORD_SUCCEEDED.equals(action)) {
+            onPasswordSucceeded(context, intent);
+        } else if (ACTION_DEVICE_ADMIN_ENABLED.equals(action)) {
+            onEnabled(context, intent);
+        } else if (ACTION_DEVICE_ADMIN_DISABLED.equals(action)) {
+            onDisabled(context, intent);
+        }
+    }
+}
diff --git a/core/java/android/app/DeviceAdminInfo.java b/core/java/android/app/DeviceAdminInfo.java
new file mode 100644
index 0000000..eac6e46
--- /dev/null
+++ b/core/java/android/app/DeviceAdminInfo.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Printer;
+import android.util.Xml;
+
+import java.io.IOException;
+
+/**
+ * This class is used to specify meta information of a device administrator
+ * component.
+ */
+public final class DeviceAdminInfo implements Parcelable {
+    static final String TAG = "DeviceAdminInfo";
+    
+    /**
+     * The BroadcastReceiver that implements this device admin component.
+     */
+    final ResolveInfo mReceiver;
+    
+    /**
+     * Constructor.
+     * 
+     * @param context The Context in which we are parsing the device admin.
+     * @param receiver The ResolveInfo returned from the package manager about
+     * this device admin's component.
+     */
+    public DeviceAdminInfo(Context context, ResolveInfo receiver)
+            throws XmlPullParserException, IOException {
+        mReceiver = receiver;
+        ActivityInfo ai = receiver.activityInfo;
+        
+        PackageManager pm = context.getPackageManager();
+        
+        XmlResourceParser parser = null;
+        try {
+            parser = ai.loadXmlMetaData(pm, DeviceAdmin.DEVICE_ADMIN_META_DATA);
+            if (parser == null) {
+                throw new XmlPullParserException("No "
+                        + DeviceAdmin.DEVICE_ADMIN_META_DATA + " meta-data");
+            }
+        
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+            
+            int type;
+            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+            }
+            
+            String nodeName = parser.getName();
+            if (!"device-admin".equals(nodeName)) {
+                throw new XmlPullParserException(
+                        "Meta-data does not start with device-admin tag");
+            }
+            
+            TypedArray sa = context.getResources().obtainAttributes(attrs,
+                    com.android.internal.R.styleable.Wallpaper);
+
+            sa.recycle();
+        } finally {
+            if (parser != null) parser.close();
+        }
+    }
+
+    DeviceAdminInfo(Parcel source) {
+        mReceiver = ResolveInfo.CREATOR.createFromParcel(source);
+    }
+    
+    /**
+     * Return the .apk package that implements this device admin.
+     */
+    public String getPackageName() {
+        return mReceiver.activityInfo.packageName;
+    }
+    
+    /**
+     * Return the class name of the receiver component that implements
+     * this device admin.
+     */
+    public String getReceiverName() {
+        return mReceiver.activityInfo.name;
+    }
+
+    /**
+     * Return the raw information about the receiver implementing this
+     * device admin.  Do not modify the returned object.
+     */
+    public ActivityInfo getActivityInfo() {
+        return mReceiver.activityInfo;
+    }
+
+    /**
+     * Return the component of the receiver that implements this device admin.
+     */
+    public ComponentName getComponent() {
+        return new ComponentName(mReceiver.activityInfo.packageName,
+                mReceiver.activityInfo.name);
+    }
+    
+    /**
+     * Load the user-displayed label for this device admin.
+     * 
+     * @param pm Supply a PackageManager used to load the device admin's
+     * resources.
+     */
+    public CharSequence loadLabel(PackageManager pm) {
+        return mReceiver.loadLabel(pm);
+    }
+    
+    /**
+     * Load the user-displayed icon for this device admin.
+     * 
+     * @param pm Supply a PackageManager used to load the device admin's
+     * resources.
+     */
+    public Drawable loadIcon(PackageManager pm) {
+        return mReceiver.loadIcon(pm);
+    }
+    
+    public void dump(Printer pw, String prefix) {
+        pw.println(prefix + "Receiver:");
+        mReceiver.dump(pw, prefix + "  ");
+    }
+    
+    @Override
+    public String toString() {
+        return "DeviceAdminInfo{" + mReceiver.activityInfo.name + "}";
+    }
+
+    /**
+     * Used to package this object into a {@link Parcel}.
+     * 
+     * @param dest The {@link Parcel} to be written.
+     * @param flags The flags used for parceling.
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        mReceiver.writeToParcel(dest, flags);
+    }
+
+    /**
+     * Used to make this class parcelable.
+     */
+    public static final Parcelable.Creator<DeviceAdminInfo> CREATOR =
+            new Parcelable.Creator<DeviceAdminInfo>() {
+        public DeviceAdminInfo createFromParcel(Parcel source) {
+            return new DeviceAdminInfo(source);
+        }
+
+        public DeviceAdminInfo[] newArray(int size) {
+            return new DeviceAdminInfo[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/app/DevicePolicyManager.java b/core/java/android/app/DevicePolicyManager.java
new file mode 100644
index 0000000..4fdfe0a
--- /dev/null
+++ b/core/java/android/app/DevicePolicyManager.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Public interface for managing policies enforced on a device.  Most clients
+ * of this class must have published a {@link DeviceAdmin} that the user
+ * has currently enabled.
+ */
+public class DevicePolicyManager {
+    private static String TAG = "DevicePolicyManager";
+    private static boolean DEBUG = false;
+    private static boolean localLOGV = DEBUG || android.util.Config.LOGV;
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final IDevicePolicyManager mService;
+
+    /*package*/ DevicePolicyManager(Context context, Handler handler) {
+        mContext = context;
+        mHandler = handler;
+        mService = IDevicePolicyManager.Stub.asInterface(
+                ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+    }
+
+    /**
+     * Activity action: ask the user to add a new device administrator to the system.
+     * The desired policy is the ComponentName of the policy in the
+     * {@link #EXTRA_DEVICE_ADMIN} extra field.  This will invoke a UI to
+     * bring the user through adding the device administrator to the system (or
+     * allowing them to reject it).
+     * 
+     * <p>Note: the current platform can only have one device administrator
+     * active at a time.  If you make this request while there is already
+     * an active administrator, this new request will be canceled automatically.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_ADD_DEVICE_ADMIN
+            = "android.app.action.ADD_DEVICE_ADMIN";
+    
+    /**
+     * The ComponentName of the administrator component.
+     *
+     * @see #ACTION_ADD_DEVICE_ADMIN
+     */
+    public static final String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
+    
+    /**
+     * Activity action: have the user enter a new password.  This activity
+     * should be launched after using {@link #setPasswordMode(ComponentName, int)}
+     * or {@link #setMinimumPasswordLength(ComponentName, int)} to have the
+     * user enter a new password that meets the current requirements.  If the
+     * current password is sufficient, the activity will exit immediately without
+     * being displayed to the user.  Upon receiving a result from this activity,
+     * you can check the new password characteristics to see if they are
+     * sufficient.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SET_NEW_PASSWORD
+            = "android.app.action.SET_NEW_PASSWORD";
+    
+    /**
+     * Return true if the given administrator component is currently
+     * active (enabled) in the system.
+     */
+    public boolean isAdminActive(ComponentName who) {
+        if (mService != null) {
+            try {
+                return who.equals(mService.getActiveAdmin());
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Remove a current administration component.  This can only be called
+     * by the application that owns the administration component; if you
+     * try to remove someone else's component, a security exception will be
+     * thrown.
+     */
+    public void removeActiveAdmin(ComponentName who) {
+        if (mService != null) {
+            try {
+                mService.removeActiveAdmin(who);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+    
+    /**
+     * Constant for {@link #setPasswordMode}: the policy has no requirements
+     * for the password.
+     */
+    public static final int PASSWORD_MODE_UNSPECIFIED = 0;
+    
+    /**
+     * Constant for {@link #setPasswordMode}: the user must have at least a
+     * numeric password.
+     */
+    public static final int PASSWORD_MODE_NUMERIC = 1000;
+    
+    /**
+     * Constant for {@link #setPasswordMode}: the user must have at least an
+     * alphanumeric password.
+     */
+    public static final int PASSWORD_MODE_ALPHANUMERIC = 2000;
+    
+    /**
+     * Called by an application that is administering the device to set the
+     * password restrictions it is imposing.  After setting this, the user
+     * will not be able to enter a new password that is not at least as
+     * restrictive as what has been set.  Note that the current password
+     * will remain until the user has set a new one, so the change does not
+     * take place immediately.  To prompt the user for a new password, use
+     * {@link #ACTION_SET_NEW_PASSWORD} after setting this value.
+     * 
+     * @param admin Which {@link DeviceAdmin} this request is associated with.
+     * @param mode The new desired mode.  One of
+     * {@link #PASSWORD_MODE_UNSPECIFIED}, {@link #PASSWORD_MODE_NUMERIC},
+     * or {@link #PASSWORD_MODE_ALPHANUMERIC}.
+     */
+    public void setPasswordMode(ComponentName admin, int mode) {
+        if (mService != null) {
+            try {
+                mService.setPasswordMode(admin, mode);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+    
+    /**
+     * Retrieve the current password mode that is in effect due to all
+     * device admins.
+     */
+    public int getPasswordMode() {
+        if (mService != null) {
+            try {
+                return mService.getPasswordMode();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return PASSWORD_MODE_UNSPECIFIED;
+    }
+    
+    /**
+     * Retrieve the password mode associated with the last password the
+     * user selected.
+     */
+    public int getActivePasswordMode() {
+        if (mService != null) {
+            try {
+                return mService.getActivePasswordMode();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return PASSWORD_MODE_UNSPECIFIED;
+    }
+    
+    /**
+     * Called by an application that is administering the device to set the
+     * minimum allowed password length.  After setting this, the user
+     * will not be able to enter a new password that is not at least as
+     * restrictive as what has been set.  Note that the current password
+     * will remain until the user has set a new one, so the change does not
+     * take place immediately.  To prompt the user for a new password, use
+     * {@link #ACTION_SET_NEW_PASSWORD} after setting this value.  This
+     * constraint is only imposed if the administrator has also requested either
+     * {@link #PASSWORD_MODE_NUMERIC} or {@link #PASSWORD_MODE_ALPHANUMERIC}
+     * with {@link #setPasswordMode}.
+     * 
+     * @param admin Which {@link DeviceAdmin} this request is associated with.
+     * @param length The new desired minimum password length.  A value of 0
+     * means there is no restriction.
+     */
+    public void setMinimumPasswordLength(ComponentName admin, int length) {
+        if (mService != null) {
+            try {
+                mService.setMinimumPasswordLength(admin, length);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+    
+    /**
+     * Retrieve the current minimum password length that is in effect due to all
+     * device admins.
+     */
+    public int getMinimumPasswordLength() {
+        if (mService != null) {
+            try {
+                return mService.getMinimumPasswordLength();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return 0;
+    }
+    
+    /**
+     * Retrieve the password length associated with the last password the
+     * user selected.
+     */
+    public int getActiveMinimumPasswordLength() {
+        if (mService != null) {
+            try {
+                return mService.getActiveMinimumPasswordLength();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return 0;
+    }
+    
+    /**
+     * Retrieve the number of times the user has failed at entering a
+     * password since that last successful password entry.
+     */
+    public int getCurrentFailedPasswordAttempts() {
+        if (mService != null) {
+            try {
+                return mService.getCurrentFailedPasswordAttempts();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return -1;
+    }
+    
+    /**
+     * Called by an application that is administering the device to set the
+     * maximum time for user activity until the device will lock.  This limits
+     * the length that the user can set.  It takes effect immediately.
+     * 
+     * @param admin Which {@link DeviceAdmin} this request is associated with.
+     * @param timeMs The new desired maximum time to lock in milliseconds.
+     * A value of 0 means there is no restriction.
+     */
+    public void setMaximumTimeToLock(ComponentName admin, long timeMs) {
+        if (mService != null) {
+            try {
+                mService.setMaximumTimeToLock(admin, timeMs);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+    
+    /**
+     * Retrieve the current maximum time to lock that is in effect due to all
+     * device admins.  Returns 0 if no maximum is set.
+     */
+    public long getMaximumTimeToLock() {
+        if (mService != null) {
+            try {
+                return mService.getMaximumTimeToLock();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return 0;
+    }
+    
+    /**
+     * Constant for {@link #wipeData}: perform a low-level format of data
+     * storage.
+     */
+    public static final int WIPE_LOW_LEVEL_FORMAT = 0x0001;
+    
+    /**
+     * Constant for {@link #wipeData}: also wipe any external storage.
+     */
+    public static final int WIPE_EXTERNAL_STORAGE = 0x0002;
+    
+    /**
+     * Ask the user date be wiped.  This will cause the device to reboot,
+     * erasing all user data while next booting up.
+     * 
+     * @param flags Bit mask of additional options: currently
+     * {@link #WIPE_LOW_LEVEL_FORMAT} and {@link #WIPE_EXTERNAL_STORAGE}.
+     */
+    public void wipeData(int flags) {
+        if (mService != null) {
+            try {
+                mService.wipeData(flags);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+    
+    /**
+     * @hide
+     */
+    public void setActiveAdmin(ComponentName policyReceiver) {
+        if (mService != null) {
+            try {
+                mService.setActiveAdmin(policyReceiver);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+    
+    /**
+     * @hide
+     */
+    public ComponentName getActiveAdmin() {
+        if (mService != null) {
+            try {
+                return mService.getActiveAdmin();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * @hide
+     */
+    public DeviceAdminInfo getActiveAdminInfo() {
+        ComponentName cn = getActiveAdmin();
+        if (cn == null) {
+            return null;
+        }
+        
+        ActivityInfo ai;
+        try {
+            ai = mContext.getPackageManager().getReceiverInfo(cn,
+                    PackageManager.GET_META_DATA);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "Unable to retrieve device policy " + cn, e);
+            return null;
+        }
+        
+        ResolveInfo ri = new ResolveInfo();
+        ri.activityInfo = ai;
+        
+        try {
+            return new DeviceAdminInfo(mContext, ri);
+        } catch (XmlPullParserException e) {
+            Log.w(TAG, "Unable to parse device policy " + cn, e);
+            return null;
+        } catch (IOException e) {
+            Log.w(TAG, "Unable to parse device policy " + cn, e);
+            return null;
+        }
+    }
+    
+    /**
+     * @hide
+     */
+    public void setActivePasswordState(int mode, int length) {
+        if (mService != null) {
+            try {
+                mService.setActivePasswordState(mode, length);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+    
+    /**
+     * @hide
+     */
+    public void reportFailedPasswordAttempt() {
+        if (mService != null) {
+            try {
+                mService.reportFailedPasswordAttempt();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+    
+    /**
+     * @hide
+     */
+    public void reportSuccessfulPasswordAttempt() {
+        if (mService != null) {
+            try {
+                mService.reportSuccessfulPasswordAttempt();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/IDevicePolicyManager.aidl b/core/java/android/app/IDevicePolicyManager.aidl
new file mode 100644
index 0000000..f62647f
--- /dev/null
+++ b/core/java/android/app/IDevicePolicyManager.aidl
@@ -0,0 +1,49 @@
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.app;
+
+import android.content.ComponentName;
+
+/**
+ * Internal IPC interface to the device policy service.
+ * {@hide}
+ */
+interface IDevicePolicyManager {
+    void setPasswordMode(in ComponentName who, int mode);
+    int getPasswordMode();
+    int getActivePasswordMode();
+    
+    void setMinimumPasswordLength(in ComponentName who, int length);
+    int getMinimumPasswordLength();
+    int getActiveMinimumPasswordLength();
+    
+    int getCurrentFailedPasswordAttempts();
+    
+    void setMaximumTimeToLock(in ComponentName who, long timeMs);
+    long getMaximumTimeToLock();
+    
+    void wipeData(int flags);
+    
+    void setActiveAdmin(in ComponentName policyReceiver);
+    ComponentName getActiveAdmin();
+    void removeActiveAdmin(in ComponentName policyReceiver);
+    
+    void setActivePasswordState(int mode, int length);
+    void reportFailedPasswordAttempt();
+    void reportSuccessfulPasswordAttempt();
+}
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 981145b..d25d670 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1838,8 +1838,10 @@
      *
      * TODO: Doing this every time we start global search is inefficient. Will fix that once
      * we have settled on the right mechanism for finding the global search activity.
+     *
+     * @hide
      */
-    private ComponentName getGlobalSearchActivity() {
+    public ComponentName getGlobalSearchActivity() {
         Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH);
         PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> activities =
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 1034fab..1612ac9 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.app;
 
 import org.xmlpull.v1.XmlPullParser;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0fafe5d..0b83f03 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1132,6 +1132,7 @@
      * you're running long tasks.
      */
     public static final String POWER_SERVICE = "power";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a
      * {@link android.view.WindowManager} for accessing the system's window
@@ -1141,6 +1142,7 @@
      * @see android.view.WindowManager
      */
     public static final String WINDOW_SERVICE = "window";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a
      * {@link android.view.LayoutInflater} for inflating layout resources in this
@@ -1150,6 +1152,7 @@
      * @see android.view.LayoutInflater
      */
     public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a
      * {@link android.accounts.AccountManager} for receiving intents at a
@@ -1159,6 +1162,7 @@
      * @see android.accounts.AccountManager
      */
     public static final String ACCOUNT_SERVICE = "account";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a
      * {@link android.app.ActivityManager} for interacting with the global
@@ -1168,6 +1172,7 @@
      * @see android.app.ActivityManager
      */
     public static final String ACTIVITY_SERVICE = "activity";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a
      * {@link android.app.AlarmManager} for receiving intents at a
@@ -1177,6 +1182,7 @@
      * @see android.app.AlarmManager
      */
     public static final String ALARM_SERVICE = "alarm";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a
      * {@link android.app.NotificationManager} for informing the user of
@@ -1186,6 +1192,7 @@
      * @see android.app.NotificationManager
      */
     public static final String NOTIFICATION_SERVICE = "notification";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a
      * {@link android.view.accessibility.AccessibilityManager} for giving the user
@@ -1195,6 +1202,7 @@
      * @see android.view.accessibility.AccessibilityManager
      */
     public static final String ACCESSIBILITY_SERVICE = "accessibility";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a
      * {@link android.app.NotificationManager} for controlling keyguard.
@@ -1203,6 +1211,7 @@
      * @see android.app.KeyguardManager
      */
     public static final String KEYGUARD_SERVICE = "keyguard";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
      * android.location.LocationManager} for controlling location
@@ -1212,6 +1221,7 @@
      * @see android.location.LocationManager
      */
     public static final String LOCATION_SERVICE = "location";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
      * android.app.SearchManager} for handling searches.
@@ -1220,6 +1230,7 @@
      * @see android.app.SearchManager
      */
     public static final String SEARCH_SERVICE = "search";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
      * android.hardware.SensorManager} for accessing sensors.
@@ -1228,6 +1239,7 @@
      * @see android.hardware.SensorManager
      */
     public static final String SENSOR_SERVICE = "sensor";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a
      * com.android.server.WallpaperService for accessing wallpapers.
@@ -1235,6 +1247,7 @@
      * @see #getSystemService
      */
     public static final String WALLPAPER_SERVICE = "wallpaper";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
      * android.os.Vibrator} for interacting with the vibration hardware.
@@ -1243,6 +1256,7 @@
      * @see android.os.Vibrator
      */
     public static final String VIBRATOR_SERVICE = "vibrator";
+    
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
      * android.app.StatusBarManager} for interacting with the status bar.
@@ -1340,6 +1354,15 @@
     public static final String DROPBOX_SERVICE = "dropbox";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a 
+     * {@link android.app.DevicePolicyManager} for working with global
+     * device policy management.
+     *
+     * @see #getSystemService
+     */
+    public static final String DEVICE_POLICY_SERVICE = "device_policy";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/gesture/Gesture.java b/core/java/android/gesture/Gesture.java
index 62330e1..d71344c 100755
--- a/core/java/android/gesture/Gesture.java
+++ b/core/java/android/gesture/Gesture.java
@@ -34,7 +34,9 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * A gesture can have a single or multiple strokes
+ * A gesture is a hand-drawn shape on a touch screen. It can have one or multiple strokes.
+ * Each stroke is a sequence of timed points. A user-defined gesture can be recognized by 
+ * a GestureLibrary and a built-in alphabet gesture can be recognized by a LetterRecognizer. 
  */
 
 public class Gesture implements Parcelable {
@@ -58,6 +60,19 @@
         mGestureID = GESTURE_ID_BASE + sGestureCount.incrementAndGet();
     }
 
+    @Override
+    public Object clone() {
+        Gesture gesture = new Gesture();
+        gesture.mBoundingBox.set(mBoundingBox.left, mBoundingBox.top, 
+                                        mBoundingBox.right, mBoundingBox.bottom);
+        final int count = mStrokes.size();
+        for (int i = 0; i < count; i++) {
+            GestureStroke stroke = mStrokes.get(i);
+            gesture.mStrokes.add((GestureStroke)stroke.clone());
+        }
+        return gesture;
+    }
+    
     /**
      * @return all the strokes of the gesture
      */
@@ -73,7 +88,7 @@
     }
 
     /**
-     * Add a stroke to the gesture
+     * Adds a stroke to the gesture.
      * 
      * @param stroke
      */
@@ -83,8 +98,8 @@
     }
 
     /**
-     * Get the total length of the gesture. When there are multiple strokes in
-     * the gesture, this returns the sum of the lengths of all the strokes
+     * Calculates the total length of the gesture. When there are multiple strokes in
+     * the gesture, this returns the sum of the lengths of all the strokes.
      * 
      * @return the length of the gesture
      */
@@ -142,7 +157,7 @@
     }
 
     /**
-     * Set the id of the gesture
+     * Sets the id of the gesture.
      * 
      * @param id
      */
@@ -158,7 +173,7 @@
     }
 
     /**
-     * Create a bitmap of the gesture with a transparent background
+     * Creates a bitmap of the gesture with a transparent background.
      * 
      * @param width width of the target bitmap
      * @param height height of the target bitmap
@@ -194,7 +209,7 @@
     }
 
     /**
-     * Create a bitmap of the gesture with a transparent background
+     * Creates a bitmap of the gesture with a transparent background.
      * 
      * @param width
      * @param height
diff --git a/core/java/android/gesture/GesturePoint.java b/core/java/android/gesture/GesturePoint.java
index 3698011..4cb7707 100644
--- a/core/java/android/gesture/GesturePoint.java
+++ b/core/java/android/gesture/GesturePoint.java
@@ -20,7 +20,7 @@
 import java.io.IOException;
 
 /**
- * A timed point of a gesture stroke
+ * A timed point of a gesture stroke. Multiple points form a stroke.
  */
 
 public class GesturePoint {
@@ -43,4 +43,9 @@
         final long timeStamp = in.readLong();
         return new GesturePoint(x, y, timeStamp);
     }
+    
+    @Override
+    public Object clone() {
+        return new GesturePoint(x, y, timestamp);
+    }
 }
diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java
index 598eb85..68dc5a6 100644
--- a/core/java/android/gesture/GestureStroke.java
+++ b/core/java/android/gesture/GestureStroke.java
@@ -27,7 +27,8 @@
 import java.util.ArrayList;
 
 /**
- * A gesture stroke started on a touch down and ended on a touch up.
+ * A gesture stroke started on a touch down and ended on a touch up. A stroke
+ * consists of a sequence of timed points. One or multiple strokes form a gesture.
  */
 public class GestureStroke {
     static final float TOUCH_TOLERANCE = 8;
@@ -41,7 +42,7 @@
     private Path mCachedPath;
 
     /**
-     * Construct a gesture stroke from a list of gesture points
+     * A constructor that constructs a gesture stroke from a list of gesture points.
      * 
      * @param points
      */
@@ -82,7 +83,22 @@
     }
 
     /**
-     * Draw the gesture with a given canvas and paint
+     * A faster constructor specially for cloning a stroke.
+     */
+    private GestureStroke(RectF bbx, float len, float[] pts, long[] times) {
+        boundingBox = new RectF(bbx.left, bbx.top, bbx.right, bbx.bottom);
+        length = len;
+        points = pts.clone();
+        timestamps = times.clone();
+    }
+    
+    @Override
+    public Object clone() {
+        return new GestureStroke(boundingBox, length, points, timestamps);
+    }
+    
+    /**
+     * Draws the stroke with a given canvas and paint.
      * 
      * @param canvas
      */
@@ -134,7 +150,7 @@
     }
 
     /**
-     * Convert the stroke to a Path based on the number of points
+     * Converts the stroke to a Path of a given number of points.
      * 
      * @param width the width of the bounding box of the target path
      * @param height the height of the bounding box of the target path
@@ -213,14 +229,15 @@
     }    
 
     /**
-     * Invalidate the cached path that is used to render the stroke
+     * Invalidates the cached path that is used to render the stroke.
      */
     public void clearPath() {
         if (mCachedPath != null) mCachedPath.rewind();
     }
     
     /**
-     * Compute an oriented bounding box of the stroke
+     * Computes an oriented bounding box of the stroke.
+     * 
      * @return OrientedBoundingBox
      */
     public OrientedBoundingBox computeOrientedBoundingBox() {
diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java
index b532365..dfe1d00 100755
--- a/core/java/android/gesture/GestureUtilities.java
+++ b/core/java/android/gesture/GestureUtilities.java
@@ -48,6 +48,10 @@
             }
         }
     }
+    
+    static float[] spatialSampling(Gesture gesture, int sampleMatrixDimension) {
+        return spatialSampling(gesture, sampleMatrixDimension, false);
+    }
 
     static float[] spatialSampling(Gesture gesture, int sampleMatrixDimension, 
             boolean uniformScaling) {
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl
index e73569a..c0c2d03 100644
--- a/core/java/android/os/IMountService.aidl
+++ b/core/java/android/os/IMountService.aidl
@@ -94,6 +94,11 @@
     String mountSecureContainer(String id, String key, int ownerUid);
 
     /*
+     * Unount a secure container.
+     */
+    void unmountSecureContainer(String id);
+
+    /*
      * Returns the filesystem path of a mounted secure container.
      */
     String getSecureContainerPath(String id);
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
new file mode 100644
index 0000000..bd6cabb
--- /dev/null
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -0,0 +1,105 @@
+/* //device/java/android/android/os/INetworkManagementService.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+/**
+ * @hide
+ */
+interface INetworkManagementService
+{
+    /**
+     ** GENERAL
+     **/
+
+    /**
+     * Returns a list of currently known network interfaces
+     */
+    String[] listInterfaces();
+
+    /**
+     * Shuts down the service
+     */
+    void shutdown();
+
+    /**
+     ** TETHERING RELATED
+     **/
+
+
+    /**
+     * Returns true if IP forwarding is enabled
+     */
+    boolean getIpForwardingEnabled();
+
+    /**
+     * Enables/Disables IP Forwarding
+     */
+    void setIpForwardingEnabled(boolean enabled);
+
+    /**
+     * Start tethering services with the specified dhcp server range
+     */
+    void startTethering(String dhcpRangeStart, String dhcpRangeEnd);
+
+    /**
+     * Stop currently running tethering services
+     */
+    void stopTethering();
+
+    /**
+     * Returns true if tethering services are started
+     */
+    boolean isTetheringStarted();
+
+    /**
+     * Tethers the specified interface
+     */
+    void tetherInterface(String iface);
+
+    /**
+     * Untethers the specified interface
+     */
+    void untetherInterface(String iface);
+
+    /**
+     * Returns a list of currently tethered interfaces
+     */
+    String[] listTetheredInterfaces();
+
+    /**
+     * Sets the list of DNS forwarders (in order of priority)
+     */
+    void setDnsForwarders(in String[] dns);
+
+    /**
+     * Returns the list of DNS fowarders (in order of priority)
+     */
+    String[] getDnsForwarders();
+
+    /**
+     *  Enables Network Address Translation between two interfaces.
+     *  The address and netmask of the external interface is used for
+     *  the NAT'ed network.
+     */
+    void enableNat(String internalInterface, String externalInterface);
+
+    /**
+     *  Disables Network Address Translation between two interfaces.
+     */
+    void disableNat(String internalInterface, String externalInterface);
+}
diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java
index c2928cb..fe8cfb0 100644
--- a/core/java/android/pim/vcard/VCardParser_V21.java
+++ b/core/java/android/pim/vcard/VCardParser_V21.java
@@ -522,7 +522,7 @@
     protected void handleParams(String params) throws VCardException {
         String[] strArray = params.split("=", 2);
         if (strArray.length == 2) {
-            String paramName = strArray[0].trim();
+            final String paramName = strArray[0].trim().toUpperCase();
             String paramValue = strArray[1].trim();
             if (paramName.equals("TYPE")) {
                 handleType(paramValue);
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 3774156..eb863ef 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -19,41 +19,45 @@
 import android.net.Uri;
 
 /**
- * Exposes constants used to interact with the download manager's
- * content provider.
- * The constants URI ... STATUS are the names of columns in the downloads table.
+ * The Download Manager
  *
- * @hide
+ * @pending
  */
-// For 1.0 the download manager can't deal with abuse from untrusted apps, so
-// this API is hidden.
 public final class Downloads {
+    /**
+     * @hide
+     */
     private Downloads() {}
 
     /**
      * The permission to access the download manager
+     * @hide
      */
     public static final String PERMISSION_ACCESS = "android.permission.ACCESS_DOWNLOAD_MANAGER";
 
     /**
      * The permission to access the download manager's advanced functions
+     * @hide
      */
     public static final String PERMISSION_ACCESS_ADVANCED =
             "android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED";
 
     /**
      * The permission to directly access the download manager's cache directory
+     * @hide
      */
     public static final String PERMISSION_CACHE = "android.permission.ACCESS_CACHE_FILESYSTEM";
 
     /**
      * The permission to send broadcasts on download completion
+     * @hide
      */
     public static final String PERMISSION_SEND_INTENTS =
             "android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS";
 
     /**
      * The content:// URI for the data table in the provider
+     * @hide
      */
     public static final Uri CONTENT_URI =
         Uri.parse("content://downloads/download");
@@ -62,6 +66,7 @@
      * Broadcast Action: this is sent by the download manager to the app
      * that had initiated a download when that download completes. The
      * download's content: uri is specified in the intent's data.
+     * @hide
      */
     public static final String ACTION_DOWNLOAD_COMPLETED =
             "android.intent.action.DOWNLOAD_COMPLETED";
@@ -75,6 +80,7 @@
      * multiple downloads.
      * Note: this is not currently sent for downloads that have completed
      * successfully.
+     * @hide
      */
     public static final String ACTION_NOTIFICATION_CLICKED =
             "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
@@ -83,6 +89,7 @@
      * The name of the column containing the URI of the data being downloaded.
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read</P>
+     * @hide
      */
     public static final String COLUMN_URI = "uri";
 
@@ -90,6 +97,7 @@
      * The name of the column containing application-specific data.
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read/Write</P>
+     * @hide
      */
     public static final String COLUMN_APP_DATA = "entity";
 
@@ -103,6 +111,7 @@
      * whether a download fully completed).
      * <P>Type: BOOLEAN</P>
      * <P>Owner can Init</P>
+     * @hide
      */
     public static final String COLUMN_NO_INTEGRITY = "no_integrity";
 
@@ -112,6 +121,7 @@
      * to use this filename, or a variation, as the actual name for the file.
      * <P>Type: TEXT</P>
      * <P>Owner can Init</P>
+     * @hide
      */
     public static final String COLUMN_FILE_NAME_HINT = "hint";
 
@@ -120,6 +130,7 @@
      * was actually stored.
      * <P>Type: TEXT</P>
      * <P>Owner can Read</P>
+     * @hide
      */
     public static final String _DATA = "_data";
 
@@ -127,6 +138,7 @@
      * The name of the column containing the MIME type of the downloaded data.
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read</P>
+     * @hide
      */
     public static final String COLUMN_MIME_TYPE = "mimetype";
 
@@ -135,6 +147,7 @@
      * of the download. See the DESTINATION_* constants for a list of legal values.
      * <P>Type: INTEGER</P>
      * <P>Owner can Init</P>
+     * @hide
      */
     public static final String COLUMN_DESTINATION = "destination";
 
@@ -144,6 +157,7 @@
      * a list of legal values.
      * <P>Type: INTEGER</P>
      * <P>Owner can Init/Read/Write</P>
+     * @hide
      */
     public static final String COLUMN_VISIBILITY = "visibility";
 
@@ -153,6 +167,7 @@
      * the CONTROL_* constants for a list of legal values.
      * <P>Type: INTEGER</P>
      * <P>Owner can Read</P>
+     * @hide
      */
     public static final String COLUMN_CONTROL = "control";
 
@@ -162,6 +177,7 @@
      * the STATUS_* constants for a list of legal values.
      * <P>Type: INTEGER</P>
      * <P>Owner can Read</P>
+     * @hide
      */
     public static final String COLUMN_STATUS = "status";
 
@@ -171,6 +187,7 @@
      * value.
      * <P>Type: BIGINT</P>
      * <P>Owner can Read</P>
+     * @hide
      */
     public static final String COLUMN_LAST_MODIFICATION = "lastmod";
 
@@ -180,6 +197,7 @@
      * notifications to a component in this package when the download completes.
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read</P>
+     * @hide
      */
     public static final String COLUMN_NOTIFICATION_PACKAGE = "notificationpackage";
 
@@ -190,6 +208,7 @@
      * Intent.setClassName(String,String).
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read</P>
+     * @hide
      */
     public static final String COLUMN_NOTIFICATION_CLASS = "notificationclass";
 
@@ -198,6 +217,7 @@
      * is sent to the specified class and package when a download has finished.
      * <P>Type: TEXT</P>
      * <P>Owner can Init</P>
+     * @hide
      */
     public static final String COLUMN_NOTIFICATION_EXTRAS = "notificationextras";
 
@@ -207,6 +227,7 @@
      * header that gets sent with the request.
      * <P>Type: TEXT</P>
      * <P>Owner can Init</P>
+     * @hide
      */
     public static final String COLUMN_COOKIE_DATA = "cookiedata";
 
@@ -215,6 +236,7 @@
      * application wants the download manager to use for this download.
      * <P>Type: TEXT</P>
      * <P>Owner can Init</P>
+     * @hide
      */
     public static final String COLUMN_USER_AGENT = "useragent";
 
@@ -223,6 +245,7 @@
      * application wants the download manager to use for this download.
      * <P>Type: TEXT</P>
      * <P>Owner can Init</P>
+     * @hide
      */
     public static final String COLUMN_REFERER = "referer";
 
@@ -231,6 +254,7 @@
      * downloaded.
      * <P>Type: INTEGER</P>
      * <P>Owner can Read</P>
+     * @hide
      */
     public static final String COLUMN_TOTAL_BYTES = "total_bytes";
 
@@ -239,6 +263,7 @@
      * has been downloaded so far.
      * <P>Type: INTEGER</P>
      * <P>Owner can Read</P>
+     * @hide
      */
     public static final String COLUMN_CURRENT_BYTES = "current_bytes";
 
@@ -251,6 +276,7 @@
      * android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED.
      * <P>Type: INTEGER</P>
      * <P>Owner can Init</P>
+     * @hide
      */
     public static final String COLUMN_OTHER_UID = "otheruid";
 
@@ -260,6 +286,7 @@
      * list of downloads.
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read/Write</P>
+     * @hide
      */
     public static final String COLUMN_TITLE = "title";
 
@@ -269,6 +296,7 @@
      * user in the list of downloads.
      * <P>Type: TEXT</P>
      * <P>Owner can Init/Read/Write</P>
+     * @hide
      */
     public static final String COLUMN_DESCRIPTION = "description";
 
@@ -284,6 +312,7 @@
      * Downloads to the external destination only write files for which
      * there is a registered handler. The resulting files are accessible
      * by filename to all applications.
+     * @hide
      */
     public static final int DESTINATION_EXTERNAL = 0;
 
@@ -295,6 +324,7 @@
      * application can access the file (indirectly through a content
      * provider). This requires the
      * android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED permission.
+     * @hide
      */
     public static final int DESTINATION_CACHE_PARTITION = 1;
 
@@ -304,6 +334,7 @@
      * for private files (similar to CACHE_PARTITION) that aren't deleted
      * immediately after they are used, and are kept around by the download
      * manager as long as space is available.
+     * @hide
      */
     public static final int DESTINATION_CACHE_PARTITION_PURGEABLE = 2;
 
@@ -311,16 +342,19 @@
      * This download will be saved to the download manager's private
      * partition, as with DESTINATION_CACHE_PARTITION, but the download
      * will not proceed if the user is on a roaming data connection.
+     * @hide
      */
     public static final int DESTINATION_CACHE_PARTITION_NOROAMING = 3;
 
     /**
      * This download is allowed to run.
+     * @hide
      */
     public static final int CONTROL_RUN = 0;
 
     /**
      * This download must pause at the first opportunity.
+     * @hide
      */
     public static final int CONTROL_PAUSED = 1;
 
@@ -337,6 +371,7 @@
 
     /**
      * Returns whether the status is informational (i.e. 1xx).
+     * @hide
      */
     public static boolean isStatusInformational(int status) {
         return (status >= 100 && status < 200);
@@ -346,6 +381,7 @@
      * Returns whether the download is suspended. (i.e. whether the download
      * won't complete without some action from outside the download
      * manager).
+     * @hide
      */
     public static boolean isStatusSuspended(int status) {
         return (status == STATUS_PENDING_PAUSED || status == STATUS_RUNNING_PAUSED);
@@ -353,6 +389,7 @@
 
     /**
      * Returns whether the status is a success (i.e. 2xx).
+     * @hide
      */
     public static boolean isStatusSuccess(int status) {
         return (status >= 200 && status < 300);
@@ -360,6 +397,7 @@
 
     /**
      * Returns whether the status is an error (i.e. 4xx or 5xx).
+     * @hide
      */
     public static boolean isStatusError(int status) {
         return (status >= 400 && status < 600);
@@ -367,6 +405,7 @@
 
     /**
      * Returns whether the status is a client error (i.e. 4xx).
+     * @hide
      */
     public static boolean isStatusClientError(int status) {
         return (status >= 400 && status < 500);
@@ -374,6 +413,7 @@
 
     /**
      * Returns whether the status is a server error (i.e. 5xx).
+     * @hide
      */
     public static boolean isStatusServerError(int status) {
         return (status >= 500 && status < 600);
@@ -382,6 +422,7 @@
     /**
      * Returns whether the download has completed (either with success or
      * error).
+     * @hide
      */
     public static boolean isStatusCompleted(int status) {
         return (status >= 200 && status < 300) || (status >= 400 && status < 600);
@@ -389,21 +430,25 @@
 
     /**
      * This download hasn't stated yet
+     * @hide
      */
     public static final int STATUS_PENDING = 190;
 
     /**
      * This download hasn't stated yet and is paused
+     * @hide
      */
     public static final int STATUS_PENDING_PAUSED = 191;
 
     /**
      * This download has started
+     * @hide
      */
     public static final int STATUS_RUNNING = 192;
 
     /**
      * This download has started and is paused
+     * @hide
      */
     public static final int STATUS_RUNNING_PAUSED = 193;
 
@@ -412,18 +457,21 @@
      * Warning: there might be other status values that indicate success
      * in the future.
      * Use isSucccess() to capture the entire category.
+     * @hide
      */
     public static final int STATUS_SUCCESS = 200;
 
     /**
      * This request couldn't be parsed. This is also used when processing
      * requests with unknown/unsupported URI schemes.
+     * @hide
      */
     public static final int STATUS_BAD_REQUEST = 400;
 
     /**
      * This download can't be performed because the content type cannot be
      * handled.
+     * @hide
      */
     public static final int STATUS_NOT_ACCEPTABLE = 406;
 
@@ -435,6 +483,7 @@
      * client when a response is received whose length cannot be determined
      * accurately (therefore making it impossible to know when a download
      * completes).
+     * @hide
      */
     public static final int STATUS_LENGTH_REQUIRED = 411;
 
@@ -442,11 +491,13 @@
      * This download was interrupted and cannot be resumed.
      * This is the code for the HTTP error "Precondition Failed", and it is
      * also used in situations where the client doesn't have an ETag at all.
+     * @hide
      */
     public static final int STATUS_PRECONDITION_FAILED = 412;
 
     /**
      * This download was canceled
+     * @hide
      */
     public static final int STATUS_CANCELED = 490;
 
@@ -454,12 +505,14 @@
      * This download has completed with an error.
      * Warning: there will be other status values that indicate errors in
      * the future. Use isStatusError() to capture the entire category.
+     * @hide
      */
     public static final int STATUS_UNKNOWN_ERROR = 491;
 
     /**
      * This download couldn't be completed because of a storage issue.
      * Typically, that's because the filesystem is missing or full.
+     * @hide
      */
     public static final int STATUS_FILE_ERROR = 492;
 
@@ -467,52 +520,66 @@
      * This download couldn't be completed because of an HTTP
      * redirect response that the download manager couldn't
      * handle.
+     * @hide
      */
     public static final int STATUS_UNHANDLED_REDIRECT = 493;
 
     /**
      * This download couldn't be completed because of an
      * unspecified unhandled HTTP code.
+     * @hide
      */
     public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
 
     /**
      * This download couldn't be completed because of an
      * error receiving or processing data at the HTTP level.
+     * @hide
      */
     public static final int STATUS_HTTP_DATA_ERROR = 495;
 
     /**
      * This download couldn't be completed because of an
      * HttpException while setting up the request.
+     * @hide
      */
     public static final int STATUS_HTTP_EXCEPTION = 496;
 
     /**
      * This download couldn't be completed because there were
      * too many redirects.
+     * @hide
      */
     public static final int STATUS_TOO_MANY_REDIRECTS = 497;
 
     /**
      * This download is visible but only shows in the notifications
      * while it's in progress.
+     * @hide
      */
     public static final int VISIBILITY_VISIBLE = 0;
 
     /**
      * This download is visible and shows in the notifications while
      * in progress and after completion.
+     * @hide
      */
     public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
 
     /**
      * This download doesn't show in the UI or in the notifications.
+     * @hide
      */
     public static final int VISIBILITY_HIDDEN = 2;
 
     /**
      * Implementation details
+     *
+     * Exposes constants used to interact with the download manager's
+     * content provider.
+     * The constants URI ... STATUS are the names of columns in the downloads table.
+     *
+     * @hide
      */
     public static final class Impl implements BaseColumns {
         private Impl() {}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index fe3b149..eb48a0c 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -20,6 +20,8 @@
 import com.android.internal.view.BaseIWindow;
 import com.android.internal.view.BaseSurfaceHolder;
 
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.app.Service;
 import android.app.WallpaperManager;
 import android.content.BroadcastReceiver;
@@ -58,9 +60,13 @@
 public abstract class WallpaperService extends Service {
     /**
      * The {@link Intent} that must be declared as handled by the service.
+     * To be supported, the service must also require the
+     * {@link android.Manifest.permission#BIND_WALLPAPER} permission so
+     * that other applications can not abuse it.
      */
+    @SdkConstant(SdkConstantType.SERVICE_ACTION)
     public static final String SERVICE_INTERFACE =
-        "android.service.wallpaper.WallpaperService";
+            "android.service.wallpaper.WallpaperService";
 
     /**
      * Name under which a WallpaperService component publishes information
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
new file mode 100644
index 0000000..2da8f14
--- /dev/null
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.Context;
+
+/**
+ * Detects transformation gestures involving more than one pointer ("multitouch")
+ * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
+ * callback will notify users when a particular gesture event has occurred.
+ * This class should only be used with {@link MotionEvent}s reported via touch.
+ * 
+ * To use this class:
+ * <ul>
+ *  <li>Create an instance of the {@code ScaleGestureDetector} for your
+ *      {@link View}
+ *  <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
+ *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your
+ *          callback will be executed when the events occur.
+ * </ul>
+ * @hide Pending API approval
+ */
+public class ScaleGestureDetector {
+    /**
+     * The listener for receiving notifications when gestures occur.
+     * If you want to listen for all the different gestures then implement
+     * this interface. If you only want to listen for a subset it might
+     * be easier to extend {@link SimpleOnScaleGestureListener}.
+     * 
+     * An application will receive events in the following order:
+     * <ul>
+     *  <li>One {@link OnScaleGestureListener#onScaleBegin()}
+     *  <li>Zero or more {@link OnScaleGestureListener#onScale()}
+     *  <li>One {@link OnScaleGestureListener#onTransformEnd()}
+     * </ul>
+     */
+    public interface OnScaleGestureListener {
+        /**
+         * Responds to scaling events for a gesture in progress.
+         * Reported by pointer motion.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         * @return Whether or not the detector should consider this event
+         *          as handled. If an event was not handled, the detector
+         *          will continue to accumulate movement until an event is
+         *          handled. This can be useful if an application, for example,
+         *          only wants to update scaling factors if the change is
+         *          greater than 0.01.
+         */
+        public boolean onScale(ScaleGestureDetector detector);
+
+        /**
+         * Responds to the beginning of a scaling gesture. Reported by
+         * new pointers going down.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         * @return Whether or not the detector should continue recognizing
+         *          this gesture. For example, if a gesture is beginning
+         *          with a focal point outside of a region where it makes
+         *          sense, onScaleBegin() may return false to ignore the
+         *          rest of the gesture.
+         */
+        public boolean onScaleBegin(ScaleGestureDetector detector);
+
+        /**
+         * Responds to the end of a scale gesture. Reported by existing
+         * pointers going up. If the end of a gesture would result in a fling,
+         * {@link onTransformFling()} is called instead.
+         * 
+         * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
+         * and {@link ScaleGestureDetector#getFocusY()} will return the location
+         * of the pointer remaining on the screen.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         */
+        public void onScaleEnd(ScaleGestureDetector detector);
+    }
+    
+    /**
+     * A convenience class to extend when you only want to listen for a subset
+     * of scaling-related events. This implements all methods in
+     * {@link OnScaleGestureListener} but does nothing.
+     * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} and
+     * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} return
+     * {@code true}. 
+     */
+    public class SimpleOnScaleGestureListener implements OnScaleGestureListener {
+
+        public boolean onScale(ScaleGestureDetector detector) {
+            return true;
+        }
+
+        public boolean onScaleBegin(ScaleGestureDetector detector) {
+            return true;
+        }
+
+        public void onScaleEnd(ScaleGestureDetector detector) {
+            // Intentionally empty
+        }
+    }
+
+    private static final float PRESSURE_THRESHOLD = 0.67f;
+
+    private Context mContext;
+    private OnScaleGestureListener mListener;
+    private boolean mGestureInProgress;
+
+    private MotionEvent mPrevEvent;
+    private MotionEvent mCurrEvent;
+
+    private float mFocusX;
+    private float mFocusY;
+    private float mPrevFingerDiffX;
+    private float mPrevFingerDiffY;
+    private float mCurrFingerDiffX;
+    private float mCurrFingerDiffY;
+    private float mCurrLen;
+    private float mPrevLen;
+    private float mScaleFactor;
+    private float mCurrPressure;
+    private float mPrevPressure;
+    private long mTimeDelta;
+
+    public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
+        mContext = context;
+        mListener = listener;
+    }
+
+    public boolean onTouchEvent(MotionEvent event) {
+        final int action = event.getAction();
+        boolean handled = true;
+
+        if (!mGestureInProgress) {
+            if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
+                    action == MotionEvent.ACTION_POINTER_2_DOWN) &&
+                    event.getPointerCount() >= 2) {
+                // We have a new multi-finger gesture
+                
+                // Be paranoid in case we missed an event
+                reset();
+                
+                mPrevEvent = MotionEvent.obtain(event);
+                mTimeDelta = 0;
+                
+                setContext(event);
+                mGestureInProgress = mListener.onScaleBegin(this);
+            }
+        } else {
+            // Transform gesture in progress - attempt to handle it
+            switch (action) {
+                case MotionEvent.ACTION_POINTER_1_UP:
+                case MotionEvent.ACTION_POINTER_2_UP:
+                    // Gesture ended
+                    setContext(event);
+                    
+                    // Set focus point to the remaining finger
+                    int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
+                            >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
+                    mFocusX = event.getX(id);
+                    mFocusY = event.getY(id);
+                    
+                    mListener.onScaleEnd(this);
+                    mGestureInProgress = false;
+
+                    reset();
+                    break;
+
+                case MotionEvent.ACTION_CANCEL:
+                    mListener.onScaleEnd(this);
+                    mGestureInProgress = false;
+
+                    reset();
+                    break;
+
+                case MotionEvent.ACTION_MOVE:
+                    setContext(event);
+
+                    // Only accept the event if our relative pressure is within
+                    // a certain limit - this can help filter shaky data as a
+                    // finger is lifted.
+                    if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
+                        final boolean updatePrevious = mListener.onScale(this);
+
+                        if (updatePrevious) {
+                            mPrevEvent.recycle();
+                            mPrevEvent = MotionEvent.obtain(event);
+                        }
+                    }
+                    break;
+            }
+        }
+        return handled;
+    }
+
+    private void setContext(MotionEvent curr) {
+        if (mCurrEvent != null) {
+            mCurrEvent.recycle();
+        }
+        mCurrEvent = MotionEvent.obtain(curr);
+
+        mCurrLen = -1;
+        mPrevLen = -1;
+        mScaleFactor = -1;
+
+        final MotionEvent prev = mPrevEvent;
+
+        final float px0 = prev.getX(0);
+        final float py0 = prev.getY(0);
+        final float px1 = prev.getX(1);
+        final float py1 = prev.getY(1);
+        final float cx0 = curr.getX(0);
+        final float cy0 = curr.getY(0);
+        final float cx1 = curr.getX(1);
+        final float cy1 = curr.getY(1);
+
+        final float pvx = px1 - px0;
+        final float pvy = py1 - py0;
+        final float cvx = cx1 - cx0;
+        final float cvy = cy1 - cy0;
+        mPrevFingerDiffX = pvx;
+        mPrevFingerDiffY = pvy;
+        mCurrFingerDiffX = cvx;
+        mCurrFingerDiffY = cvy;
+
+        mFocusX = cx0 + cvx * 0.5f;
+        mFocusY = cy0 + cvy * 0.5f;
+        mTimeDelta = curr.getEventTime() - prev.getEventTime();
+        mCurrPressure = curr.getPressure(0) + curr.getPressure(1);
+        mPrevPressure = prev.getPressure(0) + prev.getPressure(1);
+    }
+
+    private void reset() {
+        if (mPrevEvent != null) {
+            mPrevEvent.recycle();
+            mPrevEvent = null;
+        }
+        if (mCurrEvent != null) {
+            mCurrEvent.recycle();
+            mCurrEvent = null;
+        }
+    }
+
+    /**
+     * Returns {@code true} if a two-finger scale gesture is in progress.
+     * @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
+     */
+    public boolean isInProgress() {
+        return mGestureInProgress;
+    }
+
+    /**
+     * Get the X coordinate of the current gesture's focal point.
+     * If a gesture is in progress, the focal point is directly between
+     * the two pointers forming the gesture.
+     * If a gesture is ending, the focal point is the location of the
+     * remaining pointer on the screen.
+     * If {@link isInProgress()} would return false, the result of this
+     * function is undefined.
+     * 
+     * @return X coordinate of the focal point in pixels.
+     */
+    public float getFocusX() {
+        return mFocusX;
+    }
+
+    /**
+     * Get the Y coordinate of the current gesture's focal point.
+     * If a gesture is in progress, the focal point is directly between
+     * the two pointers forming the gesture.
+     * If a gesture is ending, the focal point is the location of the
+     * remaining pointer on the screen.
+     * If {@link isInProgress()} would return false, the result of this
+     * function is undefined.
+     * 
+     * @return Y coordinate of the focal point in pixels.
+     */
+    public float getFocusY() {
+        return mFocusY;
+    }
+
+    /**
+     * Return the current distance between the two pointers forming the
+     * gesture in progress.
+     * 
+     * @return Distance between pointers in pixels.
+     */
+    public float getCurrentSpan() {
+        if (mCurrLen == -1) {
+            final float cvx = mCurrFingerDiffX;
+            final float cvy = mCurrFingerDiffY;
+            mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
+        }
+        return mCurrLen;
+    }
+
+    /**
+     * Return the previous distance between the two pointers forming the
+     * gesture in progress.
+     * 
+     * @return Previous distance between pointers in pixels.
+     */
+    public float getPreviousSpan() {
+        if (mPrevLen == -1) {
+            final float pvx = mPrevFingerDiffX;
+            final float pvy = mPrevFingerDiffY;
+            mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
+        }
+        return mPrevLen;
+    }
+
+    /**
+     * Return the scaling factor from the previous scale event to the current
+     * event. This value is defined as
+     * ({@link getCurrentSpan()} / {@link getPreviousSpan()}).
+     * 
+     * @return The current scaling factor.
+     */
+    public float getScaleFactor() {
+        if (mScaleFactor == -1) {
+            mScaleFactor = getCurrentSpan() / getPreviousSpan();
+        }
+        return mScaleFactor;
+    }
+    
+    /**
+     * Return the time difference in milliseconds between the previous
+     * accepted scaling event and the current scaling event.
+     * 
+     * @return Time difference since the last scaling event in milliseconds.
+     */
+    public long getTimeDelta() {
+        return mTimeDelta;
+    }
+    
+    /**
+     * Return the event time of the current event being processed.
+     * 
+     * @return Current event time in milliseconds.
+     */
+    public long getEventTime() {
+        return mCurrEvent.getEventTime();
+    }
+}
diff --git a/core/java/android/view/TransformGestureDetector.java b/core/java/android/view/TransformGestureDetector.java
deleted file mode 100644
index 196716a..0000000
--- a/core/java/android/view/TransformGestureDetector.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.GestureDetector.SimpleOnGestureListener;
-
-/**
- * Detects transformation gestures involving more than one pointer ("multitouch")
- * using the supplied {@link MotionEvent}s. The {@link OnGestureListener} callback
- * will notify users when a particular gesture event has occurred. This class
- * should only be used with {@link MotionEvent}s reported via touch.
- * 
- * To use this class:
- * <ul>
- *  <li>Create an instance of the {@code TransformGestureDetector} for your
- *      {@link View}
- *  <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
- *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your
- *          callback will be executed when the events occur.
- * </ul>
- * @hide Pending API approval
- */
-public class TransformGestureDetector {
-    /**
-     * The listener for receiving notifications when gestures occur.
-     * If you want to listen for all the different gestures then implement
-     * this interface. If you only want to listen for a subset it might
-     * be easier to extend {@link SimpleOnGestureListener}.
-     * 
-     * An application will receive events in the following order:
-     * One onTransformBegin()
-     * Zero or more onTransform()
-     * One onTransformEnd() or onTransformFling()
-     */
-    public interface OnTransformGestureListener {
-        /**
-         * Responds to transformation events for a gesture in progress.
-         * Reported by pointer motion.
-         * 
-         * @param detector The detector reporting the event - use this to
-         *          retrieve extended info about event state.
-         * @return true if the event was handled, false otherwise.
-         */
-        public boolean onTransform(TransformGestureDetector detector);
-        
-        /**
-         * Responds to the beginning of a transformation gesture. Reported by
-         * new pointers going down.
-         * 
-         * @param detector The detector reporting the event - use this to
-         *          retrieve extended info about event state.
-         * @return true if the event was handled, false otherwise.
-         */
-        public boolean onTransformBegin(TransformGestureDetector detector);
- 
-        /**
-         * Responds to the end of a transformation gesture. Reported by existing
-         * pointers going up. If the end of a gesture would result in a fling,
-         * onTransformFling is called instead.
-         * 
-         * @param detector The detector reporting the event - use this to
-         *          retrieve extended info about event state.
-         * @return true if the event was handled, false otherwise.
-         */
-        public boolean onTransformEnd(TransformGestureDetector detector);
-
-        /**
-         * Responds to the end of a transformation gesture that begins a fling.
-         * Reported by existing pointers going up. If the end of a gesture 
-         * would not result in a fling, onTransformEnd is called instead.
-         * 
-         * @param detector The detector reporting the event - use this to
-         *          retrieve extended info about event state.
-         * @return true if the event was handled, false otherwise.
-         */
-        public boolean onTransformFling(TransformGestureDetector detector);
-    }
-    
-    private static final boolean DEBUG = false;
-    
-    private static final int INITIAL_EVENT_IGNORES = 2;
-    
-    private Context mContext;
-    private float mTouchSizeScale;
-    private OnTransformGestureListener mListener;
-    private int mVelocityTimeUnits;
-    private MotionEvent mInitialEvent;
-    
-    private MotionEvent mPrevEvent;
-    private MotionEvent mCurrEvent;
-    private VelocityTracker mVelocityTracker;
-
-    private float mCenterX;
-    private float mCenterY;
-    private float mTransX;
-    private float mTransY;
-    private float mPrevFingerDiffX;
-    private float mPrevFingerDiffY;
-    private float mCurrFingerDiffX;
-    private float mCurrFingerDiffY;
-    private float mRotateDegrees;
-    private float mCurrLen;
-    private float mPrevLen;
-    private float mScaleFactor;
-    
-    // Units in pixels. Current value is pulled out of thin air for debugging only.
-    private float mPointerJumpLimit = 30;
-    
-    private int mEventIgnoreCount;
-    
-   public TransformGestureDetector(Context context, OnTransformGestureListener listener,
-            int velocityTimeUnits) {
-        mContext = context;
-        mListener = listener;
-        mTouchSizeScale = context.getResources().getDisplayMetrics().widthPixels/3;
-        mVelocityTimeUnits = velocityTimeUnits;
-        mEventIgnoreCount = INITIAL_EVENT_IGNORES;
-    }
-    
-    public TransformGestureDetector(Context context, OnTransformGestureListener listener) {
-        this(context, listener, 1000);
-    }
-    
-    public boolean onTouchEvent(MotionEvent event) {
-        final int action = event.getAction();
-        boolean handled = true;
-
-        if (mInitialEvent == null) {
-            // No transform gesture in progress
-            if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
-                    action == MotionEvent.ACTION_POINTER_2_DOWN) &&
-                    event.getPointerCount() >= 2) {
-                // We have a new multi-finger gesture
-                mInitialEvent = MotionEvent.obtain(event);
-                mPrevEvent = MotionEvent.obtain(event);
-                mVelocityTracker = VelocityTracker.obtain();
-                handled = mListener.onTransformBegin(this);
-            }
-        } else {
-            // Transform gesture in progress - attempt to handle it
-            switch (action) {
-                case MotionEvent.ACTION_POINTER_1_UP:
-                case MotionEvent.ACTION_POINTER_2_UP:
-                    // Gesture ended
-                    handled = mListener.onTransformEnd(this);
-
-                    reset();
-                    break;
-                    
-                case MotionEvent.ACTION_CANCEL:
-                    handled = mListener.onTransformEnd(this);
-                    
-                    reset();
-                    break;
-                    
-                case MotionEvent.ACTION_MOVE:
-                    setContext(event);
-
-                    // Our first few events can be crazy from some touchscreens - drop them.
-                    if (mEventIgnoreCount == 0) {
-                        mVelocityTracker.addMovement(event);
-                        handled = mListener.onTransform(this);
-                    } else {
-                        mEventIgnoreCount--;
-                    }
-                    
-                    mPrevEvent.recycle();
-                    mPrevEvent = MotionEvent.obtain(event);
-                    break;
-            }
-        }
-        return handled;
-    }
-    
-    private void setContext(MotionEvent curr) {
-        mCurrEvent = MotionEvent.obtain(curr);
-
-        mRotateDegrees = -1;
-        mCurrLen = -1;
-        mPrevLen = -1;
-        mScaleFactor = -1;
-
-        final MotionEvent prev = mPrevEvent;
-        
-        float px0 = prev.getX(0);
-        float py0 = prev.getY(0);
-        float px1 = prev.getX(1);
-        float py1 = prev.getY(1);
-        float cx0 = curr.getX(0);
-        float cy0 = curr.getY(0);
-        float cx1 = curr.getX(1);
-        float cy1 = curr.getY(1);
-
-        // Some touchscreens do weird things with pointer values where points are
-        // too close along one axis. Try to detect this here and smooth things out.
-        // The main indicator is that we get the X or Y value from the other pointer.
-        final float dx0 = cx0 - px0;
-        final float dy0 = cy0 - py0;
-        final float dx1 = cx1 - px1;
-        final float dy1 = cy1 - py1;
-
-        if (cx0 == cx1) {
-            if (Math.abs(dx0) > mPointerJumpLimit) {
-                 cx0 = px0;
-            } else if (Math.abs(dx1) > mPointerJumpLimit) {
-                cx1 = px1;
-            }
-        } else if (cy0 == cy1) {
-            if (Math.abs(dy0) > mPointerJumpLimit) {
-                cy0 = py0;
-            } else if (Math.abs(dy1) > mPointerJumpLimit) {
-                cy1 = py1;
-            }
-        }
-        
-        final float pvx = px1 - px0;
-        final float pvy = py1 - py0;
-        final float cvx = cx1 - cx0;
-        final float cvy = cy1 - cy0;
-        mPrevFingerDiffX = pvx;
-        mPrevFingerDiffY = pvy;
-        mCurrFingerDiffX = cvx;
-        mCurrFingerDiffY = cvy;
-
-        final float pmidx = px0 + pvx * 0.5f;
-        final float pmidy = py0 + pvy * 0.5f;
-        final float cmidx = cx0 + cvx * 0.5f;
-        final float cmidy = cy0 + cvy * 0.5f;
-
-        mCenterX = cmidx;
-        mCenterY = cmidy;
-        mTransX = cmidx - pmidx;
-        mTransY = cmidy - pmidy;
-    }
-    
-    private void reset() {
-        if (mInitialEvent != null) {
-            mInitialEvent.recycle();
-            mInitialEvent = null;
-        }
-        if (mPrevEvent != null) {
-            mPrevEvent.recycle();
-            mPrevEvent = null;
-        }
-        if (mCurrEvent != null) {
-            mCurrEvent.recycle();
-            mCurrEvent = null;
-        }
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-        mEventIgnoreCount = INITIAL_EVENT_IGNORES;
-    }
-    
-    public float getCenterX() {
-        return mCenterX;
-    }
-
-    public float getCenterY() {
-        return mCenterY;
-    }
-
-    public float getTranslateX() {
-        return mTransX;
-    }
-
-    public float getTranslateY() {
-        return mTransY;
-    }
-
-    public float getCurrentSpan() {
-        if (mCurrLen == -1) {
-            final float cvx = mCurrFingerDiffX;
-            final float cvy = mCurrFingerDiffY;
-            mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
-        }
-        return mCurrLen;
-    }
-
-    public float getPreviousSpan() {
-        if (mPrevLen == -1) {
-            final float pvx = mPrevFingerDiffX;
-            final float pvy = mPrevFingerDiffY;
-            mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
-        }
-        return mPrevLen;
-    }
-
-    public float getScaleFactor() {
-        if (mScaleFactor == -1) {
-            mScaleFactor = getCurrentSpan() / getPreviousSpan();
-        }
-        return mScaleFactor;
-    }
-
-    public float getRotation() {
-        throw new UnsupportedOperationException();
-    }
-}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index a5e0e94..2ddf5f8 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -16,6 +16,8 @@
 
 package android.view.inputmethod;
 
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.inputmethodservice.InputMethodService;
 import android.os.IBinder;
 import android.os.ResultReceiver;
@@ -54,9 +56,12 @@
     /**
      * This is the interface name that a service implementing an input
      * method should say that it supports -- that is, this is the action it
-     * uses for its intent filter.  (Note: this name is used because this
-     * interface should be moved to the view package.)
+     * uses for its intent filter.
+     * To be supported, the service must also require the
+     * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission so
+     * that other applications can not abuse it.
      */
+    @SdkConstant(SdkConstantType.SERVICE_ACTION)
     public static final String SERVICE_INTERFACE = "android.view.InputMethod";
     
     /**
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 37372c5..0ce70fa 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -50,6 +50,7 @@
      * Whether the children of this layout are baseline aligned.  Only applicable
      * if {@link #mOrientation} is horizontal.
      */
+    @ViewDebug.ExportedProperty
     private boolean mBaselineAligned = true;
 
     /**
@@ -59,6 +60,7 @@
      * Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
      * with whether the children of this layout are baseline aligned.
      */
+    @ViewDebug.ExportedProperty
     private int mBaselineAlignedChildIndex = -1;
 
     /**
@@ -66,12 +68,30 @@
      * We'll calculate the baseline of this layout as we measure vertically; for
      * horizontal linear layouts, the offset of 0 is appropriate.
      */
+    @ViewDebug.ExportedProperty
     private int mBaselineChildTop = 0;
 
+    @ViewDebug.ExportedProperty
     private int mOrientation;
+    @ViewDebug.ExportedProperty(mapping = {
+            @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
+            @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
+            @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
+            @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
+            @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
+            @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
+            @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
+            @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
+            @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
+            @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL,   to = "FILL_HORIZONTAL"),
+            @ViewDebug.IntToString(from = Gravity.CENTER,            to = "CENTER"),
+            @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
+        })
     private int mGravity = Gravity.LEFT | Gravity.TOP;
+    @ViewDebug.ExportedProperty
     private int mTotalLength;
 
+    @ViewDebug.ExportedProperty
     private float mWeightSum;
 
     private int[] mMaxAscent;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c49a86a..d81476a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -927,21 +927,27 @@
         android:description="@string/permdesc_readInputState"
         android:protectionLevel="signature" />
 
-    <!-- Must be required by input method services, to ensure that only the
-         system can bind to them. -->
+    <!-- Must be required by an {@link android.inputmethodservice.InputMethodService},
+         to ensure that only the system can bind to it. -->
     <permission android:name="android.permission.BIND_INPUT_METHOD"
         android:label="@string/permlab_bindInputMethod"
         android:description="@string/permdesc_bindInputMethod"
         android:protectionLevel="signature" />
 
-    <!-- Must be required by wallpaper services, to ensure that only the
-         system can bind to them.
-         @hide Live Wallpaper -->
+    <!-- Must be required by a {@link android.service.wallpaper.WallpaperService},
+         to ensure that only the system can bind to it. -->
     <permission android:name="android.permission.BIND_WALLPAPER"
         android:label="@string/permlab_bindWallpaper"
         android:description="@string/permdesc_bindWallpaper"
         android:protectionLevel="signatureOrSystem" />
 
+    <!-- Must be required by device administration receiver, to ensure that only the
+         system can interact with it. -->
+    <permission android:name="android.permission.BIND_DEVICE_ADMIN"
+        android:label="@string/permlab_bindDeviceAdmin"
+        android:description="@string/permdesc_bindDeviceAdmin"
+        android:protectionLevel="signature" />
+
     <!-- Allows low-level access to setting the orientation (actually
          rotation) of the screen.  Not for use by normal applications. -->
     <permission android:name="android.permission.SET_ORIENTATION"
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 53bb586..3dbfa25 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -131,7 +131,8 @@
          the first component from this list which is found to be installed is set as the
          preferred activity. -->
     <string-array name="default_web_search_providers">
-        <item>com.android.quicksearchbox/com.android.googlesearch.GoogleSearch</item>
+        <item>com.google.android.googlequicksearchbox/.google.GoogleSearch</item>
+        <item>com.android.quicksearchbox/.google.GoogleSearch</item>
         <item>com.google.android.providers.enhancedgooglesearch/.Launcher</item>
         <item>com.android.googlesearch/.GoogleSearch</item>
         <item>com.android.websearch/.Search.1</item>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 539db83..265dacd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -617,6 +617,12 @@
         interface of a wallpaper. Should never be needed for normal applications.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bindDeviceAdmin">interact with a device admin</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bindDeviceAdmin">Allows the holder to send intents to
+        a device administrator. Should never be needed for normal applications.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_setOrientation">change screen orientation</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_setOrientation">Allows an application to change
diff --git a/keystore/tests/src/android/security/SystemKeyStoreTest.java b/keystore/tests/src/android/security/SystemKeyStoreTest.java
index bbeceeb7..a9e2687 100644
--- a/keystore/tests/src/android/security/SystemKeyStoreTest.java
+++ b/keystore/tests/src/android/security/SystemKeyStoreTest.java
@@ -60,7 +60,7 @@
 
     public void testBasicAccess() throws Exception {
         try {
-            byte[] newKey = mSysKeyStore.generateNewKey(128, "AES", keyName);
+            byte[] newKey = mSysKeyStore.generateNewKey(128, "Blowfish", keyName);
             assertNotNull(newKey);
             byte[] recKey = mSysKeyStore.retrieveKey(keyName);
             assertEquals(newKey.length, recKey.length);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index e36e78c..26b9357 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -29,6 +29,7 @@
         MPEG4Extractor.cpp        \
         MPEG4Writer.cpp           \
         MediaExtractor.cpp        \
+        SampleIterator.cpp        \
         SampleTable.cpp           \
         ShoutcastSource.cpp       \
         StagefrightMediaScanner.cpp \
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 07a5a82..0e9900b 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -232,8 +232,9 @@
             uint32_t sampleIndex;
             uint32_t sampleTime;
             if (track->sampleTable->findThumbnailSample(&sampleIndex) == OK
-                    && track->sampleTable->getDecodingTime(
-                        sampleIndex, &sampleTime) == OK) {
+                    && track->sampleTable->getMetaDataForSample(
+                        sampleIndex, NULL /* offset */, NULL /* size */,
+                        &sampleTime) == OK) {
                 track->meta->setInt64(
                         kKeyThumbnailTime,
                         ((int64_t)sampleTime * 1000000) / track->timescale);
@@ -929,20 +930,16 @@
     if (mBuffer == NULL) {
         newBuffer = true;
 
-        status_t err = mSampleTable->getSampleOffsetAndSize(
-                mCurrentSampleIndex, &offset, &size);
-
-        if (err != OK) {
-            return err;
-        }
-
-        err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
+        status_t err =
+            mSampleTable->getMetaDataForSample(
+                    mCurrentSampleIndex, &offset, &size, &dts);
 
         if (err != OK) {
             return err;
         }
 
         err = mGroup->acquire_buffer(&mBuffer);
+
         if (err != OK) {
             CHECK_EQ(mBuffer, NULL);
             return err;
diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp
new file mode 100644
index 0000000..faad42ba
--- /dev/null
+++ b/media/libstagefright/SampleIterator.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SampleIterator"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "include/SampleIterator.h"
+
+#include <arpa/inet.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/Utils.h>
+
+#include "include/SampleTable.h"
+
+namespace android {
+
+SampleIterator::SampleIterator(SampleTable *table)
+    : mTable(table),
+      mInitialized(false),
+      mTimeToSampleIndex(0),
+      mTTSSampleIndex(0),
+      mTTSSampleTime(0),
+      mTTSCount(0),
+      mTTSDuration(0) {
+    reset();
+}
+
+void SampleIterator::reset() {
+    mSampleToChunkIndex = 0;
+    mFirstChunk = 0;
+    mFirstChunkSampleIndex = 0;
+    mStopChunk = 0;
+    mStopChunkSampleIndex = 0;
+    mSamplesPerChunk = 0;
+    mChunkDesc = 0;
+}
+
+status_t SampleIterator::seekTo(uint32_t sampleIndex) {
+    LOGV("seekTo(%d)", sampleIndex);
+
+    if (mTable->mSampleToChunkOffset < 0
+            || mTable->mChunkOffsetOffset < 0
+            || mTable->mSampleSizeOffset < 0
+            || mTable->mTimeToSampleCount == 0) {
+
+        return ERROR_MALFORMED;
+    }
+
+    if (mInitialized && mCurrentSampleIndex == sampleIndex) {
+        return OK;
+    }
+
+    if (!mInitialized || sampleIndex < mFirstChunkSampleIndex) {
+        reset();
+    }
+
+    if (sampleIndex >= mStopChunkSampleIndex) {
+        status_t err;
+        if ((err = findChunkRange(sampleIndex)) != OK) {
+            LOGE("findChunkRange failed");
+            return err;
+        }
+    }
+
+    CHECK(sampleIndex < mStopChunkSampleIndex);
+
+    uint32_t chunk =
+        (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk
+        + mFirstChunk;
+
+    if (!mInitialized || chunk != mCurrentChunkIndex) {
+        mCurrentChunkIndex = chunk;
+
+        status_t err;
+        if ((err = getChunkOffset(chunk, &mCurrentChunkOffset)) != OK) {
+            LOGE("getChunkOffset return error");
+            return err;
+        }
+
+        mCurrentChunkSampleSizes.clear();
+
+        uint32_t firstChunkSampleIndex =
+            mFirstChunkSampleIndex
+                + mSamplesPerChunk * (mCurrentChunkIndex - mFirstChunk);
+
+        for (uint32_t i = 0; i < mSamplesPerChunk; ++i) {
+            size_t sampleSize;
+            if ((err = getSampleSizeDirect(
+                            firstChunkSampleIndex + i, &sampleSize)) != OK) {
+                LOGE("getSampleSizeDirect return error");
+                return err;
+            }
+
+            mCurrentChunkSampleSizes.push(sampleSize);
+        }
+    }
+
+    uint32_t chunkRelativeSampleIndex =
+        (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk;
+
+    mCurrentSampleOffset = mCurrentChunkOffset;
+    for (uint32_t i = 0; i < chunkRelativeSampleIndex; ++i) {
+        mCurrentSampleOffset += mCurrentChunkSampleSizes[i];
+    }
+
+    mCurrentSampleSize = mCurrentChunkSampleSizes[chunkRelativeSampleIndex];
+    if (sampleIndex < mTTSSampleIndex) {
+        mTimeToSampleIndex = 0;
+        mTTSSampleIndex = 0;
+        mTTSSampleTime = 0;
+        mTTSCount = 0;
+        mTTSDuration = 0;
+    }
+
+    status_t err;
+    if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) {
+        LOGE("findSampleTime return error");
+        return err;
+    }
+
+    mCurrentSampleIndex = sampleIndex;
+
+    mInitialized = true;
+
+    return OK;
+}
+
+status_t SampleIterator::findChunkRange(uint32_t sampleIndex) {
+    CHECK(sampleIndex >= mFirstChunkSampleIndex);
+
+    while (sampleIndex >= mStopChunkSampleIndex) {
+        if (mSampleToChunkIndex == mTable->mNumSampleToChunkOffsets) {
+            return ERROR_OUT_OF_RANGE;
+        }
+
+        mFirstChunkSampleIndex = mStopChunkSampleIndex;
+
+        const SampleTable::SampleToChunkEntry *entry =
+            &mTable->mSampleToChunkEntries[mSampleToChunkIndex];
+
+        mFirstChunk = entry->startChunk;
+        mSamplesPerChunk = entry->samplesPerChunk;
+        mChunkDesc = entry->chunkDesc;
+
+        if (mSampleToChunkIndex + 1 < mTable->mNumSampleToChunkOffsets) {
+            mStopChunk = entry[1].startChunk;
+
+            mStopChunkSampleIndex =
+                mFirstChunkSampleIndex
+                    + (mStopChunk - mFirstChunk) * mSamplesPerChunk;
+        } else {
+            mStopChunk = 0xffffffff;
+            mStopChunkSampleIndex = 0xffffffff;
+        }
+
+        ++mSampleToChunkIndex;
+    }
+
+    return OK;
+}
+
+status_t SampleIterator::getChunkOffset(uint32_t chunk, off_t *offset) {
+    *offset = 0;
+
+    if (chunk >= mTable->mNumChunkOffsets) {
+        return ERROR_OUT_OF_RANGE;
+    }
+
+    if (mTable->mChunkOffsetType == SampleTable::kChunkOffsetType32) {
+        uint32_t offset32;
+
+        if (mTable->mDataSource->readAt(
+                    mTable->mChunkOffsetOffset + 8 + 4 * chunk,
+                    &offset32,
+                    sizeof(offset32)) < (ssize_t)sizeof(offset32)) {
+            return ERROR_IO;
+        }
+
+        *offset = ntohl(offset32);
+    } else {
+        CHECK_EQ(mTable->mChunkOffsetType, SampleTable::kChunkOffsetType64);
+
+        uint64_t offset64;
+        if (mTable->mDataSource->readAt(
+                    mTable->mChunkOffsetOffset + 8 + 8 * chunk,
+                    &offset64,
+                    sizeof(offset64)) < (ssize_t)sizeof(offset64)) {
+            return ERROR_IO;
+        }
+
+        *offset = ntoh64(offset64);
+    }
+
+    return OK;
+}
+
+status_t SampleIterator::getSampleSizeDirect(
+        uint32_t sampleIndex, size_t *size) {
+    *size = 0;
+
+    if (sampleIndex >= mTable->mNumSampleSizes) {
+        return ERROR_OUT_OF_RANGE;
+    }
+
+    if (mTable->mDefaultSampleSize > 0) {
+        *size = mTable->mDefaultSampleSize;
+        return OK;
+    }
+
+    switch (mTable->mSampleSizeFieldSize) {
+        case 32:
+        {
+            if (mTable->mDataSource->readAt(
+                        mTable->mSampleSizeOffset + 12 + 4 * sampleIndex,
+                        size, sizeof(*size)) < (ssize_t)sizeof(*size)) {
+                return ERROR_IO;
+            }
+
+            *size = ntohl(*size);
+            break;
+        }
+
+        case 16:
+        {
+            uint16_t x;
+            if (mTable->mDataSource->readAt(
+                        mTable->mSampleSizeOffset + 12 + 2 * sampleIndex,
+                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+                return ERROR_IO;
+            }
+
+            *size = ntohs(x);
+            break;
+        }
+
+        case 8:
+        {
+            uint8_t x;
+            if (mTable->mDataSource->readAt(
+                        mTable->mSampleSizeOffset + 12 + sampleIndex,
+                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+                return ERROR_IO;
+            }
+
+            *size = x;
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ(mTable->mSampleSizeFieldSize, 4);
+
+            uint8_t x;
+            if (mTable->mDataSource->readAt(
+                        mTable->mSampleSizeOffset + 12 + sampleIndex / 2,
+                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+                return ERROR_IO;
+            }
+
+            *size = (sampleIndex & 1) ? x & 0x0f : x >> 4;
+            break;
+        }
+    }
+
+    return OK;
+}
+
+status_t SampleIterator::findSampleTime(
+        uint32_t sampleIndex, uint32_t *time) {
+    if (sampleIndex >= mTable->mNumSampleSizes) {
+        return ERROR_OUT_OF_RANGE;
+    }
+
+    while (sampleIndex >= mTTSSampleIndex + mTTSCount) {
+        if (mTimeToSampleIndex == mTable->mTimeToSampleCount) {
+            return ERROR_OUT_OF_RANGE;
+        }
+
+        mTTSSampleIndex += mTTSCount;
+        mTTSSampleTime += mTTSCount * mTTSDuration;
+
+        mTTSCount = mTable->mTimeToSample[2 * mTimeToSampleIndex];
+        mTTSDuration = mTable->mTimeToSample[2 * mTimeToSampleIndex + 1];
+
+        ++mTimeToSampleIndex;
+    }
+
+    *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex);
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 2de96d4..89a522e 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -15,9 +15,11 @@
  */
 
 #define LOG_TAG "SampleTable"
+//#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
 #include "include/SampleTable.h"
+#include "include/SampleIterator.h"
 
 #include <arpa/inet.h>
 
@@ -27,10 +29,16 @@
 
 namespace android {
 
-static const uint32_t kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o');
-static const uint32_t kChunkOffsetType64 = FOURCC('c', 'o', '6', '4');
-static const uint32_t kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
-static const uint32_t kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
+// static
+const uint32_t SampleTable::kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o');
+// static
+const uint32_t SampleTable::kChunkOffsetType64 = FOURCC('c', 'o', '6', '4');
+// static
+const uint32_t SampleTable::kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
+// static
+const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
+
+////////////////////////////////////////////////////////////////////////////////
 
 SampleTable::SampleTable(const sp<DataSource> &source)
     : mDataSource(source),
@@ -46,12 +54,20 @@
       mTimeToSampleCount(0),
       mTimeToSample(NULL),
       mSyncSampleOffset(-1),
-      mNumSyncSamples(0) {
+      mNumSyncSamples(0),
+      mSampleToChunkEntries(NULL) {
+    mSampleIterator = new SampleIterator(this);
 }
 
 SampleTable::~SampleTable() {
+    delete[] mSampleToChunkEntries;
+    mSampleToChunkEntries = NULL;
+
     delete[] mTimeToSample;
     mTimeToSample = NULL;
+
+    delete mSampleIterator;
+    mSampleIterator = NULL;
 }
 
 status_t SampleTable::setChunkOffsetParams(
@@ -124,6 +140,25 @@
         return ERROR_MALFORMED;
     }
 
+    mSampleToChunkEntries =
+        new SampleToChunkEntry[mNumSampleToChunkOffsets];
+
+    for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
+        uint8_t buffer[12];
+        if (mDataSource->readAt(
+                    mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer))
+                != (ssize_t)sizeof(buffer)) {
+            return ERROR_IO;
+        }
+
+        CHECK(U32_AT(buffer) >= 1);  // chunk index is 1 based in the spec.
+
+        // We want the chunk index to be 0-based.
+        mSampleToChunkEntries[i].startChunk = U32_AT(buffer) - 1;
+        mSampleToChunkEntries[i].samplesPerChunk = U32_AT(&buffer[4]);
+        mSampleToChunkEntries[i].chunkDesc = U32_AT(&buffer[8]);
+    }
+
     return OK;
 }
 
@@ -250,217 +285,10 @@
     return mNumChunkOffsets;
 }
 
-status_t SampleTable::getChunkOffset(uint32_t chunk_index, off_t *offset) {
-    *offset = 0;
-
-    if (mChunkOffsetOffset < 0) {
-        return ERROR_MALFORMED;
-    }
-
-    if (chunk_index >= mNumChunkOffsets) {
-        return ERROR_OUT_OF_RANGE;
-    }
-
-    if (mChunkOffsetType == kChunkOffsetType32) {
-        uint32_t offset32;
-
-        if (mDataSource->readAt(
-                    mChunkOffsetOffset + 8 + 4 * chunk_index,
-                    &offset32,
-                    sizeof(offset32)) < (ssize_t)sizeof(offset32)) {
-            return ERROR_IO;
-        }
-
-        *offset = ntohl(offset32);
-    } else {
-        CHECK_EQ(mChunkOffsetType, kChunkOffsetType64);
-
-        uint64_t offset64;
-        if (mDataSource->readAt(
-                    mChunkOffsetOffset + 8 + 8 * chunk_index,
-                    &offset64,
-                    sizeof(offset64)) < (ssize_t)sizeof(offset64)) {
-            return ERROR_IO;
-        }
-
-        *offset = ntoh64(offset64);
-    }
-
-    return OK;
-}
-
-status_t SampleTable::getChunkForSample(
-        uint32_t sample_index,
-        uint32_t *chunk_index,
-        uint32_t *chunk_relative_sample_index,
-        uint32_t *desc_index) {
-    *chunk_index = 0;
-    *chunk_relative_sample_index = 0;
-    *desc_index = 0;
-
-    if (mSampleToChunkOffset < 0) {
-        return ERROR_MALFORMED;
-    }
-
-    if (sample_index >= countSamples()) {
-        return ERROR_END_OF_STREAM;
-    }
-
-    uint32_t first_chunk = 0;
-    uint32_t samples_per_chunk = 0;
-    uint32_t chunk_desc_index = 0;
-
-    uint32_t index = 0;
-    while (index < mNumSampleToChunkOffsets) {
-        uint8_t buffer[12];
-        if (mDataSource->readAt(mSampleToChunkOffset + 8 + index * 12,
-                                 buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
-            return ERROR_IO;
-        }
-
-        uint32_t stop_chunk = U32_AT(buffer);
-        if (sample_index < (stop_chunk - first_chunk) * samples_per_chunk) {
-            break;
-        }
-
-        sample_index -= (stop_chunk - first_chunk) * samples_per_chunk;
-        first_chunk = stop_chunk;
-        samples_per_chunk = U32_AT(&buffer[4]);
-        chunk_desc_index = U32_AT(&buffer[8]);
-
-        ++index;
-    }
-
-    *chunk_index = sample_index / samples_per_chunk + first_chunk - 1;
-    *chunk_relative_sample_index = sample_index % samples_per_chunk;
-    *desc_index = chunk_desc_index;
-
-    return OK;
-}
-
 uint32_t SampleTable::countSamples() const {
     return mNumSampleSizes;
 }
 
-status_t SampleTable::getSampleSize(
-        uint32_t sample_index, size_t *sample_size) {
-    *sample_size = 0;
-
-    if (mSampleSizeOffset < 0) {
-        return ERROR_MALFORMED;
-    }
-
-    if (sample_index >= mNumSampleSizes) {
-        return ERROR_OUT_OF_RANGE;
-    }
-
-    if (mDefaultSampleSize > 0) {
-        *sample_size = mDefaultSampleSize;
-        return OK;
-    }
-
-    switch (mSampleSizeFieldSize) {
-        case 32:
-        {
-            if (mDataSource->readAt(
-                        mSampleSizeOffset + 12 + 4 * sample_index,
-                        sample_size, sizeof(*sample_size)) < (ssize_t)sizeof(*sample_size)) {
-                return ERROR_IO;
-            }
-
-            *sample_size = ntohl(*sample_size);
-            break;
-        }
-
-        case 16:
-        {
-            uint16_t x;
-            if (mDataSource->readAt(
-                        mSampleSizeOffset + 12 + 2 * sample_index,
-                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
-                return ERROR_IO;
-            }
-
-            *sample_size = ntohs(x);
-            break;
-        }
-
-        case 8:
-        {
-            uint8_t x;
-            if (mDataSource->readAt(
-                        mSampleSizeOffset + 12 + sample_index,
-                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
-                return ERROR_IO;
-            }
-
-            *sample_size = x;
-            break;
-        }
-
-        default:
-        {
-            CHECK_EQ(mSampleSizeFieldSize, 4);
-
-            uint8_t x;
-            if (mDataSource->readAt(
-                        mSampleSizeOffset + 12 + sample_index / 2,
-                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
-                return ERROR_IO;
-            }
-
-            *sample_size = (sample_index & 1) ? x & 0x0f : x >> 4;
-            break;
-        }
-    }
-
-    return OK;
-}
-
-status_t SampleTable::getSampleOffsetAndSize(
-        uint32_t sample_index, off_t *offset, size_t *size) {
-    Mutex::Autolock autoLock(mLock);
-
-    *offset = 0;
-    *size = 0;
-
-    uint32_t chunk_index;
-    uint32_t chunk_relative_sample_index;
-    uint32_t desc_index;
-    status_t err = getChunkForSample(
-            sample_index, &chunk_index, &chunk_relative_sample_index,
-            &desc_index);
-
-    if (err != OK) {
-        return err;
-    }
-
-    err = getChunkOffset(chunk_index, offset);
-
-    if (err != OK) {
-        return err;
-    }
-
-    for (uint32_t j = 0; j < chunk_relative_sample_index; ++j) {
-        size_t sample_size;
-        err = getSampleSize(sample_index - j - 1, &sample_size);
-
-        if (err != OK) {
-            return err;
-        }
-
-        *offset += sample_size;
-    }
-
-    err = getSampleSize(sample_index, size);
-
-    if (err != OK) {
-        return err;
-    }
-
-    return OK;
-}
-
 status_t SampleTable::getMaxSampleSize(size_t *max_size) {
     Mutex::Autolock autoLock(mLock);
 
@@ -468,7 +296,7 @@
 
     for (uint32_t i = 0; i < mNumSampleSizes; ++i) {
         size_t sample_size;
-        status_t err = getSampleSize(i, &sample_size);
+        status_t err = getSampleSize_l(i, &sample_size);
 
         if (err != OK) {
             return err;
@@ -482,34 +310,6 @@
     return OK;
 }
 
-status_t SampleTable::getDecodingTime(uint32_t sample_index, uint32_t *time) {
-    // XXX FIXME idiotic (for the common use-case) O(n) algorithm below...
-
-    Mutex::Autolock autoLock(mLock);
-
-    if (sample_index >= mNumSampleSizes) {
-        return ERROR_OUT_OF_RANGE;
-    }
-
-    uint32_t cur_sample = 0;
-    *time = 0;
-    for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
-        uint32_t n = mTimeToSample[2 * i];
-        uint32_t delta = mTimeToSample[2 * i + 1];
-
-        if (sample_index < cur_sample + n) {
-            *time += delta * (sample_index - cur_sample);
-
-            return OK;
-        }
-
-        *time += delta * n;
-        cur_sample += n;
-    }
-
-    return ERROR_OUT_OF_RANGE;
-}
-
 uint32_t abs_difference(uint32_t time1, uint32_t time2) {
     return time1 > time2 ? time1 - time2 : time2 - time1;
 }
@@ -539,7 +339,7 @@
             }
 
             if (flags & kSyncSample_Flag) {
-                return findClosestSyncSample(*sample_index, sample_index);
+                return findClosestSyncSample_l(*sample_index, sample_index);
             }
 
             return OK;
@@ -552,7 +352,7 @@
     return ERROR_OUT_OF_RANGE;
 }
 
-status_t SampleTable::findClosestSyncSample(
+status_t SampleTable::findClosestSyncSample_l(
         uint32_t start_sample_index, uint32_t *sample_index) {
     *sample_index = 0;
 
@@ -590,6 +390,8 @@
 }
 
 status_t SampleTable::findThumbnailSample(uint32_t *sample_index) {
+    Mutex::Autolock autoLock(mLock);
+
     if (mSyncSampleOffset < 0) {
         // All samples are sync-samples.
         *sample_index = 0;
@@ -620,7 +422,7 @@
 
         // Now x is a sample index.
         size_t sampleSize;
-        status_t err = getSampleSize(x, &sampleSize);
+        status_t err = getSampleSize_l(x, &sampleSize);
         if (err != OK) {
             return err;
         }
@@ -636,5 +438,38 @@
     return OK;
 }
 
+status_t SampleTable::getSampleSize_l(
+        uint32_t sampleIndex, size_t *sampleSize) {
+    return mSampleIterator->getSampleSizeDirect(
+            sampleIndex, sampleSize);
+}
+
+status_t SampleTable::getMetaDataForSample(
+        uint32_t sampleIndex,
+        off_t *offset,
+        size_t *size,
+        uint32_t *decodingTime) {
+    Mutex::Autolock autoLock(mLock);
+
+    status_t err;
+    if ((err = mSampleIterator->seekTo(sampleIndex)) != OK) {
+        return err;
+    }
+
+    if (offset) {
+        *offset = mSampleIterator->getSampleOffset();
+    }
+
+    if (size) {
+        *size = mSampleIterator->getSampleSize();
+    }
+
+    if (decodingTime) {
+        *decodingTime = mSampleIterator->getSampleTime();
+    }
+
+    return OK;
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/include/SampleIterator.h b/media/libstagefright/include/SampleIterator.h
new file mode 100644
index 0000000..a5eaed9
--- /dev/null
+++ b/media/libstagefright/include/SampleIterator.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/Vector.h>
+
+namespace android {
+
+struct SampleTable;
+
+struct SampleIterator {
+    SampleIterator(SampleTable *table);
+
+    status_t seekTo(uint32_t sampleIndex);
+
+    uint32_t getChunkIndex() const { return mCurrentChunkIndex; }
+    uint32_t getDescIndex() const { return mChunkDesc; }
+    off_t getSampleOffset() const { return mCurrentSampleOffset; }
+    size_t getSampleSize() const { return mCurrentSampleSize; }
+    uint32_t getSampleTime() const { return mCurrentSampleTime; }
+
+    status_t getSampleSizeDirect(
+            uint32_t sampleIndex, size_t *size);
+
+private:
+    SampleTable *mTable;
+
+    bool mInitialized;
+
+    uint32_t mSampleToChunkIndex;
+    uint32_t mFirstChunk;
+    uint32_t mFirstChunkSampleIndex;
+    uint32_t mStopChunk;
+    uint32_t mStopChunkSampleIndex;
+    uint32_t mSamplesPerChunk;
+    uint32_t mChunkDesc;
+
+    uint32_t mCurrentChunkIndex;
+    off_t mCurrentChunkOffset;
+    Vector<size_t> mCurrentChunkSampleSizes;
+
+    uint32_t mTimeToSampleIndex;
+    uint32_t mTTSSampleIndex;
+    uint32_t mTTSSampleTime;
+    uint32_t mTTSCount;
+    uint32_t mTTSDuration;
+
+    uint32_t mCurrentSampleIndex;
+    off_t mCurrentSampleOffset;
+    size_t mCurrentSampleSize;
+    uint32_t mCurrentSampleTime;
+
+    void reset();
+    status_t findChunkRange(uint32_t sampleIndex);
+    status_t getChunkOffset(uint32_t chunk, off_t *offset);
+    status_t findSampleTime(uint32_t sampleIndex, uint32_t *time);
+
+    SampleIterator(const SampleIterator &);
+    SampleIterator &operator=(const SampleIterator &);
+};
+
+}  // namespace android
+
diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h
index ead3431..533ce84 100644
--- a/media/libstagefright/include/SampleTable.h
+++ b/media/libstagefright/include/SampleTable.h
@@ -28,6 +28,7 @@
 namespace android {
 
 class DataSource;
+struct SampleIterator;
 
 class SampleTable : public RefBase {
 public:
@@ -50,21 +51,16 @@
     ////////////////////////////////////////////////////////////////////////////
 
     uint32_t countChunkOffsets() const;
-    status_t getChunkOffset(uint32_t chunk_index, off_t *offset);
-
-    status_t getChunkForSample(
-            uint32_t sample_index, uint32_t *chunk_index,
-            uint32_t *chunk_relative_sample_index, uint32_t *desc_index);
 
     uint32_t countSamples() const;
-    status_t getSampleSize(uint32_t sample_index, size_t *sample_size);
-
-    status_t getSampleOffsetAndSize(
-            uint32_t sample_index, off_t *offset, size_t *size);
 
     status_t getMaxSampleSize(size_t *size);
 
-    status_t getDecodingTime(uint32_t sample_index, uint32_t *time);
+    status_t getMetaDataForSample(
+            uint32_t sampleIndex,
+            off_t *offset,
+            size_t *size,
+            uint32_t *decodingTime);
 
     enum {
         kSyncSample_Flag = 1
@@ -72,15 +68,17 @@
     status_t findClosestSample(
             uint32_t req_time, uint32_t *sample_index, uint32_t flags);
 
-    status_t findClosestSyncSample(
-            uint32_t start_sample_index, uint32_t *sample_index);
-
     status_t findThumbnailSample(uint32_t *sample_index);
 
 protected:
     ~SampleTable();
 
 private:
+    static const uint32_t kChunkOffsetType32;
+    static const uint32_t kChunkOffsetType64;
+    static const uint32_t kSampleSizeType32;
+    static const uint32_t kSampleSizeTypeCompact;
+
     sp<DataSource> mDataSource;
     Mutex mLock;
 
@@ -102,6 +100,22 @@
     off_t mSyncSampleOffset;
     uint32_t mNumSyncSamples;
 
+    SampleIterator *mSampleIterator;
+
+    struct SampleToChunkEntry {
+        uint32_t startChunk;
+        uint32_t samplesPerChunk;
+        uint32_t chunkDesc;
+    };
+    SampleToChunkEntry *mSampleToChunkEntries;
+
+    friend struct SampleIterator;
+
+    status_t findClosestSyncSample_l(
+            uint32_t start_sample_index, uint32_t *sample_index);
+
+    status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size);
+
     SampleTable(const SampleTable &);
     SampleTable &operator=(const SampleTable &);
 };
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 8c3f252..0d617a5 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -135,50 +135,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-class BufferMeta {
-public:
-    BufferMeta(OMX *owner, const sp<IMemory> &mem, bool is_backup = false)
-        : mOwner(owner),
-          mMem(mem),
-          mIsBackup(is_backup) {
-    }
-
-    BufferMeta(OMX *owner, size_t size)
-        : mOwner(owner),
-          mSize(size),
-          mIsBackup(false) {
-    }
-
-    void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) {
-        if (!mIsBackup) {
-            return;
-        }
-
-        memcpy((OMX_U8 *)mMem->pointer() + header->nOffset,
-               header->pBuffer + header->nOffset,
-               header->nFilledLen);
-    }
-
-    void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) {
-        if (!mIsBackup) {
-            return;
-        }
-
-        memcpy(header->pBuffer + header->nOffset,
-               (const OMX_U8 *)mMem->pointer() + header->nOffset,
-               header->nFilledLen);
-    }
-
-private:
-    OMX *mOwner;
-    sp<IMemory> mMem;
-    size_t mSize;
-    bool mIsBackup;
-
-    BufferMeta(const BufferMeta &);
-    BufferMeta &operator=(const BufferMeta &);
-};
-
 OMX::OMX()
     : mMaster(new OMXMaster),
       mDispatcher(new CallbackDispatcher(this)),
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 2e23899..5b45c1c 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -586,14 +586,14 @@
 
         double r = uniform_rand();
 
-        if (r < 0.5) {
+        if (i > 0 && r < 0.5) {
             // 50% chance of just continuing to decode from last position.
 
             requestedSeekTimeUs = -1;
 
             LOGI("requesting linear read");
         } else {
-            if (r < 0.55) {
+            if (i > 0 && r < 0.55) {
                 // 5% chance of seeking beyond end of stream.
 
                 requestedSeekTimeUs = durationUs;
diff --git a/media/sdutils/sdutil.cpp b/media/sdutils/sdutil.cpp
index c77424f..322f743 100644
--- a/media/sdutils/sdutil.cpp
+++ b/media/sdutils/sdutil.cpp
@@ -86,7 +86,7 @@
 
 static int mount(const char* path) {
     String16 string(path);
-    gMountService->mountMedia(string);
+    gMountService->mountVolume(string);
     
     for (int i = 0; i < 60; i++) {
         if (isMounted(path)) {
@@ -129,6 +129,11 @@
     return 0;
 }
 
+static void asec_unmount(const char *id) {
+    String16 sId(id);
+    gMountService->unmountSecureContainer(sId);
+}
+
 static int asec_path(const char *id) {
     String16 sId(id);
     gMountService->getSecureContainerPath(sId);
@@ -137,7 +142,7 @@
 
 static int unmount(const char* path) {
     String16 string(path);
-    gMountService->unmountMedia(string);
+    gMountService->unmountVolume(string);
 
     for (int i = 0; i < 20; i++) {
         if (!isMounted(path)) {
@@ -155,7 +160,7 @@
 
     if (isMounted(path))
         return -EBUSY;
-    gMountService->formatMedia(string);
+    gMountService->formatVolume(string);
 
     return 0;
 }
@@ -208,6 +213,9 @@
             return android::asec_destroy(id);
         } else if (!strcmp(argument, "mount")) {
             return android::asec_mount(id, argv[4], atoi(argv[5]));
+        } else if (!strcmp(argument, "unmount")) {
+            android::asec_unmount(id);
+            return 0;
         } else if (!strcmp(argument, "path")) {
             return android::asec_path(id);
         }
@@ -224,6 +232,7 @@
                     "    sdutil asec finalize <id>\n"
                     "    sdutil asec destroy <id>\n"
                     "    sdutil asec mount <id> <key> <ownerUid>\n"
+                    "    sdutil asec unmount <id>\n"
                     "    sdutil asec path <id>\n"
                     );
     return -1;
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 471625e..a186e58 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -157,6 +157,7 @@
      *
      * @see #getRenderMode()
      * @see #setRenderMode(int)
+     * @see #requestRender()
      */
     public final static int RENDERMODE_WHEN_DIRTY = 0;
     /**
@@ -165,7 +166,6 @@
      *
      * @see #getRenderMode()
      * @see #setRenderMode(int)
-     * @see #requestRender()
      */
     public final static int RENDERMODE_CONTINUOUSLY = 1;
 
@@ -210,6 +210,9 @@
         // underlying surface is created and destroyed
         SurfaceHolder holder = getHolder();
         holder.addCallback(this);
+        // setType is not needed for SDK 2.0 or newer. Uncomment this
+        // statement if back-porting this code to older SDKs.
+        // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
     }
 
     /**
@@ -579,7 +582,7 @@
          * Called when the surface is created or recreated.
          * <p>
          * Called when the rendering thread
-         * starts and whenever the EGL context is lost. The context will typically
+         * starts and whenever the EGL context is lost. The EGL context will typically
          * be lost when the Android device awakes after going to sleep.
          * <p>
          * Since this method is called at the beginning of rendering, as well as
@@ -871,7 +874,7 @@
          * Initialize EGL for a given configuration spec.
          * @param configSpec
          */
-        public void start(){
+        public void start() {
             /*
              * Get an EGL instance
              */
@@ -896,8 +899,8 @@
             mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
 
             /*
-            * Create an OpenGL ES context. This must be done only once, an
-            * OpenGL context is a somewhat heavy object.
+            * Create an EGL context. We want to do this as rarely as we can, because an
+            * EGL context is a somewhat heavy object.
             */
             mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
             if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
@@ -1010,6 +1013,7 @@
         EGLSurface mEglSurface;
         EGLConfig mEglConfig;
         EGLContext mEglContext;
+
     }
 
     /**
@@ -1031,6 +1035,7 @@
             mRenderer = renderer;
         }
 
+
         @Override
         public void run() {
             setName("GLThread " + getId());
@@ -1051,20 +1056,33 @@
          * This private method should only be called inside a
          * synchronized(sGLThreadManager) block.
          */
-        private void stopEglLocked() {
-            if (mHaveEgl) {
-                mHaveEgl = false;
+        private void stopEglSurfaceLocked() {
+            if (mHaveEglSurface) {
+                mHaveEglSurface = false;
                 mEglHelper.destroySurface();
-                mEglHelper.finish();
-                sGLThreadManager.releaseEglSurfaceLocked(this);
             }
         }
 
+        /*
+         * This private method should only be called inside a
+         * synchronized(sGLThreadManager) block.
+         */
+        private void stopEglContextLocked() {
+            if (mHaveEglContext) {
+                mEglHelper.finish();
+                mHaveEglContext = false;
+                sGLThreadManager.releaseEglContextLocked(this);
+            }
+        }
         private void guardedRun() throws InterruptedException {
             mEglHelper = new EglHelper();
+            mHaveEglContext = false;
+            mHaveEglSurface = false;
             try {
                 GL10 gl = null;
+                boolean createEglContext = false;
                 boolean createEglSurface = false;
+                boolean lostEglContext = false;
                 boolean sizeChanged = false;
                 boolean wantRenderNotification = false;
                 boolean doRenderNotification = false;
@@ -1084,12 +1102,25 @@
                                 break;
                             }
 
+                            // Have we lost the EGL context?
+                            if (lostEglContext) {
+                                stopEglSurfaceLocked();
+                                stopEglContextLocked();
+                                lostEglContext = false;
+                            }
+
                             // Do we need to release the EGL surface?
-                            if (mHaveEgl && mPaused) {
+                            if (mHaveEglSurface && mPaused) {
                                 if (LOG_SURFACE) {
                                     Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
                                 }
-                                stopEglLocked();
+                                stopEglSurfaceLocked();
+                                if (sGLThreadManager.shouldReleaseEGLContextWhenPausing()) {
+                                    stopEglContextLocked();
+                                    if (LOG_SURFACE) {
+                                        Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
+                                    }
+                                }
                             }
 
                             // Have we lost the surface view surface?
@@ -1097,8 +1128,8 @@
                                 if (LOG_SURFACE) {
                                     Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
                                 }
-                                if (mHaveEgl) {
-                                    stopEglLocked();
+                                if (mHaveEglSurface) {
+                                    stopEglSurfaceLocked();
                                 }
                                 mWaitingForSurface = true;
                                 sGLThreadManager.notifyAll();
@@ -1125,16 +1156,22 @@
                                 && (mWidth > 0) && (mHeight > 0)
                                 && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {
 
-                                // If we don't have an egl surface, try to acquire one.
-                                if ((! mHaveEgl) && sGLThreadManager.tryAcquireEglSurfaceLocked(this)) {
-                                    mHaveEgl = true;
+                                // If we don't have an EGL context, try to acquire one.
+                                if ((! mHaveEglContext) && sGLThreadManager.tryAcquireEglContextLocked(this)) {
+                                    mHaveEglContext = true;
+                                    createEglContext = true;
                                     mEglHelper.start();
-                                    createEglSurface = true;
-                                    sizeChanged = true;
+
                                     sGLThreadManager.notifyAll();
                                 }
 
-                                if (mHaveEgl) {
+                                if (mHaveEglContext && !mHaveEglSurface) {
+                                    mHaveEglSurface = true;
+                                    createEglSurface = true;
+                                    sizeChanged = true;
+                                }
+
+                                if (mHaveEglSurface) {
                                     if (mSizeChanged) {
                                         sizeChanged = true;
                                         w = mWidth;
@@ -1177,10 +1214,14 @@
                         if (LOG_RENDERER) {
                             Log.w("GLThread", "onSurfaceCreated");
                         }
-                        mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
                         createEglSurface = false;
                     }
 
+                    if (createEglContext) {
+                        mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+                        createEglContext = false;
+                    }
+
                     if (sizeChanged) {
                         if (LOG_RENDERER) {
                             Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
@@ -1193,22 +1234,25 @@
                         Log.w("GLThread", "onDrawFrame");
                     }
                     mRenderer.onDrawFrame(gl);
-                    if(!mEglHelper.swap()) {
+                    if (!mEglHelper.swap()) {
                         if (LOG_SURFACE) {
-                            Log.i("GLThread", "egl surface lost tid=" + getId());
+                            Log.i("GLThread", "egl context lost tid=" + getId());
                         }
+                        lostEglContext = true;
                     }
 
                     if (wantRenderNotification) {
                         doRenderNotification = true;
                     }
                 }
+
             } finally {
                 /*
                  * clean-up everything...
                  */
                 synchronized (sGLThreadManager) {
-                    stopEglLocked();
+                    stopEglSurfaceLocked();
+                    stopEglContextLocked();
                 }
             }
         }
@@ -1338,13 +1382,15 @@
         private boolean mPaused;
         private boolean mHasSurface;
         private boolean mWaitingForSurface;
-        private boolean mHaveEgl;
+        private boolean mHaveEglContext;
+        private boolean mHaveEglSurface;
         private int mWidth;
         private int mHeight;
         private int mRenderMode;
         private boolean mRequestRender;
         private boolean mRenderComplete;
         private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
+
         // End of member variables protected by the sGLThreadManager monitor.
 
         private Renderer mRenderer;
@@ -1406,12 +1452,12 @@
 
         /*
          * Tries once to acquire the right to use an EGL
-         * surface. Does not block. Requires that we are already
+         * context. Does not block. Requires that we are already
          * in the sGLThreadManager monitor when this is called.
          *
-         * @return true if the right to use an EGL surface was acquired.
+         * @return true if the right to use an EGL context was acquired.
          */
-        public boolean tryAcquireEglSurfaceLocked(GLThread thread) {
+        public boolean tryAcquireEglContextLocked(GLThread thread) {
             if (mEglOwner == thread || mEglOwner == null) {
                 mEglOwner = thread;
                 notifyAll();
@@ -1423,17 +1469,23 @@
             }
             return false;
         }
+
         /*
-         * Releases the EGL surface. Requires that we are already in the
+         * Releases the EGL context. Requires that we are already in the
          * sGLThreadManager monitor when this is called.
          */
-        public void releaseEglSurfaceLocked(GLThread thread) {
+        public void releaseEglContextLocked(GLThread thread) {
             if (mEglOwner == thread) {
                 mEglOwner = null;
             }
             notifyAll();
         }
 
+        public synchronized boolean shouldReleaseEGLContextWhenPausing() {
+            checkGLESVersion();
+            return mMultipleGLESContextsAllowed;
+        }
+
         public synchronized void checkGLDriver(GL10 gl) {
             if (! mGLESDriverCheckComplete) {
                 checkGLESVersion();
@@ -1457,14 +1509,12 @@
                 }
                 mGLESVersionCheckComplete = true;
             }
-
         }
 
         private boolean mGLESVersionCheckComplete;
         private int mGLESVersion;
         private boolean mGLESDriverCheckComplete;
         private boolean mMultipleGLESContextsAllowed;
-        private int mGLContextCount;
         private static final int kGLES_20 = 0x20000;
         private static final String kMSM7K_RENDERER_PREFIX =
             "Q3Dimension MSM7500 ";
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
new file mode 100644
index 0000000..e13ddc8
--- /dev/null
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import com.android.common.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.app.DeviceAdmin;
+import android.app.DeviceAdminInfo;
+import android.app.DevicePolicyManager;
+import android.app.IDevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.util.Log;
+import android.util.Xml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Implementation of the device policy APIs.
+ */
+public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+    private static final String TAG = "DevicePolicyManagerService";
+    
+    private final Context mContext;
+
+    int mActivePasswordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
+    int mActivePasswordLength = 0;
+    int mFailedPasswordAttempts = 0;
+    
+    ActiveAdmin mActiveAdmin;
+    
+    static class ActiveAdmin {
+        ActiveAdmin(DeviceAdminInfo _info) {
+            info = _info;
+        }
+        
+        final DeviceAdminInfo info;
+        int getUid() { return info.getActivityInfo().applicationInfo.uid; }
+        
+        int passwordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
+        int minimumPasswordLength = 0;
+        long maximumTimeToUnlock = 0;
+    }
+    
+    /**
+     * Instantiates the service.
+     */
+    public DevicePolicyManagerService(Context context) {
+        mContext = context;
+    }
+
+    ActiveAdmin getActiveAdminForCallerLocked(ComponentName who) throws SecurityException {
+        if (mActiveAdmin != null && mActiveAdmin.getUid() == Binder.getCallingPid()) {
+            if (who != null) {
+                if (!who.getPackageName().equals(mActiveAdmin.info.getActivityInfo().packageName)
+                        || !who.getClassName().equals(mActiveAdmin.info.getActivityInfo().name)) {
+                    throw new SecurityException("Current admin is not " + who);
+                }
+            }
+            return mActiveAdmin;
+        }
+        throw new SecurityException("Current admin is not owned by uid " + Binder.getCallingUid());
+    }
+    
+    
+    void sendAdminCommandLocked(ActiveAdmin policy, String action) {
+        Intent intent = new Intent(action);
+        intent.setComponent(policy.info.getComponent());
+        mContext.sendBroadcast(intent);
+    }
+    
+    ComponentName getActiveAdminLocked() {
+        if (mActiveAdmin != null) {
+            return mActiveAdmin.info.getComponent();
+        }
+        return null;
+    }
+    
+    void removeActiveAdminLocked(ComponentName adminReceiver) {
+        ComponentName cur = getActiveAdminLocked();
+        if (cur != null && cur.equals(adminReceiver)) {
+            sendAdminCommandLocked(mActiveAdmin,
+                    DeviceAdmin.ACTION_DEVICE_ADMIN_DISABLED);
+            // XXX need to wait for it to complete.
+            mActiveAdmin = null;
+        }
+    }
+    
+    public DeviceAdminInfo findAdmin(ComponentName adminName) {
+        Intent resolveIntent = new Intent();
+        resolveIntent.setComponent(adminName);
+        List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers(
+                resolveIntent, PackageManager.GET_META_DATA);
+        if (infos == null || infos.size() <= 0) {
+            throw new IllegalArgumentException("Unknown admin: " + adminName);
+        }
+        
+        try {
+            return new DeviceAdminInfo(mContext, infos.get(0));
+        } catch (XmlPullParserException e) {
+            Log.w(TAG, "Bad device admin requested: " + adminName, e);
+            return null;
+        } catch (IOException e) {
+            Log.w(TAG, "Bad device admin requested: " + adminName, e);
+            return null;
+        }
+    }
+    
+    private static JournaledFile makeJournaledFile() {
+        final String base = "/data/system/device_policies.xml";
+        return new JournaledFile(new File(base), new File(base + ".tmp"));
+    }
+
+    private void saveSettingsLocked() {
+        JournaledFile journal = makeJournaledFile();
+        FileOutputStream stream = null;
+        try {
+            stream = new FileOutputStream(journal.chooseForWrite(), false);
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(stream, "utf-8");
+            out.startDocument(null, true);
+
+            out.startTag(null, "policies");
+            
+            ActiveAdmin ap = mActiveAdmin;
+            if (ap != null) {
+                out.startTag(null, "admin");
+                out.attribute(null, "name", ap.info.getComponent().flattenToString());
+                if (ap.passwordMode != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
+                    out.startTag(null, "password-mode");
+                    out.attribute(null, "value", Integer.toString(ap.passwordMode));
+                    out.endTag(null, "password-mode");
+                    if (ap.minimumPasswordLength > 0) {
+                        out.startTag(null, "min-password-length");
+                        out.attribute(null, "value", Integer.toString(ap.minimumPasswordLength));
+                        out.endTag(null, "mn-password-length");
+                    }
+                }
+                if (ap.maximumTimeToUnlock != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
+                    out.startTag(null, "max-time-to-unlock");
+                    out.attribute(null, "value", Long.toString(ap.maximumTimeToUnlock));
+                    out.endTag(null, "max-time-to-unlock");
+                }
+                out.endTag(null, "admin");
+            }
+            out.endTag(null, "policies");
+
+            out.endDocument();
+            stream.close();
+            journal.commit();
+        } catch (IOException e) {
+            try {
+                if (stream != null) {
+                    stream.close();
+                }
+            } catch (IOException ex) {
+                // Ignore
+            }
+            journal.rollback();
+        }
+    }
+
+    private void loadSettingsLocked() {
+        JournaledFile journal = makeJournaledFile();
+        FileInputStream stream = null;
+        File file = journal.chooseForRead();
+        boolean success = false;
+        try {
+            stream = new FileInputStream(file);
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, null);
+
+            int type = parser.next();
+            while (type != XmlPullParser.START_TAG) {
+                type = parser.next();
+            }
+            String tag = parser.getName();
+            if ("policies".equals(tag)) {
+                ActiveAdmin ap = null;
+                do {
+                    type = parser.next();
+                    if (type == XmlPullParser.START_TAG) {
+                        tag = parser.getName();
+                        if (ap == null) {
+                            if ("admin".equals(tag)) {
+                                DeviceAdminInfo dai = findAdmin(
+                                        ComponentName.unflattenFromString(
+                                                parser.getAttributeValue(null, "name")));
+                                if (dai != null) {
+                                    ap = new ActiveAdmin(dai);
+                                }
+                            }
+                        } else if ("password-mode".equals(tag)) {
+                            ap.passwordMode = Integer.parseInt(
+                                    parser.getAttributeValue(null, "value"));
+                        } else if ("min-password-length".equals(tag)) {
+                            ap.minimumPasswordLength = Integer.parseInt(
+                                    parser.getAttributeValue(null, "value"));
+                        } else if ("max-time-to-unlock".equals(tag)) {
+                            ap.maximumTimeToUnlock = Long.parseLong(
+                                    parser.getAttributeValue(null, "value"));
+                        }
+                    } else if (type == XmlPullParser.END_TAG) {
+                        tag = parser.getName();
+                        if (ap != null && "admin".equals(tag)) {
+                            mActiveAdmin = ap;
+                            ap = null;
+                        }
+                    }
+                } while (type != XmlPullParser.END_DOCUMENT);
+                success = true;
+            }
+        } catch (NullPointerException e) {
+            Log.w(TAG, "failed parsing " + file + " " + e);
+        } catch (NumberFormatException e) {
+            Log.w(TAG, "failed parsing " + file + " " + e);
+        } catch (XmlPullParserException e) {
+            Log.w(TAG, "failed parsing " + file + " " + e);
+        } catch (IOException e) {
+            Log.w(TAG, "failed parsing " + file + " " + e);
+        } catch (IndexOutOfBoundsException e) {
+            Log.w(TAG, "failed parsing " + file + " " + e);
+        }
+        try {
+            if (stream != null) {
+                stream.close();
+            }
+        } catch (IOException e) {
+            // Ignore
+        }
+
+        if (!success) {
+            Log.w(TAG, "No valid start tag found in policies file");
+        }
+    }
+
+    public void systemReady() {
+        synchronized (this) {
+            loadSettingsLocked();
+        }
+    }
+    
+    public void setActiveAdmin(ComponentName adminReceiver) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+        
+        DeviceAdminInfo info = findAdmin(adminReceiver);
+        if (info == null) {
+            throw new IllegalArgumentException("Bad admin: " + adminReceiver);
+        }
+        synchronized (this) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                ComponentName cur = getActiveAdminLocked();
+                if (cur != null && cur.equals(adminReceiver)) {
+                    throw new IllegalStateException("An admin is already set");
+                }
+                if (cur != null) {
+                    removeActiveAdminLocked(adminReceiver);
+                }
+                mActiveAdmin = new ActiveAdmin(info);
+                saveSettingsLocked();
+                sendAdminCommandLocked(mActiveAdmin,
+                        DeviceAdmin.ACTION_DEVICE_ADMIN_ENABLED);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+    
+    public ComponentName getActiveAdmin() {
+        synchronized (this) {
+            return getActiveAdminLocked();
+        }
+    }
+    
+    public void removeActiveAdmin(ComponentName adminReceiver) {
+        synchronized (this) {
+            if (mActiveAdmin == null || mActiveAdmin.getUid() != Binder.getCallingUid()) {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+            }
+            long ident = Binder.clearCallingIdentity();
+            try {
+                removeActiveAdminLocked(adminReceiver);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+    
+    public void setPasswordMode(ComponentName who, int mode) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who);
+            if (ap.passwordMode != mode) {
+                ap.passwordMode = mode;
+                saveSettingsLocked();
+            }
+        }
+    }
+    
+    public int getPasswordMode() {
+        synchronized (this) {
+            return mActiveAdmin != null ? mActiveAdmin.passwordMode
+                    : DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
+        }
+    }
+    
+    public int getActivePasswordMode() {
+        synchronized (this) {
+            // This API can only be called by an active device admin,
+            // so try to retrieve it to check that the caller is one.
+            getActiveAdminForCallerLocked(null);
+            return mActivePasswordMode;
+        }
+    }
+    
+    public void setMinimumPasswordLength(ComponentName who, int length) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who);
+            if (ap.minimumPasswordLength != length) {
+                ap.minimumPasswordLength = length;
+                saveSettingsLocked();
+            }
+        }
+    }
+    
+    public int getMinimumPasswordLength() {
+        synchronized (this) {
+            return mActiveAdmin != null ? mActiveAdmin.minimumPasswordLength : 0;
+        }
+    }
+    
+    public int getActiveMinimumPasswordLength() {
+        synchronized (this) {
+            // This API can only be called by an active device admin,
+            // so try to retrieve it to check that the caller is one.
+            getActiveAdminForCallerLocked(null);
+            return mActivePasswordLength;
+        }
+    }
+    
+    public int getCurrentFailedPasswordAttempts() {
+        synchronized (this) {
+            // This API can only be called by an active device admin,
+            // so try to retrieve it to check that the caller is one.
+            getActiveAdminForCallerLocked(null);
+            return mFailedPasswordAttempts;
+        }
+    }
+    
+    public void setMaximumTimeToLock(ComponentName who, long timeMs) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who);
+            if (ap.maximumTimeToUnlock != timeMs) {
+                ap.maximumTimeToUnlock = timeMs;
+                saveSettingsLocked();
+            }
+        }
+    }
+    
+    public long getMaximumTimeToLock() {
+        synchronized (this) {
+            return mActiveAdmin != null ? mActiveAdmin.maximumTimeToUnlock : 0;
+        }
+    }
+    
+    public void wipeData(int flags) {
+        synchronized (this) {
+            // This API can only be called by an active device admin,
+            // so try to retrieve it to check that the caller is one.
+            getActiveAdminForCallerLocked(null);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                Log.w(TAG, "*************** WIPE DATA HERE");
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+    
+    public void setActivePasswordState(int mode, int length) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+        
+        synchronized (this) {
+            if (mActivePasswordMode != mode || mActivePasswordLength != length
+                    || mFailedPasswordAttempts != 0) {
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    mActivePasswordMode = mode;
+                    mActivePasswordLength = length;
+                    mFailedPasswordAttempts = 0;
+                    sendAdminCommandLocked(mActiveAdmin,
+                            DeviceAdmin.ACTION_PASSWORD_CHANGED);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
+    }
+    
+    public void reportFailedPasswordAttempt() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+        
+        synchronized (this) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                mFailedPasswordAttempts++;
+                sendAdminCommandLocked(mActiveAdmin,
+                        DeviceAdmin.ACTION_PASSWORD_FAILED);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+    
+    public void reportSuccessfulPasswordAttempt() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+        
+        synchronized (this) {
+            if (mFailedPasswordAttempts != 0) {
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    mFailedPasswordAttempts = 0;
+                    sendAdminCommandLocked(mActiveAdmin,
+                            DeviceAdmin.ACTION_PASSWORD_SUCCEEDED);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 406897d..1c82c94 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -1203,12 +1203,10 @@
             // Remove expired alerts
             if (intentsToRemove != null) {
                 for (PendingIntent i : intentsToRemove) {
-                    mProximityAlerts.remove(i);
-                    ProximityAlert alert = mProximityAlerts.get(i);
+                    ProximityAlert alert = mProximityAlerts.remove(i);
                     mProximitiesEntered.remove(alert);
                 }
             }
-
         }
 
         // Note: this is called with the lock held.
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index c8a6915..f8f8742 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -127,6 +127,7 @@
 
     private boolean mUmsConnected = false;
     private boolean mUmsEnabled = false;
+    private boolean mUmsEnabling = false;
 
     private String  mLegacyState = Environment.MEDIA_REMOVED;
 
@@ -332,13 +333,16 @@
             String vp = Environment.getExternalStorageDirectory().getPath();
             String vs = getVolumeState(vp);
 
+            mUmsEnabling = enable;
             if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
                 unmountVolume(vp);
+                mUmsEnabling = false;
                 updateUsbMassStorageNotification(true, false);
             }
 
             setShareMethodEnabled(vp, "ums", enable);
             mUmsEnabled = enable;
+            mUmsEnabling = false;
             if (!enable) {
                 mountVolume(vp);
                 if (mPromptUms) {
@@ -594,10 +598,14 @@
         } else if (newState == VolumeState.NoMedia) {
             // NoMedia is handled via Disk Remove events
         } else if (newState == VolumeState.Idle) {
-            // Don't notify if we're in BAD_REMOVAL, NOFS, or UNMOUNTABLE
+            /*
+             * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
+             * if we're in the process of enabling UMS
+             */
             if (!vs.equals(Environment.MEDIA_BAD_REMOVAL) &&
                 !vs.equals(Environment.MEDIA_NOFS) &&
-                !vs.equals(Environment.MEDIA_UNMOUNTABLE)) {
+                !vs.equals(Environment.MEDIA_UNMOUNTABLE) &&
+                !mUmsEnabling) {
                 notifyMediaUnmounted(mountPoint);
             }
         } else if (newState == VolumeState.Pending) {
@@ -1049,6 +1057,11 @@
         return getSecureContainerPath(id);
     }
 
+    public void unmountSecureContainer(String id) throws IllegalStateException {
+        String cmd = String.format("unmount_asec %s ", id);
+        mConnector.doCommand(cmd);
+    }
+
     public String getSecureContainerPath(String id) throws IllegalStateException {
         ArrayList<String> rsp = mConnector.doCommand("asec_path " + id);
 
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
new file mode 100644
index 0000000..97fa0cc
--- /dev/null
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.INetworkManagementService;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.Log;
+import java.util.ArrayList;
+
+import android.provider.Settings;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+
+import java.io.File;
+import java.io.FileReader;
+import java.lang.IllegalStateException;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * @hide
+ */
+class NetworkManagementService extends INetworkManagementService.Stub {
+
+    private static final String TAG = "NetworkManagmentService";
+
+    class NetdResponseCode {
+        public static final int InterfaceListResult       = 110;
+        public static final int TetherInterfaceListResult = 111;
+        public static final int TetherDnsFwdTgtListResult = 112;
+
+        public static final int TetherStatusResult        = 210;
+        public static final int IpFwdStatusResult         = 211;
+    }
+
+    /**
+     * Binder context for this service
+     */
+    private Context mContext;
+
+    /**
+     * connector object for communicating with netd
+     */
+    private NativeDaemonConnector mConnector;
+
+    /**
+     * Constructs a new NetworkManagementService instance
+     *
+     * @param context  Binder context for this service
+     */
+    private NetworkManagementService(Context context) {
+        mContext = context;
+
+        mConnector = new NativeDaemonConnector(
+                new NetdCallbackReceiver(), "netd", 10, "NetdConnector");
+        Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
+        thread.start();
+    }
+
+    //
+    // Netd Callback handling
+    //
+
+    class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
+        public void onDaemonConnected() {
+            new Thread() {
+                public void run() {
+                    // XXX: Run some tests
+                }
+            }.start();
+        }
+        public boolean onEvent(int code, String raw, String[] cooked) {
+           return false;
+        }
+    }
+
+    //
+    // INetworkManagementService members
+    //
+
+    public String[] listInterfaces() throws IllegalStateException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+        ArrayList<String> rsp = mConnector.doCommand("list_interfaces");
+
+        String[] rdata = new String[rsp.size()];
+        int idx = 0;
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code == NetdResponseCode.InterfaceListResult) {
+                if (tok.length !=2) {
+                    throw new IllegalStateException(
+                            String.format("Malformatted list entry '%s'", line));
+                }
+                rdata[idx++] = tok[1];
+            } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
+                return rdata;
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+        throw new IllegalStateException("Got an empty response");
+    }
+
+    public void shutdown() {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.SHUTDOWN)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires SHUTDOWN permission");
+        }
+
+        Log.d(TAG, "Shutting down");
+    }
+
+    public boolean getIpForwardingEnabled() throws IllegalStateException{
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+        ArrayList<String> rsp = mConnector.doCommand("ipfwd status");
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code == NetdResponseCode.IpFwdStatusResult) {
+                // 211 Forwarding <enabled/disabled>
+                if (tok.length !=2) {
+                    throw new IllegalStateException(
+                            String.format("Malformatted list entry '%s'", line));
+                }
+                if (tok[2].equals("enabled"))
+                    return true;
+                return false;
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+        throw new IllegalStateException("Got an empty response");
+    }
+
+    public void setIpForwardingEnabled(boolean enable) throws IllegalStateException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+        mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
+    }
+
+    public void startTethering(String dhcpRangeStart, String dhcpRangeEnd)
+             throws IllegalStateException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+        mConnector.doCommand(String.format("tether start %s %s", dhcpRangeStart, dhcpRangeEnd));
+    }
+
+    public void stopTethering() throws IllegalStateException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+        mConnector.doCommand("tether stop");
+    }
+
+    public boolean isTetheringStarted() throws IllegalStateException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+        ArrayList<String> rsp = mConnector.doCommand("tether status");
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code == NetdResponseCode.TetherStatusResult) {
+                // XXX: Tethering services <started/stopped> <TBD>...
+                if (tok[2].equals("started"))
+                    return true;
+                return false;
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+        throw new IllegalStateException("Got an empty response");
+    }
+
+    public void tetherInterface(String iface) throws IllegalStateException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+        mConnector.doCommand("tether interface add " + iface);
+    }
+
+    public void untetherInterface(String iface) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+        mConnector.doCommand("tether interface remove " + iface);
+    }
+
+    public String[] listTetheredInterfaces() throws IllegalStateException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+        ArrayList<String> rsp = mConnector.doCommand("tether interface list");
+
+        String[] rdata = new String[rsp.size()];
+        int idx = 0;
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code == NetdResponseCode.TetherInterfaceListResult) {
+                if (tok.length !=2) {
+                    throw new IllegalStateException(
+                            String.format("Malformatted list entry '%s'", line));
+                }
+                rdata[idx++] = tok[1];
+            } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
+                return rdata;
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+        throw new IllegalStateException("Got an empty response");
+    }
+
+    public void setDnsForwarders(String[] dns) throws IllegalStateException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+        try {
+            String cmd = "tether dns set ";
+            for (String s : dns) {
+                cmd += InetAddress.getByName(s).toString() + " ";
+            }
+            mConnector.doCommand(cmd);
+        } catch (UnknownHostException e) {
+            throw new IllegalStateException("Error resolving dns name", e);
+        }
+    }
+
+    public String[] getDnsForwarders() throws IllegalStateException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+        ArrayList<String> rsp = mConnector.doCommand("tether dns list");
+
+        String[] rdata = new String[rsp.size()];
+        int idx = 0;
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code == NetdResponseCode.TetherDnsFwdTgtListResult) {
+                if (tok.length !=2) {
+                    throw new IllegalStateException(
+                            String.format("Malformatted list entry '%s'", line));
+                }
+                rdata[idx++] = tok[1];
+            } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
+                return rdata;
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+        throw new IllegalStateException("Got an empty response");
+    }
+
+    public void enableNat(String internalInterface, String externalInterface)
+            throws IllegalStateException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+        mConnector.doCommand(
+                String.format("nat enable %s %s", internalInterface, externalInterface));
+    }
+
+    public void disableNat(String internalInterface, String externalInterface)
+            throws IllegalStateException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
+        mConnector.doCommand(
+                String.format("nat disable %s %s", internalInterface, externalInterface));
+    }
+}
+
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 170477f..1b6a56a 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -75,6 +75,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.security.SystemKeyStore;
 import android.util.*;
 import android.view.Display;
 import android.view.WindowManager;
@@ -89,6 +90,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
+import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -7439,7 +7441,8 @@
 
     // ------- apps on sdcard specific code -------
     static final boolean DEBUG_SD_INSTALL = false;
-    final private String mSdEncryptKey = "none";
+    final private String mSdEncryptKey = "AppsOnSD";
+    final private String mSdEncryptAlg = "AES";
 
     private MountService getMountService() {
         return (MountService) ServiceManager.getService("mount");
@@ -7457,10 +7460,25 @@
         String cachePath = null;
         // Remove any pending destroy messages
         mHandler.removeMessages(DESTROY_SD_CONTAINER, pkgName);
+        String sdEncKey;
+        try {
+            sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(mSdEncryptKey);
+            if (sdEncKey == null) {
+                sdEncKey = SystemKeyStore.getInstance().
+                        generateNewKeyHexString(128, mSdEncryptAlg, mSdEncryptKey);
+                if (sdEncKey == null) {
+                    Log.e(TAG, "Failed to create encryption keys for package: " + pkgName + ".");
+                    return null;
+                }
+            }
+        } catch (NoSuchAlgorithmException nsae) {
+            Log.e(TAG, "Failed to create encryption keys with exception: " + nsae);
+            return null;
+        }
         try {
             cachePath = mountService.createSecureContainer(pkgName,
                 mbLen,
-                "vfat", mSdEncryptKey, Process.SYSTEM_UID);
+                "vfat", sdEncKey, Process.SYSTEM_UID);
             if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install " + pkgName + ", cachePath =" + cachePath);
             return cachePath;
         } catch(IllegalStateException e) {
@@ -7477,7 +7495,7 @@
        try {
             cachePath = mountService.createSecureContainer(pkgName,
                 mbLen,
-                "vfat", mSdEncryptKey, Process.SYSTEM_UID);
+                "vfat", sdEncKey, Process.SYSTEM_UID);
             if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install again " + pkgName + ", cachePath =" + cachePath);
             return cachePath;
         } catch(IllegalStateException e) {
@@ -7487,8 +7505,13 @@
     }
 
    private String mountSdDir(String pkgName, int ownerUid) {
+       String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(mSdEncryptKey);
+       if (sdEncKey == null) {
+           Log.e(TAG, "Failed to retrieve encryption keys to mount package code: " + pkgName + ".");
+           return null;
+       }
        try {
-           return getMountService().mountSecureContainer(pkgName, mSdEncryptKey, ownerUid);
+           return getMountService().mountSecureContainer(pkgName, sdEncKey, ownerUid);
        } catch (IllegalStateException e) {
            Log.i(TAG, "Failed to mount container for pkg : " + pkgName + " exception : " + e);
        }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 674ade9..6b3f433 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -201,6 +201,7 @@
             Log.e("System", "Failure starting core service", e);
         }
 
+        DevicePolicyManagerService devicePolicy = null;
         StatusBarService statusBar = null;
         InputMethodManagerService imm = null;
         AppWidgetService appWidget = null;
@@ -209,16 +210,25 @@
 
         if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
             try {
+                Log.i(TAG, "Device Policy");
+                devicePolicy = new DevicePolicyManagerService(context);
+                ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy);
+            } catch (Throwable e) {
+                Log.e(TAG, "Failure starting DevicePolicyService", e);
+            }
+
+            try {
                 Log.i(TAG, "Status Bar");
                 statusBar = new StatusBarService(context);
-                ServiceManager.addService("statusbar", statusBar);
+                ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
             } catch (Throwable e) {
                 Log.e(TAG, "Failure starting StatusBarService", e);
             }
 
             try {
                 Log.i(TAG, "Clipboard Service");
-                ServiceManager.addService("clipboard", new ClipboardService(context));
+                ServiceManager.addService(Context.CLIPBOARD_SERVICE,
+                        new ClipboardService(context));
             } catch (Throwable e) {
                 Log.e(TAG, "Failure starting Clipboard Service", e);
             }
@@ -280,14 +290,16 @@
 
             try {
                 Log.i(TAG, "Location Manager");
-                ServiceManager.addService(Context.LOCATION_SERVICE, new LocationManagerService(context));
+                ServiceManager.addService(Context.LOCATION_SERVICE,
+                        new LocationManagerService(context));
             } catch (Throwable e) {
                 Log.e(TAG, "Failure starting Location Manager", e);
             }
 
             try {
                 Log.i(TAG, "Search Service");
-                ServiceManager.addService( Context.SEARCH_SERVICE, new SearchManagerService(context) );
+                ServiceManager.addService(Context.SEARCH_SERVICE,
+                        new SearchManagerService(context));
             } catch (Throwable e) {
                 Log.e(TAG, "Failure starting Search Service", e);
             }
@@ -351,7 +363,8 @@
 
             try {
                 Log.i(TAG, "Backup Service");
-                ServiceManager.addService(Context.BACKUP_SERVICE, new BackupManagerService(context));
+                ServiceManager.addService(Context.BACKUP_SERVICE,
+                        new BackupManagerService(context));
             } catch (Throwable e) {
                 Log.e(TAG, "Failure starting Backup Service", e);
             }
@@ -391,6 +404,10 @@
 
         // It is now time to start up the app processes...
 
+        if (devicePolicy != null) {
+            devicePolicy.systemReady();
+        }
+
         if (notification != null) {
             notification.systemReady();
         }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 40d194c..843058c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -8381,7 +8381,7 @@
                         ActivityInfo ai = ris.get(i).activityInfo;
                         intent.setComponent(new ComponentName(ai.packageName, ai.name));
                         IIntentReceiver finisher = null;
-                        if (i == 0) {
+                        if (i == ris.size()-1) {
                             finisher = new IIntentReceiver.Stub() {
                                 public void performReceive(Intent intent, int resultCode,
                                         String data, Bundle extras, boolean ordered,
@@ -8397,7 +8397,7 @@
                         Log.i(TAG, "Sending system update to: " + intent.getComponent());
                         broadcastIntentLocked(null, null, intent, null, finisher,
                                 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID);
-                        if (i == 0) {
+                        if (finisher != null) {
                             mWaitingUpdate = true;
                         }
                     }
diff --git a/tests/AndroidTests/src/com/android/unit_tests/TimeTest.java b/tests/AndroidTests/src/com/android/unit_tests/TimeTest.java
index 3b33a99..724ef6a 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/TimeTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/TimeTest.java
@@ -359,56 +359,58 @@
         Time t = new Time(Time.TIMEZONE_UTC);
 
         t.parse3339("1980-05-23");
-        if (!t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23) {
+        if (!t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23) {
             fail("Did not parse all-day date correctly");
         }
 
         t.parse3339("1980-05-23T09:50:50");
-        if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
                 t.hour != 9 || t.minute != 50 || t.second != 50 ||
                 t.gmtoff != 0) {
             fail("Did not parse timezone-offset-less date correctly");
         }
 
         t.parse3339("1980-05-23T09:50:50Z");
-        if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
                 t.hour != 9 || t.minute != 50 || t.second != 50 ||
                 t.gmtoff != 0) {
             fail("Did not parse UTC date correctly");
         }
 
         t.parse3339("1980-05-23T09:50:50.0Z");
-        if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
                 t.hour != 9 || t.minute != 50 || t.second != 50 ||
                 t.gmtoff != 0) {
             fail("Did not parse UTC date correctly");
         }
 
         t.parse3339("1980-05-23T09:50:50.12Z");
-        if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
                 t.hour != 9 || t.minute != 50 || t.second != 50 ||
                 t.gmtoff != 0) {
             fail("Did not parse UTC date correctly");
         }
 
         t.parse3339("1980-05-23T09:50:50.123Z");
-        if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
                 t.hour != 9 || t.minute != 50 || t.second != 50 ||
                 t.gmtoff != 0) {
             fail("Did not parse UTC date correctly");
         }
 
-        t.parse3339("1980-05-23T09:50:50-06:00");
-        if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
-                t.hour != 9 || t.minute != 50 || t.second != 50 ||
-                t.gmtoff != -6*3600) {
+        // The time should be normalized to UTC
+        t.parse3339("1980-05-23T09:50:50-01:05");
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+                t.hour != 10 || t.minute != 55 || t.second != 50 ||
+                t.gmtoff != 0) {
             fail("Did not parse timezone-offset date correctly");
         }
 
-        t.parse3339("1980-05-23T09:50:50.123-06:00");
-        if (t.allDay || t.year != 1980 || t.month != 05 || t.monthDay != 23 ||
-                t.hour != 9 || t.minute != 50 || t.second != 50 ||
-                t.gmtoff != -6*3600) {
+        // The time should be normalized to UTC
+        t.parse3339("1980-05-23T09:50:50.123-01:05");
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+                t.hour != 10 || t.minute != 55 || t.second != 50 ||
+                t.gmtoff != 0) {
             fail("Did not parse timezone-offset date correctly");
         }
 
diff --git a/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
index 52286d1..31ee120 100644
--- a/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
+++ b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
@@ -24,9 +24,8 @@
 import android.os.Bundle;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
-import android.view.TransformGestureDetector;
+import android.view.ScaleGestureDetector;
 import android.view.View;
 import android.widget.LinearLayout;
 
@@ -48,9 +47,6 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
-        final LayoutInflater li = (LayoutInflater)getSystemService(
-                LAYOUT_INFLATER_SERVICE);
         
         this.setTitle(R.string.act_title);
         LinearLayout root = new LinearLayout(this);
@@ -71,15 +67,19 @@
         private float mPosY;
         private float mScale = 1.f;
         private Matrix mMatrix;
-        private TransformGestureDetector mDetector;
+        private ScaleGestureDetector mDetector;
         
-        private class Listener implements TransformGestureDetector.OnTransformGestureListener {
+        private float mLastX;
+        private float mLastY;
+        
+        private class Listener implements ScaleGestureDetector.OnScaleGestureListener {
 
-            public boolean onTransform(TransformGestureDetector detector) {
-                Log.d("ttest", "Translation: (" + detector.getTranslateX() +
-                        ", " + detector.getTranslateY() + ")");
+            public boolean onScale(ScaleGestureDetector detector) {
                 float scale = detector.getScaleFactor();
+                
                 Log.d("ttest", "Scale: " + scale);
+                
+                // Limit the scale so our object doesn't get too big or disappear
                 if (mScale * scale > 0.1f) {
                     if (mScale * scale < 10.f) {
                         mScale *= scale;
@@ -89,16 +89,13 @@
                 } else {
                     mScale = 0.1f;
                 }
-
-                mPosX += detector.getTranslateX();
-                mPosY += detector.getTranslateY();
                 
                 Log.d("ttest", "mScale: " + mScale + " mPos: (" + mPosX + ", " + mPosY + ")");
                 
                 float sizeX = mDrawable.getIntrinsicWidth()/2;
                 float sizeY = mDrawable.getIntrinsicHeight()/2;
-                float centerX = detector.getCenterX();
-                float centerY = detector.getCenterY();
+                float centerX = detector.getFocusX();
+                float centerY = detector.getFocusY();
                 float diffX = centerX - mPosX;
                 float diffY = centerY - mPosY;
                 diffX = diffX*scale - diffX;
@@ -115,24 +112,20 @@
                 return true;
             }
 
-            public boolean onTransformBegin(TransformGestureDetector detector) {
+            public boolean onScaleBegin(ScaleGestureDetector detector) {
                 return true;
             }
 
-            public boolean onTransformEnd(TransformGestureDetector detector) {
-                return true;
-            }
-
-            public boolean onTransformFling(TransformGestureDetector detector) {
-                return false;
-            }
-            
+            public void onScaleEnd(ScaleGestureDetector detector) {
+                mLastX = detector.getFocusX();
+                mLastY = detector.getFocusY();
+            }            
         }
         
         public TransformView(Context context) {
             super(context);
             mMatrix = new Matrix();
-            mDetector = new TransformGestureDetector(context, new Listener());
+            mDetector = new ScaleGestureDetector(context, new Listener());
             DisplayMetrics metrics = context.getResources().getDisplayMetrics();
             mPosX = metrics.widthPixels/2;
             mPosY = metrics.heightPixels/2;
@@ -151,12 +144,37 @@
         
         @Override
         public boolean onTouchEvent(MotionEvent event) {
-            boolean handled = mDetector.onTouchEvent(event);
+            mDetector.onTouchEvent(event);
             
-            int pointerCount = event.getPointerCount();
-            Log.d("ttest", "pointerCount: " + pointerCount);
+            // Handling single finger pan
+            if (!mDetector.isInProgress()) {
+                switch (event.getAction()) {
+                    case MotionEvent.ACTION_DOWN:
+                        mLastX = event.getX();
+                        mLastY = event.getY();
+                        break;
+                        
+                    case MotionEvent.ACTION_MOVE:
+                        final float x = event.getX();
+                        final float y = event.getY();
+                        mPosX += x - mLastX;
+                        mPosY += y - mLastY;
+                        mLastX = x;
+                        mLastY = y;
+                        
+                        float sizeX = mDrawable.getIntrinsicWidth()/2;
+                        float sizeY = mDrawable.getIntrinsicHeight()/2;
+                        
+                        mMatrix.reset();
+                        mMatrix.postTranslate(-sizeX, -sizeY);
+                        mMatrix.postScale(mScale, mScale);
+                        mMatrix.postTranslate(mPosX, mPosY);
+                        invalidate();
+                        break;
+                }
+            }
 
-            return handled;
+            return true;
         }
         
         @Override
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
index ff1b295..35f022e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
@@ -20,6 +20,7 @@
 import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 
 import javax.imageio.ImageIO;
 
@@ -33,6 +34,12 @@
         mImage = ImageIO.read(input);
     }
 
+    public Bitmap(InputStream is) throws IOException {
+        super(1, true, null, -1);
+
+        mImage = ImageIO.read(is);
+    }
+
     Bitmap(BufferedImage image) {
         super(1, true, null, -1);
         mImage = image;
@@ -237,4 +244,35 @@
         return createBitmap(colors, 0, width, width, height, config);
     }
 
+    public static Bitmap createScaledBitmap(Bitmap src, int dstWidth,
+            int dstHeight, boolean filter) {
+        Matrix m;
+        synchronized (Bitmap.class) {
+            // small pool of just 1 matrix
+            m = sScaleMatrix;
+            sScaleMatrix = null;
+        }
+
+        if (m == null) {
+            m = new Matrix();
+        }
+
+        final int width = src.getWidth();
+        final int height = src.getHeight();
+        final float sx = dstWidth  / (float)width;
+        final float sy = dstHeight / (float)height;
+        m.setScale(sx, sy);
+        Bitmap b = Bitmap.createBitmap(src, 0, 0, width, height, m, filter);
+
+        synchronized (Bitmap.class) {
+            // do we need to check for null? why not just assign everytime?
+            if (sScaleMatrix == null) {
+                sScaleMatrix = m;
+            }
+        }
+
+        return b;
+    }
+
+
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
new file mode 100644
index 0000000..e978fe8
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+
+import java.io.BufferedInputStream;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Creates Bitmap objects from various sources, including files, streams,
+ * and byte-arrays.
+ */
+public class BitmapFactory {
+    public static class Options {
+        /**
+         * Create a default Options object, which if left unchanged will give
+         * the same result from the decoder as if null were passed.
+         */
+        public Options() {
+            inDither = true;
+            inScaled = true;
+        }
+
+        /**
+         * If set to true, the decoder will return null (no bitmap), but
+         * the out... fields will still be set, allowing the caller to query
+         * the bitmap without having to allocate the memory for its pixels.
+         */
+        public boolean inJustDecodeBounds;
+
+        /**
+         * If set to a value > 1, requests the decoder to subsample the original
+         * image, returning a smaller image to save memory. The sample size is
+         * the number of pixels in either dimension that correspond to a single
+         * pixel in the decoded bitmap. For example, inSampleSize == 4 returns
+         * an image that is 1/4 the width/height of the original, and 1/16 the
+         * number of pixels. Any value <= 1 is treated the same as 1. Note: the
+         * decoder will try to fulfill this request, but the resulting bitmap
+         * may have different dimensions that precisely what has been requested.
+         * Also, powers of 2 are often faster/easier for the decoder to honor.
+         */
+        public int inSampleSize;
+
+        /**
+         * If this is non-null, the decoder will try to decode into this
+         * internal configuration. If it is null, or the request cannot be met,
+         * the decoder will try to pick the best matching config based on the
+         * system's screen depth, and characteristics of the original image such
+         * as if it has per-pixel alpha (requiring a config that also does).
+         */
+        public Bitmap.Config inPreferredConfig;
+
+        /**
+         * If dither is true, the decoder will attempt to dither the decoded
+         * image.
+         */
+        public boolean inDither;
+
+        /**
+         * The pixel density to use for the bitmap.  This will always result
+         * in the returned bitmap having a density set for it (see
+         * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)).  In addition,
+         * if {@link #inScaled} is set (which it is by default} and this
+         * density does not match {@link #inTargetDensity}, then the bitmap
+         * will be scaled to the target density before being returned.
+         *
+         * <p>If this is 0,
+         * {@link BitmapFactory#decodeResource(Resources, int)},
+         * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
+         * and {@link BitmapFactory#decodeResourceStream}
+         * will fill in the density associated with the resource.  The other
+         * functions will leave it as-is and no density will be applied.
+         *
+         * @see #inTargetDensity
+         * @see #inScreenDensity
+         * @see #inScaled
+         * @see Bitmap#setDensity(int)
+         * @see android.util.DisplayMetrics#densityDpi
+         */
+        public int inDensity;
+
+        /**
+         * The pixel density of the destination this bitmap will be drawn to.
+         * This is used in conjunction with {@link #inDensity} and
+         * {@link #inScaled} to determine if and how to scale the bitmap before
+         * returning it.
+         *
+         * <p>If this is 0,
+         * {@link BitmapFactory#decodeResource(Resources, int)},
+         * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
+         * and {@link BitmapFactory#decodeResourceStream}
+         * will fill in the density associated the Resources object's
+         * DisplayMetrics.  The other
+         * functions will leave it as-is and no scaling for density will be
+         * performed.
+         *
+         * @see #inDensity
+         * @see #inScreenDensity
+         * @see #inScaled
+         * @see android.util.DisplayMetrics#densityDpi
+         */
+        public int inTargetDensity;
+
+        /**
+         * The pixel density of the actual screen that is being used.  This is
+         * purely for applications running in density compatibility code, where
+         * {@link #inTargetDensity} is actually the density the application
+         * sees rather than the real screen density.
+         *
+         * <p>By setting this, you
+         * allow the loading code to avoid scaling a bitmap that is currently
+         * in the screen density up/down to the compatibility density.  Instead,
+         * if {@link #inDensity} is the same as {@link #inScreenDensity}, the
+         * bitmap will be left as-is.  Anything using the resulting bitmap
+         * must also used {@link Bitmap#getScaledWidth(int)
+         * Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight
+         * Bitmap.getScaledHeight} to account for any different between the
+         * bitmap's density and the target's density.
+         *
+         * <p>This is never set automatically for the caller by
+         * {@link BitmapFactory} itself.  It must be explicitly set, since the
+         * caller must deal with the resulting bitmap in a density-aware way.
+         *
+         * @see #inDensity
+         * @see #inTargetDensity
+         * @see #inScaled
+         * @see android.util.DisplayMetrics#densityDpi
+         */
+        public int inScreenDensity;
+
+        /**
+         * When this flag is set, if {@link #inDensity} and
+         * {@link #inTargetDensity} are not 0, the
+         * bitmap will be scaled to match {@link #inTargetDensity} when loaded,
+         * rather than relying on the graphics system scaling it each time it
+         * is drawn to a Canvas.
+         *
+         * <p>This flag is turned on by default and should be turned off if you need
+         * a non-scaled version of the bitmap.  Nine-patch bitmaps ignore this
+         * flag and are always scaled.
+         */
+        public boolean inScaled;
+
+        /**
+         * If this is set to true, then the resulting bitmap will allocate its
+         * pixels such that they can be purged if the system needs to reclaim
+         * memory. In that instance, when the pixels need to be accessed again
+         * (e.g. the bitmap is drawn, getPixels() is called), they will be
+         * automatically re-decoded.
+         *
+         * For the re-decode to happen, the bitmap must have access to the
+         * encoded data, either by sharing a reference to the input
+         * or by making a copy of it. This distinction is controlled by
+         * inInputShareable. If this is true, then the bitmap may keep a shallow
+         * reference to the input. If this is false, then the bitmap will
+         * explicitly make a copy of the input data, and keep that. Even if
+         * sharing is allowed, the implementation may still decide to make a
+         * deep copy of the input data.
+         */
+        public boolean inPurgeable;
+
+        /**
+         * This field works in conjuction with inPurgeable. If inPurgeable is
+         * false, then this field is ignored. If inPurgeable is true, then this
+         * field determines whether the bitmap can share a reference to the
+         * input data (inputstream, array, etc.) or if it must make a deep copy.
+         */
+        public boolean inInputShareable;
+
+        /**
+         * Normally bitmap allocations count against the dalvik heap, which
+         * means they help trigger GCs when a lot have been allocated. However,
+         * in rare cases, the caller may want to allocate the bitmap outside of
+         * that heap. To request that, set inNativeAlloc to true. In these
+         * rare instances, it is solely up to the caller to ensure that OOM is
+         * managed explicitly by calling bitmap.recycle() as soon as such a
+         * bitmap is no longer needed.
+         *
+         * @hide pending API council approval
+         */
+        public boolean inNativeAlloc;
+
+        /**
+         * The resulting width of the bitmap, set independent of the state of
+         * inJustDecodeBounds. However, if there is an error trying to decode,
+         * outWidth will be set to -1.
+         */
+        public int outWidth;
+
+        /**
+         * The resulting height of the bitmap, set independent of the state of
+         * inJustDecodeBounds. However, if there is an error trying to decode,
+         * outHeight will be set to -1.
+         */
+        public int outHeight;
+
+        /**
+         * If known, this string is set to the mimetype of the decoded image.
+         * If not know, or there is an error, it is set to null.
+         */
+        public String outMimeType;
+
+        /**
+         * Temp storage to use for decoding.  Suggest 16K or so.
+         */
+        public byte[] inTempStorage;
+
+        private native void requestCancel();
+
+        /**
+         * Flag to indicate that cancel has been called on this object.  This
+         * is useful if there's an intermediary that wants to first decode the
+         * bounds and then decode the image.  In that case the intermediary
+         * can check, inbetween the bounds decode and the image decode, to see
+         * if the operation is canceled.
+         */
+        public boolean mCancel;
+
+        /**
+         *  This can be called from another thread while this options object is
+         *  inside a decode... call. Calling this will notify the decoder that
+         *  it should cancel its operation. This is not guaranteed to cancel
+         *  the decode, but if it does, the decoder... operation will return
+         *  null, or if inJustDecodeBounds is true, will set outWidth/outHeight
+         *  to -1
+         */
+        public void requestCancelDecode() {
+            mCancel = true;
+            requestCancel();
+        }
+    }
+
+    /**
+     * Decode a file path into a bitmap. If the specified file name is null,
+     * or cannot be decoded into a bitmap, the function returns null.
+     *
+     * @param pathName complete path name for the file to be decoded.
+     * @param opts null-ok; Options that control downsampling and whether the
+     *             image should be completely decoded, or just is size returned.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded, or, if opts is non-null, if opts requested only the
+     *         size be returned (in opts.outWidth and opts.outHeight)
+     */
+    public static Bitmap decodeFile(String pathName, Options opts) {
+        Bitmap bm = null;
+        InputStream stream = null;
+        try {
+            stream = new FileInputStream(pathName);
+            bm = decodeStream(stream, null, opts);
+        } catch (Exception e) {
+            /*  do nothing.
+                If the exception happened on open, bm will be null.
+            */
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    // do nothing here
+                }
+            }
+        }
+        return bm;
+    }
+
+    /**
+     * Decode a file path into a bitmap. If the specified file name is null,
+     * or cannot be decoded into a bitmap, the function returns null.
+     *
+     * @param pathName complete path name for the file to be decoded.
+     * @return the resulting decoded bitmap, or null if it could not be decoded.
+     */
+    public static Bitmap decodeFile(String pathName) {
+        return decodeFile(pathName, null);
+    }
+
+    /**
+     * Decode a new Bitmap from an InputStream. This InputStream was obtained from
+     * resources, which we pass to be able to scale the bitmap accordingly.
+     */
+    public static Bitmap decodeResourceStream(Resources res, TypedValue value,
+            InputStream is, Rect pad, Options opts) {
+
+        if (opts == null) {
+            opts = new Options();
+        }
+
+        if (opts.inDensity == 0 && value != null) {
+            final int density = value.density;
+            if (density == TypedValue.DENSITY_DEFAULT) {
+                opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
+            } else if (density != TypedValue.DENSITY_NONE) {
+                opts.inDensity = density;
+            }
+        }
+
+        if (opts.inTargetDensity == 0 && res != null) {
+            opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
+        }
+
+        return decodeStream(is, pad, opts);
+    }
+
+    /**
+     * Synonym for opening the given resource and calling
+     * {@link #decodeResourceStream}.
+     *
+     * @param res   The resources object containing the image data
+     * @param id The resource id of the image data
+     * @param opts null-ok; Options that control downsampling and whether the
+     *             image should be completely decoded, or just is size returned.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded, or, if opts is non-null, if opts requested only the
+     *         size be returned (in opts.outWidth and opts.outHeight)
+     */
+    public static Bitmap decodeResource(Resources res, int id, Options opts) {
+        Bitmap bm = null;
+        InputStream is = null;
+
+        try {
+            final TypedValue value = new TypedValue();
+            is = res.openRawResource(id, value);
+
+            bm = decodeResourceStream(res, value, is, null, opts);
+        } catch (Exception e) {
+            /*  do nothing.
+                If the exception happened on open, bm will be null.
+                If it happened on close, bm is still valid.
+            */
+        } finally {
+            try {
+                if (is != null) is.close();
+            } catch (IOException e) {
+                // Ignore
+            }
+        }
+
+        return bm;
+    }
+
+    /**
+     * Synonym for {@link #decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}
+     * will null Options.
+     *
+     * @param res The resources object containing the image data
+     * @param id The resource id of the image data
+     * @return The decoded bitmap, or null if the image could not be decode.
+     */
+    public static Bitmap decodeResource(Resources res, int id) {
+        return decodeResource(res, id, null);
+    }
+
+    /**
+     * Decode an immutable bitmap from the specified byte array.
+     *
+     * @param data byte array of compressed image data
+     * @param offset offset into imageData for where the decoder should begin
+     *               parsing.
+     * @param length the number of bytes, beginning at offset, to parse
+     * @param opts null-ok; Options that control downsampling and whether the
+     *             image should be completely decoded, or just is size returned.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded, or, if opts is non-null, if opts requested only the
+     *         size be returned (in opts.outWidth and opts.outHeight)
+     */
+    public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
+        if ((offset | length) < 0 || data.length < offset + length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+        // FIXME: implement as needed, but it's unlikely that this is needed in the context of the bridge.
+        return null;
+        //return nativeDecodeByteArray(data, offset, length, opts);
+    }
+
+    /**
+     * Decode an immutable bitmap from the specified byte array.
+     *
+     * @param data byte array of compressed image data
+     * @param offset offset into imageData for where the decoder should begin
+     *               parsing.
+     * @param length the number of bytes, beginning at offset, to parse
+     * @return The decoded bitmap, or null if the image could not be decode.
+     */
+    public static Bitmap decodeByteArray(byte[] data, int offset, int length) {
+        return decodeByteArray(data, offset, length, null);
+    }
+
+    /**
+     * Decode an input stream into a bitmap. If the input stream is null, or
+     * cannot be used to decode a bitmap, the function returns null.
+     * The stream's position will be where ever it was after the encoded data
+     * was read.
+     *
+     * @param is The input stream that holds the raw data to be decoded into a
+     *           bitmap.
+     * @param outPadding If not null, return the padding rect for the bitmap if
+     *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If
+     *                   no bitmap is returned (null) then padding is
+     *                   unchanged.
+     * @param opts null-ok; Options that control downsampling and whether the
+     *             image should be completely decoded, or just is size returned.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded, or, if opts is non-null, if opts requested only the
+     *         size be returned (in opts.outWidth and opts.outHeight)
+     */
+    public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
+        // we don't throw in this case, thus allowing the caller to only check
+        // the cache, and not force the image to be decoded.
+        if (is == null) {
+            return null;
+        }
+
+        // we need mark/reset to work properly
+
+        if (!is.markSupported()) {
+            is = new BufferedInputStream(is, 16 * 1024);
+        }
+
+        // so we can call reset() if a given codec gives up after reading up to
+        // this many bytes. FIXME: need to find out from the codecs what this
+        // value should be.
+        is.mark(1024);
+
+        Bitmap  bm;
+
+        if (is instanceof AssetManager.AssetInputStream) {
+            // FIXME: log this.
+            return null;
+        } else {
+            // pass some temp storage down to the native code. 1024 is made up,
+            // but should be large enough to avoid too many small calls back
+            // into is.read(...) This number is not related to the value passed
+            // to mark(...) above.
+            try {
+                bm = new Bitmap(is);
+            } catch (IOException e) {
+                return null;
+            }
+        }
+
+        return finishDecode(bm, outPadding, opts);
+    }
+
+    private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
+        if (bm == null || opts == null) {
+            return bm;
+        }
+
+        final int density = opts.inDensity;
+        if (density == 0) {
+            return bm;
+        }
+
+        bm.setDensity(density);
+        final int targetDensity = opts.inTargetDensity;
+        if (targetDensity == 0 || density == targetDensity
+                || density == opts.inScreenDensity) {
+            return bm;
+        }
+
+        byte[] np = bm.getNinePatchChunk();
+        final boolean isNinePatch = false; //np != null && NinePatch.isNinePatchChunk(np);
+        if (opts.inScaled || isNinePatch) {
+            float scale = targetDensity / (float)density;
+            // TODO: This is very inefficient and should be done in native by Skia
+            final Bitmap oldBitmap = bm;
+            bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f),
+                    (int) (bm.getHeight() * scale + 0.5f), true);
+            oldBitmap.recycle();
+
+            if (isNinePatch) {
+                //np = nativeScaleNinePatch(np, scale, outPadding);
+                bm.setNinePatchChunk(np);
+            }
+            bm.setDensity(targetDensity);
+        }
+
+        return bm;
+    }
+
+    /**
+     * Decode an input stream into a bitmap. If the input stream is null, or
+     * cannot be used to decode a bitmap, the function returns null.
+     * The stream's position will be where ever it was after the encoded data
+     * was read.
+     *
+     * @param is The input stream that holds the raw data to be decoded into a
+     *           bitmap.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded, or, if opts is non-null, if opts requested only the
+     *         size be returned (in opts.outWidth and opts.outHeight)
+     */
+    public static Bitmap decodeStream(InputStream is) {
+        return decodeStream(is, null, null);
+    }
+
+    /**
+     * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
+     * return null. The position within the descriptor will not be changed when
+     * this returns, so the descriptor can be used again as-is.
+     *
+     * @param fd The file descriptor containing the bitmap data to decode
+     * @param outPadding If not null, return the padding rect for the bitmap if
+     *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If
+     *                   no bitmap is returned (null) then padding is
+     *                   unchanged.
+     * @param opts null-ok; Options that control downsampling and whether the
+     *             image should be completely decoded, or just is size returned.
+     * @return the decoded bitmap, or null
+     */
+    public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
+        return null;
+
+        /* FIXME: implement as needed
+        try {
+            if (MemoryFile.isMemoryFile(fd)) {
+                int mappedlength = MemoryFile.getMappedSize(fd);
+                MemoryFile file = new MemoryFile(fd, mappedlength, "r");
+                InputStream is = file.getInputStream();
+                Bitmap bm = decodeStream(is, outPadding, opts);
+                return finishDecode(bm, outPadding, opts);
+            }
+        } catch (IOException ex) {
+            // invalid filedescriptor, no need to call nativeDecodeFileDescriptor()
+            return null;
+        }
+        //Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
+        //return finishDecode(bm, outPadding, opts);
+        */
+    }
+
+    /**
+     * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
+     * return null. The position within the descriptor will not be changed when
+     * this returns, so the descriptor can be used again as is.
+     *
+     * @param fd The file descriptor containing the bitmap data to decode
+     * @return the decoded bitmap, or null
+     */
+    public static Bitmap decodeFileDescriptor(FileDescriptor fd) {
+        return decodeFileDescriptor(fd, null, null);
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
index 4986c77..02e3220 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
@@ -236,10 +236,15 @@
      */
     @Override
     public int save() {
+        // get the current save count
+        int count = mGraphicsStack.size();
+
+        // create a new graphics and add it to the stack
         Graphics2D g = (Graphics2D)getGraphics2d().create();
         mGraphicsStack.push(g);
 
-        return mGraphicsStack.size() - 1;
+        // return the old save count
+        return count;
     }
 
     /* (non-Javadoc)
@@ -274,10 +279,9 @@
      */
     @Override
     public int getSaveCount() {
-        return mGraphicsStack.size() - 1;
+        return mGraphicsStack.size();
     }
 
-
     /* (non-Javadoc)
      * @see android.graphics.Canvas#clipRect(float, float, float, float, android.graphics.Region.Op)
      */
@@ -953,10 +957,6 @@
      */
     @Override
     public void setMatrix(Matrix matrix) {
-        // since SetMatrix *replaces* all the other transformation, we have to restore/save
-        restore();
-        save();
-
         // get the new current graphics
         Graphics2D g = getGraphics2d();
 
@@ -968,6 +968,27 @@
         }
     }
 
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#concat(android.graphics.Matrix)
+     */
+    @Override
+    public void concat(Matrix matrix) {
+        // get the current top graphics2D object.
+        Graphics2D g = getGraphics2d();
+
+        // get its current matrix
+        AffineTransform currentTx = g.getTransform();
+        // get the AffineTransform of the given matrix
+        AffineTransform matrixTx = matrix.getTransform();
+
+        // combine them so that the given matrix is applied after.
+        currentTx.preConcatenate(matrixTx);
+
+        // give it to the graphics2D as a new matrix replacing all previous transform
+        g.setTransform(currentTx);
+    }
+
+
     // --------------------
 
     /* (non-Javadoc)
@@ -1008,15 +1029,6 @@
     }
 
     /* (non-Javadoc)
-     * @see android.graphics.Canvas#concat(android.graphics.Matrix)
-     */
-    @Override
-    public void concat(Matrix matrix) {
-        // TODO Auto-generated method stub
-        super.concat(matrix);
-    }
-
-    /* (non-Javadoc)
      * @see android.graphics.Canvas#drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint)
      */
     @Override
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix.java b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
index 3974e08..522415c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
@@ -87,8 +87,12 @@
     }
 
     public AffineTransform getTransform() {
-        return new AffineTransform(mValues[0], mValues[1], mValues[2],
-                mValues[3], mValues[4], mValues[5]);
+        // the AffineTransform constructor takes the value in a different order
+        // for a matrix [ 0 1 2 ]
+        //              [ 3 4 5 ]
+        // the order is 0, 3, 1, 4, 2, 5...
+        return new AffineTransform(mValues[0], mValues[3], mValues[1],
+                mValues[4], mValues[2], mValues[5]);
     }
 
     public boolean hasPerspective() {
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 5a13b0b..2623570 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -43,6 +43,7 @@
     public final static String[] RENAMED_CLASSES =
         new String[] {
             "android.graphics.Bitmap",              "android.graphics._Original_Bitmap",
+            "android.graphics.BitmapFactory",       "android.graphics._Original_BitmapFactory",
             "android.graphics.BitmapShader",        "android.graphics._Original_BitmapShader",
             "android.graphics.Canvas",              "android.graphics._Original_Canvas",
             "android.graphics.ComposeShader",       "android.graphics._Original_ComposeShader",