Merge "Check the boolean value for the arg to ZygoteInit to make sure it's either true or false. Make a slightly more informative usage message. Give developers a slightly easier way to have their preloaded classes list out of sync without blowing up."
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/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 4953f5d..68373cb 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -92,6 +92,11 @@
             return;
         }
 
+        if ("mountsd".equals(op)) {
+            runMountSd();
+            return;
+        }
+
         if ("uninstall".equals(op)) {
             runUninstall();
             return;
@@ -637,6 +642,37 @@
         }
     }
 
+    private void runMountSd() {
+        String opt;
+        boolean mount = false;
+        while ((opt=nextOption()) != null) {
+            if (opt.equals("-m")) {
+                String mountStr = nextOptionData();
+                if (mountStr == null) {
+                    System.err.println("Error: no value specified for -m");
+                    showUsage();
+                    return;
+                }
+                if ("true".equalsIgnoreCase(mountStr)) {
+                    mount = true;
+                } else if ("false".equalsIgnoreCase(mountStr)) {
+                    mount = false;
+                } else {
+                    System.err.println("Error: no value specified for -m");
+                    showUsage();
+                    return;
+                }
+            }
+        }
+
+        try {
+            mPm.updateExternalMediaStatus(mount);
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(PM_NOT_RUNNING_ERR);
+        }
+    }
+
     class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
         boolean finished;
         boolean result;
@@ -826,6 +862,7 @@
         System.err.println("       pm path PACKAGE");
         System.err.println("       pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] PATH");
         System.err.println("       pm uninstall [-k] PACKAGE");
+        System.err.println("       pm mountsd [-m true/false]");
         System.err.println("       pm enable PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable PACKAGE_OR_COMPONENT");
         System.err.println("");
@@ -862,6 +899,9 @@
         System.err.println("  -k: keep the data and cache directories around.");
         System.err.println("after the package removal.");
         System.err.println("");
+        System.err.println("The mountsd command simulates mounting/unmounting sdcard.Options:");
+        System.err.println("  -m: true or false.");
+        System.err.println("");
         System.err.println("The enable and disable commands change the enabled state of");
         System.err.println("a given package or component (written as \"package/class\").");
     }
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/common/java/com/android/common/DomainNameValidator.java b/common/java/com/android/common/DomainNameValidator.java
index ad44a7d..25dc007 100644
--- a/common/java/com/android/common/DomainNameValidator.java
+++ b/common/java/com/android/common/DomainNameValidator.java
@@ -166,19 +166,13 @@
                 }
             }
         } catch (CertificateParsingException e) {
-            // one way we can get here is if an alternative name starts with
-            // '*' character, which is contrary to one interpretation of the
-            // spec (a valid DNS name must start with a letter); there is no
-            // good way around this, and in order to be compatible we proceed
-            // to check the common name (ie, ignore alternative names)
-            if (LOG_ENABLED) {
-                String errorMessage = e.getMessage();
-                if (errorMessage == null) {
-                    errorMessage = "failed to parse certificate";
-                }
-
-                Log.v(TAG, "DomainNameValidator.matchDns(): " + errorMessage);
+            String errorMessage = e.getMessage();
+            if (errorMessage == null) {
+                errorMessage = "failed to parse certificate";
             }
+
+            Log.w(TAG, "DomainNameValidator.matchDns(): " + errorMessage);
+            return false;
         }
 
         if (!hasDns) {
diff --git a/common/tests/res/raw/alt_ip_only.crt b/common/tests/res/raw/alt_ip_only.crt
new file mode 100644
index 0000000..3ac9f5a
--- /dev/null
+++ b/common/tests/res/raw/alt_ip_only.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICsjCCAZqgAwIBAgIJALrC37YAXFIeMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNV
+BAYTAkpQMCAXDTEwMDExMjIxMzk0NloYDzIwNjQxMDE1MjEzOTQ2WjANMQswCQYD
+VQQGEwJKUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALr8s/4Abpby
+IYks5YCJE2nbWH7kj6XbwnRzsVP9RVC33bPoQ1M+2ZY24HqkigjQS/HEXR0s0bYh
+dewNUnTj1uGyGs6cYzsbu7x114vmVYqjxUo3hKjwfYiPeF6f3IE1vpLI7I2G32gq
+Zwm9c1/vXNHIdWQxCpFcuPA8P3YGfoApFX4pQPFplBUNAQqnjdmA68cbxxMC+1F3
+mX42D7iIEVwyVpah5HjyxjIZQlf3X7QBj0bCmkL+ibIHTALrkNNwNM6i4xzYLz/5
+14GkN9ncHY87eSOk6r53ptER6mQMhCe9qPRjSHnpWTTyj6IXTaYe+dDQw657B80w
+cSHL7Ed25zUCAwEAAaMTMBEwDwYDVR0RBAgwBocEwKgKATANBgkqhkiG9w0BAQUF
+AAOCAQEAgrwrtOWZT3fbi1AafpGaAiOBWSJqYqRhtQy0AfiZBxv1U0XaYqmZmpnq
+DVAqr0NkljowD28NBrxIFO5gBNum2ZOPDl2/5vjFn+IirUCJ9u9wS7zYkTCW2lQR
+xE7Ic3mfWv7wUbKDfjlWqP1IDHUxwkrBTAl+HnwOPiaKKk1ttwcrgS8AHlqASe03
+mlwnvJ+Stk54IneRaegL0L93sNAy63RZqnPCTxGz7eHcFwX8Jdr4sbxTxQqV6pIc
+WPjHQcWfpkFzAF5wyOq0kveVfx0g5xPhOVDd+U+q7WastbXICpCoHp9FxISmZVik
+sAyifp8agkYdzaSh55fFmKXlFnRsQw==
+-----END CERTIFICATE-----
diff --git a/common/tests/res/raw/subject_alt_only.crt b/common/tests/res/raw/subject_alt_only.crt
new file mode 100644
index 0000000..d5808fb
--- /dev/null
+++ b/common/tests/res/raw/subject_alt_only.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvTCCAaWgAwIBAgIJALbA0TZk2YmNMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNV
+BAYTAkpQMCAXDTEwMDExMjIwNTg1NFoYDzIwNjQxMDE1MjA1ODU0WjANMQswCQYD
+VQQGEwJKUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMEg6acVC9V4
+xNGoLNVLPbqBc8IvMvcsc88dF6MW3d9VagX3aeWU8c79tI/KOV/1AOakH7WYxw/w
+yD8aOX7+9BK1Hu0qKKKbSM+ycqaMthXd6xytrNDsIx5WiGUz8zTko0Gk3orIR7p7
+rPcNzB/zwtESkscqPv85aEn7S/yClNkzLfEzm3CtaYOc0tfhBMyzi/ipXzGMxUmx
+PvOLr3v/Oz5pZEQw7Kxlm4+tAtn7bJlHziQ1UW4WPIy+T3hySBEpODFiqZi7Ok3X
+Zjxdii62fgo5B2Ee7q5Amo0mUIwcQTDjJ2CLAqzYnSh3tpiPJGjEIjmRyCoMQ1bx
+7D+y7nSPIq8CAwEAAaMeMBwwGgYDVR0RBBMwEYIPd3d3LmV4YW1wbGUuY29tMA0G
+CSqGSIb3DQEBBQUAA4IBAQBsGEh+nHc0l9FJTzWqvG3qs7i6XoJZdtThCDx4HjKJ
+8GMrJtreNN4JvIxn7KC+alVbnILjzCRO+c3rsnpxKBi5cp2imjuw5Kf/x2Seimb9
+UvZbaJvBVOzy4Q1IGef9bLy3wZzy2/WfBFyvPTAkgkRaX7LN2jnYOYVhNoNFrwqe
+EWxkA6fzrpyseUEFeGFFjGxRSRCDcQ25Eq6d9rkC1x21zNtt4QwZBO0wHrTy155M
+JPRynf9244Pn0Sr/wsnmdsTRFIFYynrc51hQ7DkwbUxpcaewkZzilru/SwZ3+pPT
+9JSqm5hJ1pg5WDlPkW7c/1VA0/141N52Q8MIU+2ZpuOj
+-----END CERTIFICATE-----
diff --git a/common/tests/res/raw/subject_only.crt b/common/tests/res/raw/subject_only.crt
new file mode 100644
index 0000000..11b34e7
--- /dev/null
+++ b/common/tests/res/raw/subject_only.crt
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbmgAwIBAgIJANCQbJPPw31SMA0GCSqGSIb3DQEBBQUAMCcxCzAJBgNV
+BAYTAkpQMRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wIBcNMTAwMTEyMjA1ODE4
+WhgPMjA2NDEwMTUyMDU4MThaMCcxCzAJBgNVBAYTAkpQMRgwFgYDVQQDEw93d3cu
+ZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDsdUJk
+4KxADA3vlDHxNbyC27Ozw4yiSVzPTHUct471YmdDRW3orO2P5a5hRnUGV70gjH9X
+MU4oeOdWYAgXB9pxfLyr6621k1+uNrmaZtzp0ECH9twcwxNJJFDZsN7o9vt7V6Ej
+NN9weeqDr/aeQXo07a12vyVfR6jWO8jHB0e4aemwZNoYjNvM69fivQTse2ZoRVfj
+eSHhjRTX6I8ry4a31Hwt+fT1QiWWNN6o7+WOtpJAhX3eg4smhSD1svi2kOT8tdUe
+NS4hWlmXmumU9G4tI8PBurcLNTm7PB2lUlbn/IV18WavqKE/Uy/1WgAx+a1EJNdp
+i07AG1PsqaONKkf1AgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAJrNsuL7fZZNC8gL
+BdePJ7DYW2e7mXANU3bCBe2BZqmXKQxKwibZnEsqA+yMLqcSd8uxISlyHY2tw9wT
+4wB9KPIttfNLbwn/rk+MbOTHpvyF60d9WhJJVUkPBl8D4VuPSl+VnlA54kU9dtZN
++ZYdxYbNtSsI/Flz9SCoOV79W9GhN+uYJhv6RwyIMIHeMpZpyX1xSUVx5dZlmerQ
+WAUvghDH3fFRt2ZdnA4OXoKkTAaM3Pv7PUMsnah8bux6MQi0AuLMWFWOI1H34koH
+rs2oQLwOLnuifH52ey9+tJguabo+brlYYigAuWWFEzJfBzikDkIwnE/L7wlrypIk
+taXDWI4=
+-----END CERTIFICATE-----
diff --git a/common/tests/res/raw/subject_with_alt_names.crt b/common/tests/res/raw/subject_with_alt_names.crt
new file mode 100644
index 0000000..6963c7e
--- /dev/null
+++ b/common/tests/res/raw/subject_with_alt_names.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDBDCCAeygAwIBAgIJALv14qjcuhw9MA0GCSqGSIb3DQEBBQUAMCcxCzAJBgNV
+BAYTAkpQMRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wIBcNMTAwMTEyMjA1OTM4
+WhgPMjA2NDEwMTUyMDU5MzhaMCcxCzAJBgNVBAYTAkpQMRgwFgYDVQQDEw93d3cu
+ZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCiTVgU
+kBO9KNYZZLmiPR0eBrk8u61CLnm35BGKW8EFpDaINLbbIFIQvqOMekURON/N+xFY
+D8roo7aFZVuHWAUqFcOJ4e6NmviK5qocLihtzAexsw4f4AzZxM3A8kcLlWLyAt7e
+EVLxhcMHogY7GaF6q+33Z8p+zp6x3tj07mwyPrriCLse2PeRsRunZl/fp/VvRlr6
+YbC7CbRrhnIv5nqohs8BsbBiiFpxQftsMQmiXhY2LUzqY2RXUIOw24fHjoQkHTL2
+4z5nUM3b6ueQe+CBnobUS6fzK/36Nct4dRpev9i/ORdRLuIDKJ+QR16G1V/BJYBR
+dAK+3iXvg6z8vP1XAgMBAAGjMTAvMC0GA1UdEQQmMCSCEHd3dzIuZXhhbXBsZS5j
+b22CEHd3dzMuZXhhbXBsZS5jb20wDQYJKoZIhvcNAQEFBQADggEBAJQNf38uXm3h
+0vsF+Yd6/HqM48Su7tWnTDAfTXnQZZkzjzITq3JXzquMXICktAVN2cLnT9zPfRAE
+8V8A3BNO5zXiR5W3o/mJP5HQ3/WxpzBGM2N+YmDCJyBoQrIVaAZaXAZUaBBvn5A+
+kEVfGWquwIFuvA67xegbJOCRLD4eUzRdNsn5+NFiakWO1tkFqEzqyQ0PNPviRjgu
+z9NxdPvd1JQOhydkucsPKJzlEBbGyL5QL/Jkot3Qy+FOeuNzgQUfAGtQgzRrsZDK
+hrTVypLSoRXuTB2aWilu4p6aNh84xTdyqo2avtNr2MiQMZIcdamBq8LdBIAShFXI
+h5G2eVGXH/Y=
+-----END CERTIFICATE-----
diff --git a/common/tests/res/raw/subject_with_wild_alt_name.crt b/common/tests/res/raw/subject_with_wild_alt_name.crt
new file mode 100644
index 0000000..19b1174
--- /dev/null
+++ b/common/tests/res/raw/subject_with_wild_alt_name.crt
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8DCCAdigAwIBAgIJAL/oWJ64VAdXMA0GCSqGSIb3DQEBBQUAMCcxCzAJBgNV
+BAYTAkpQMRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wIBcNMTAwMTEyMjEwMDAx
+WhgPMjA2NDEwMTUyMTAwMDFaMCcxCzAJBgNVBAYTAkpQMRgwFgYDVQQDEw93d3cu
+ZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbx1QB
+92iea7VybLYICA4MX4LWipYrRsgXUXQrcIQ3YLTQ9rH0VwScrHL4O4JDxgXCQnR+
+4VOzD42q1KXHJAqzqGUYCNPyvZEzkGCnQ4FBIUEmxZd5SNEefJVH3Z6GizYJomTh
+p78yDcoqymD9umxRC2cWFu8GscfFGMVyhsqLlOofu7UWOs22mkXPo43jDx+VOAoV
+n48YP3P57a2Eo0gcd4zVL00y62VegqBO/1LW38aTS7teiCBFc1TkNYa5I40yN9lP
+rB9ICHYQWyzf/7OxU9iauEK2w6DmSsQoLs9JzEhgeNZddkcc77ciSUCo2Hx0VpOJ
+BFyf2rbryJeAk+FDAgMBAAGjHTAbMBkGA1UdEQQSMBCCDiouZXhhbXBsZTIuY29t
+MA0GCSqGSIb3DQEBBQUAA4IBAQA2a14pRL+4laJ8sscQlucaDB/oSdb0cwhk4IkE
+kKl/ZKr6rKwPZ81sJRgzvI4imLbUAKt4AJHdpI9cIQUq1gw9bzil7LKwmFtFSPmC
+MYb1iadaYrvp7RE4yXrWCcSbU0hup9JQLHTrHLlqLtRuU48NHMvWYThBcS9Q/hQp
+nJ/JxYy3am99MHALWLAfuRxQXhE4C5utDmBwI2KD6A8SA30s+CnuegmkYScuSqBu
+Y3R0HZvKzNIU3pwAm69HCJoG+/9MZEIDJb0WJc5UygxDT45XE9zQMQe4dBOTaNXT
++ntgaB62kE10HzrzpqXAgoAWxWK4RzFcUpBWw9qYq9xOCewJ
+-----END CERTIFICATE-----
diff --git a/common/tests/res/raw/wild_alt_name_only.crt b/common/tests/res/raw/wild_alt_name_only.crt
new file mode 100644
index 0000000..fafdebf
--- /dev/null
+++ b/common/tests/res/raw/wild_alt_name_only.crt
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICuzCCAaOgAwIBAgIJAP82tgcvmAGxMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNV
+BAYTAkpQMCAXDTEwMDExMjIxMDAyN1oYDzIwNjQxMDE1MjEwMDI3WjANMQswCQYD
+VQQGEwJKUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALs528EQbcB1
+x4BwxthQBZrgDJzoO7KPV3dhGYoeP8EnRjapZm+T/sj9P/O4HvfxjnB+fsjYSdmE
+WWUtnFrP7wtG9DUC748Ea2PMV8WFhOG58dqBNIko5XzkHB7SxkNZD5S/0KQYMGLr
+rchDsDlmsEf2Qb6qiqpNEU70aSkExZJcH+B9nWdeBpsVFu7wtezwSWEc2NUa2bhW
+gcXQ/aafwHZ4o2PyGwy0sgS/UifqO9tEllC2tPleSNJOmYsVudv5Bz4Q0GG38BSz
+Pc0IcOoln0ZWpXbGr03V2vlXWCwzaFAl3I1T3O7YVqDiaSWoP+d0tHZzmw8aJLXd
+B+KaUUGxRPsCAwEAAaMcMBowGAYDVR0RBBEwD4INKi5leGFtcGxlLmNvbTANBgkq
+hkiG9w0BAQUFAAOCAQEAJbVan4QgJ0cvpJnK9UWIVJNC+UbP87RC5go2fQiTnmGv
+prOrIuMqz1+vGcpIheLTLctJRHPoadXq0+UbQEIaU3pQbY6C4nNdfl+hcvmJeqrt
+kOCcvmIamO68iNvTSeszuHuu4O38PefrW2Xd0nn7bjFZrzBzHFhTudmnqNliP3ue
+KKQpqkUt5lCytnH8V/u/UCWdvVx5LnUa2XFGVLi3ongBIojW5fvF+yxn9ADqxdrI
+va++ow5r1VxQXFJc0ZPzsDo+6TlktoDHaRQJGMqQomqHWT4i7F5UZgf6BHGfEUPU
+qep+GsF3QRHSBtpObWkVDZNFvky3a1iZ2q25+hFIqQ==
+-----END CERTIFICATE-----
diff --git a/common/tests/src/com/android/common/DomainNameValidatorTest.java b/common/tests/src/com/android/common/DomainNameValidatorTest.java
index 4fdd4cdc..b825be4 100644
--- a/common/tests/src/com/android/common/DomainNameValidatorTest.java
+++ b/common/tests/src/com/android/common/DomainNameValidatorTest.java
@@ -15,6 +15,11 @@
  */
 package com.android.common;
 
+import com.android.common.tests.R;
+
+import android.test.AndroidTestCase;
+
+import java.io.InputStream;
 import java.math.BigInteger;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
@@ -25,6 +30,7 @@
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
 import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
@@ -37,22 +43,16 @@
 
 import javax.security.auth.x500.X500Principal;
 
-import junit.framework.TestCase;
-
-public class DomainNameValidatorTest extends TestCase {
+public class DomainNameValidatorTest extends AndroidTestCase {
     private static final int ALT_UNKNOWN = 0;
     private static final int ALT_DNS_NAME = 2;
     private static final int ALT_IPA_NAME = 7;
 
     /**
-     * Tests {@link DomainNameValidator#match}
+     * Tests {@link DomainNameValidator#match}, using a simple {@link X509Certificate}
+     * implementation.
      */
     public void testMatch() {
-        // TODO Use actual X509Certificate objects, instead of StubX509Certificate.
-        // Comment in DomainNameValidator suggests X509Certificate fails to parse a certificate
-        // if subject alternative names contain a domain name that begins with '*'.
-        // This test won't cover this kind of errors.
-
         checkMatch("11", new StubX509Certificate("cn=imap.g.com"), "imap.g.com", true);
         checkMatch("12", new StubX509Certificate("cn=imap2.g.com"), "imap.g.com", false);
         checkMatch("13", new StubX509Certificate("cn=sub.imap.g.com"), "imap.g.com", false);
@@ -74,7 +74,6 @@
                 .addSubjectAlternativeName(ALT_DNS_NAME, "*.g.com")
                 , "imap.g.com", true);
 
-
         // host name is ip address
         checkMatch("31", new StubX509Certificate("")
                 .addSubjectAlternativeName(ALT_IPA_NAME, "1.2.3.4")
@@ -170,6 +169,70 @@
     }
 
     /**
+     * Test {@link DomainNameValidator#match} with actual certificates.
+     */
+    public void testWithActualCert() throws Exception {
+        // subject_only
+        //
+        // subject: C=JP, CN=www.example.com
+        // subject alt names: n/a
+        checkWithActualCert("11", R.raw.subject_only, "www.example.com", true);
+        checkWithActualCert("12", R.raw.subject_only, "www2.example.com", false);
+
+        // subject_alt_only
+        //
+        // subject: C=JP (no CN)
+        // subject alt names: DNS:www.example.com
+        checkWithActualCert("21", R.raw.subject_alt_only, "www.example.com", true);
+        checkWithActualCert("22", R.raw.subject_alt_only, "www2.example.com", false);
+
+        // subject_with_alt_names
+        //
+        // subject: C=JP, CN=www.example.com
+        // subject alt names: DNS:www2.example.com, DNS:www3.example.com
+        // * Subject should be ignored, because it has subject alt names.
+        checkWithActualCert("31", R.raw.subject_with_alt_names, "www.example.com", false);
+        checkWithActualCert("32", R.raw.subject_with_alt_names, "www2.example.com", true);
+        checkWithActualCert("33", R.raw.subject_with_alt_names, "www3.example.com", true);
+        checkWithActualCert("34", R.raw.subject_with_alt_names, "www4.example.com", false);
+
+        // subject_with_wild_alt_name
+        //
+        // subject: C=JP, CN=www.example.com
+        // subject alt names: DNS:*.example2.com
+        // * Subject should be ignored, because it has subject alt names.
+        checkWithActualCert("41", R.raw.subject_with_wild_alt_name, "www.example.com", false);
+        checkWithActualCert("42", R.raw.subject_with_wild_alt_name, "www2.example.com", false);
+        checkWithActualCert("43", R.raw.subject_with_wild_alt_name, "www.example2.com", true);
+        checkWithActualCert("44", R.raw.subject_with_wild_alt_name, "abc.example2.com", true);
+        checkWithActualCert("45", R.raw.subject_with_wild_alt_name, "www.example3.com", false);
+
+        // wild_alt_name_only
+        //
+        // subject: C=JP
+        // subject alt names: DNS:*.example.com
+        checkWithActualCert("51", R.raw.wild_alt_name_only, "www.example.com", true);
+        checkWithActualCert("52", R.raw.wild_alt_name_only, "www2.example.com", true);
+        checkWithActualCert("53", R.raw.wild_alt_name_only, "www.example2.com", false);
+
+        // wild_alt_name_only
+        //
+        // subject: C=JP
+        // subject alt names: IP Address:192.168.10.1
+        checkWithActualCert("61", R.raw.alt_ip_only, "192.168.10.1", true);
+        checkWithActualCert("61", R.raw.alt_ip_only, "192.168.10.2", false);
+    }
+
+    private void checkWithActualCert(String message, int resId, String domain,
+            boolean expected) throws Exception {
+        CertificateFactory factory = CertificateFactory.getInstance("X509");
+        InputStream certStream = getContext().getResources().openRawResource(resId);
+        X509Certificate certificate = (X509Certificate) factory.generateCertificate(certStream);
+
+        checkMatch(message, certificate, domain, expected);
+    }
+
+    /**
      * Minimal {@link X509Certificate} implementation for {@link DomainNameValidator}.
      */
     private static class StubX509Certificate extends X509Certificate {
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/Notification.java b/core/java/android/app/Notification.java
index be5a7d3..4d72f73 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -443,11 +443,7 @@
             contentView.setTextViewText(com.android.internal.R.id.text, contentText);
         }
         if (this.when != 0) {
-            Date date = new Date(when);
-            CharSequence str = 
-                DateUtils.isToday(when) ? DateFormat.getTimeFormat(context).format(date)
-                    : DateFormat.getDateFormat(context).format(date);
-            contentView.setTextViewText(com.android.internal.R.id.time, str);
+            contentView.setLong(com.android.internal.R.id.time, "setTime", when);
         }
 
         this.contentView = contentView;
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 d6d4899..dfe1d00 100755
--- a/core/java/android/gesture/GestureUtilities.java
+++ b/core/java/android/gesture/GestureUtilities.java
@@ -27,7 +27,10 @@
 import static android.gesture.GestureConstants.*;
 
 final class GestureUtilities {
-
+  
+    private static final float SCALING_THRESHOLD = 0.26f;
+    private static final float NONUNIFORM_SCALE = (float) Math.sqrt(2);
+    
     private GestureUtilities() {
     }
 
@@ -45,64 +48,87 @@
             }
         }
     }
-
+    
     static float[] spatialSampling(Gesture gesture, int sampleMatrixDimension) {
+        return spatialSampling(gesture, sampleMatrixDimension, false);
+    }
+
+    static float[] spatialSampling(Gesture gesture, int sampleMatrixDimension, 
+            boolean uniformScaling) {
         final float targetPatchSize = sampleMatrixDimension - 1; // edge inclusive
         float[] sample = new float[sampleMatrixDimension * sampleMatrixDimension];
         Arrays.fill(sample, 0);
   
         RectF rect = gesture.getBoundingBox();
-        float sx = targetPatchSize / rect.width();
-        float sy = targetPatchSize / rect.height();
-        float scale = sx < sy ? sx : sy;
-  
+        final float gestureWidth = rect.width();
+        final float gestureHeight = rect.height();
+        float sx = targetPatchSize / gestureWidth;
+        float sy = targetPatchSize / gestureHeight;
+        
+        if (uniformScaling) {
+            float scale = sx < sy ? sx : sy;
+            sx = scale;
+            sy = scale;
+        } else {
+
+            float aspectRatio = gestureWidth / gestureHeight;
+            if (aspectRatio > 1) {
+                aspectRatio = 1 / aspectRatio;
+            }
+            if (aspectRatio < SCALING_THRESHOLD) {
+                float scale = sx < sy ? sx : sy;
+                sx = scale;
+                sy = scale;
+            } else {
+                if (sx > sy) {
+                    float scale = sy * NONUNIFORM_SCALE;
+                    if (scale < sx) {
+                        sx = scale;
+                    }
+                } else {
+                    float scale = sx * NONUNIFORM_SCALE; 
+                    if (scale < sy) {
+                        sy = scale;
+                    }
+                }
+            }
+        }
         float preDx = -rect.centerX();
         float preDy = -rect.centerY();
         float postDx = targetPatchSize / 2;
         float postDy = targetPatchSize / 2;
-  
         final ArrayList<GestureStroke> strokes = gesture.getStrokes();
         final int count = strokes.size();
-  
         int size;
         float xpos;
         float ypos;
-  
         for (int index = 0; index < count; index++) {
             final GestureStroke stroke = strokes.get(index);
             float[] strokepoints = stroke.points;
             size = strokepoints.length;
-  
             final float[] pts = new float[size];
-             
             for (int i = 0; i < size; i += 2) {
-                pts[i] = (strokepoints[i] + preDx) * scale + postDx;
-                pts[i + 1] = (strokepoints[i + 1] + preDy) * scale + postDy;
+                pts[i] = (strokepoints[i] + preDx) * sx + postDx;
+                pts[i + 1] = (strokepoints[i + 1] + preDy) * sy + postDy;
             }
-        
             float segmentEndX = -1;
             float segmentEndY = -1;
-            
             for (int i = 0; i < size; i += 2) {
-                
                 float segmentStartX = pts[i] < 0 ? 0 : pts[i];
                 float segmentStartY = pts[i + 1] < 0 ? 0 : pts[i + 1];
-                
                 if (segmentStartX > targetPatchSize) {
                     segmentStartX = targetPatchSize;
                 } 
-                
                 if (segmentStartY > targetPatchSize) {
                     segmentStartY = targetPatchSize;
                 }
-                 
                 plot(segmentStartX, segmentStartY, sample, sampleMatrixDimension);
-                
                 if (segmentEndX != -1) {
                     // evaluate horizontally
                     if (segmentEndX > segmentStartX) {
                         xpos = (float) Math.ceil(segmentStartX);
-                        float slope = (segmentEndY - segmentStartY) / (segmentEndX - segmentStartX);
+                        float slope = (segmentEndY - segmentStartY) / 
+                                      (segmentEndX - segmentStartX);
                         while (xpos < segmentEndX) {
                             ypos = slope * (xpos - segmentStartX) + segmentStartY;
                             plot(xpos, ypos, sample, sampleMatrixDimension); 
@@ -110,18 +136,19 @@
                         }
                     } else if (segmentEndX < segmentStartX){
                         xpos = (float) Math.ceil(segmentEndX);
-                        float slope = (segmentEndY - segmentStartY) / (segmentEndX - segmentStartX);
+                        float slope = (segmentEndY - segmentStartY) / 
+                                      (segmentEndX - segmentStartX);
                         while (xpos < segmentStartX) {
                             ypos = slope * (xpos - segmentStartX) + segmentStartY;
                             plot(xpos, ypos, sample, sampleMatrixDimension); 
                             xpos++;
                         }
                     }
-  
                     // evaluating vertically
                     if (segmentEndY > segmentStartY) {
                         ypos = (float) Math.ceil(segmentStartY);
-                        float invertSlope = (segmentEndX - segmentStartX) / (segmentEndY - segmentStartY);
+                        float invertSlope = (segmentEndX - segmentStartX) / 
+                                            (segmentEndY - segmentStartY);
                         while (ypos < segmentEndY) {
                             xpos = invertSlope * (ypos - segmentStartY) + segmentStartX;
                             plot(xpos, ypos, sample, sampleMatrixDimension); 
@@ -129,7 +156,8 @@
                         }
                     } else if (segmentEndY < segmentStartY) {
                         ypos = (float) Math.ceil(segmentEndY);
-                        float invertSlope = (segmentEndX - segmentStartX) / (segmentEndY - segmentStartY);
+                        float invertSlope = (segmentEndX - segmentStartX) / 
+                                            (segmentEndY - segmentStartY);
                         while (ypos < segmentStartY) {
                             xpos = invertSlope * (ypos - segmentStartY) + segmentStartX; 
                             plot(xpos, ypos, sample, sampleMatrixDimension); 
@@ -137,13 +165,10 @@
                         }
                     }
                 } 
-                
                 segmentEndX = segmentStartX;
                 segmentEndY = segmentStartY;
             }
         }
-  
-  
         return sample;
     }
   
@@ -162,40 +187,44 @@
                 sample[index] = 1;
             }
         } else {
-            double topLeft = Math.sqrt(Math.pow(xFloor - x, 2) + Math.pow(yFloor - y, 2));
-            double topRight = Math.sqrt(Math.pow(xCeiling - x, 2) + Math.pow(yFloor - y, 2));
-            double btmLeft = Math.sqrt(Math.pow(xFloor - x, 2) + Math.pow(yCeiling - y, 2));
-            double btmRight = Math.sqrt(Math.pow(xCeiling - x, 2) + Math.pow(yCeiling - y, 2));
-            double sum = topLeft + topRight + btmLeft + btmRight;
+            final double xFloorSq = Math.pow(xFloor - x, 2);
+            final double yFloorSq = Math.pow(yFloor - y, 2);
+            final double xCeilingSq = Math.pow(xCeiling - x, 2);
+            final double yCeilingSq = Math.pow(yCeiling - y, 2);
+            float topLeft = (float) Math.sqrt(xFloorSq + yFloorSq);
+            float topRight = (float) Math.sqrt(xCeilingSq + yFloorSq);
+            float btmLeft = (float) Math.sqrt(xFloorSq + yCeilingSq);
+            float btmRight = (float) Math.sqrt(xCeilingSq + yCeilingSq);
+            float sum = topLeft + topRight + btmLeft + btmRight;
             
-            double value = topLeft / sum;
+            float value = topLeft / sum;
             int index = yFloor * sampleSize + xFloor;
             if (value > sample[index]){
-                sample[index] = (float) value;
+                sample[index] = value;
             }
             
             value = topRight / sum;
             index = yFloor * sampleSize + xCeiling;
             if (value > sample[index]){
-                sample[index] = (float) value;
+                sample[index] = value;
             }
             
             value = btmLeft / sum;
             index = yCeiling * sampleSize + xFloor;
             if (value > sample[index]){
-                sample[index] = (float) value;
+                sample[index] = value;
             }
             
             value = btmRight / sum;
             index = yCeiling * sampleSize + xCeiling;
             if (value > sample[index]){
-                sample[index] = (float) value;
+                sample[index] = value;
             }
         }
     }
-    
+
     /**
-     * Featurize a stroke into a vector of a given number of elements
+     * Featurizes a stroke into a vector of a given number of elements
      * 
      * @param stroke
      * @param sampleSize
diff --git a/core/java/android/gesture/Instance.java b/core/java/android/gesture/Instance.java
index 68a2985..bb0b340 100755
--- a/core/java/android/gesture/Instance.java
+++ b/core/java/android/gesture/Instance.java
@@ -84,7 +84,7 @@
     }
 
     private static float[] spatialSampler(Gesture gesture) {
-        return GestureUtilities.spatialSampling(gesture, PATCH_SAMPLE_SIZE);
+        return GestureUtilities.spatialSampling(gesture, PATCH_SAMPLE_SIZE, false);
     }
 
     private static float[] temporalSampler(int orientationType, Gesture gesture) {
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/Browser.java b/core/java/android/provider/Browser.java
index b876f05..d67169f 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -173,9 +173,32 @@
         c.startActivity(i);
     }
 
+    /**
+     * Stores a String extra in an {@link Intent} representing the title of a
+     * page to share.  When receiving an {@link Intent#ACTION_SEND} from the
+     * Browser, use this to access the title.
+     * @hide
+     */
+    public final static String EXTRA_SHARE_TITLE = "share_title";
+
+    /**
+     * Stores a Bitmap extra in an {@link Intent} representing the screenshot of
+     * a page to share.  When receiving an {@link Intent#ACTION_SEND} from the
+     * Browser, use this to access the screenshot.
+     * @hide
+     */
+    public final static String EXTRA_SHARE_SCREENSHOT = "share_screenshot";
+
+    /**
+     * Stores a Bitmap extra in an {@link Intent} representing the favicon of a
+     * page to share.  When receiving an {@link Intent#ACTION_SEND} from the
+     * Browser, use this to access the favicon.
+     * @hide
+     */
+    public final static String EXTRA_SHARE_FAVICON = "share_favicon";
+
     public static final void sendString(Context c, String s) {
-        sendString(c, s,
-                c.getText(com.android.internal.R.string.sendText).toString());
+        sendString(c, s, c.getString(com.android.internal.R.string.sendText));
     }
 
     /**
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 93b5b4d..16746d4 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -474,18 +474,44 @@
         public static final String DISPLAY_NAME_SOURCE = "display_name_source";
 
         /**
-         * The default text shown as the contact's display name.  It is based on
-         * available data, see {@link #DISPLAY_NAME_SOURCE}.
+         * <p>
+         * The standard text shown as the contact's display name, based on the best
+         * available information for the contact (for example, it might be the email address
+         * if the name is not available).
+         * The information actually used to compute the name is stored in
+         * {@link #DISPLAY_NAME_SOURCE}.
+         * </p>
+         * <p>
+         * A contacts provider is free to choose whatever representation makes most
+         * sense for its target market.
+         * For example in the default Android Open Source Project implementation,
+         * if the display name is
+         * based on the structured name and the structured name follows
+         * the Western full-name style, then this field contains the "given name first"
+         * version of the full name.
+         * <p>
          *
          * @see ContactsContract.ContactNameColumns#DISPLAY_NAME_ALTERNATIVE
          */
         public static final String DISPLAY_NAME_PRIMARY = "display_name";
 
         /**
-         * An alternative representation of the display name.  If display name is
+         * <p>
+         * An alternative representation of the display name, such as "family name first"
+         * instead of "given name first" for Western names.  If an alternative is not
+         * available, the values should be the same as {@link #DISPLAY_NAME_PRIMARY}.
+         * </p>
+         * <p>
+         * A contacts provider is free to provide alternatives as necessary for
+         * its target market.
+         * For example the default Android Open Source Project contacts provider
+         * currently provides an
+         * alternative in a single case:  if the display name is
          * based on the structured name and the structured name follows
-         * the Western full name style, then this field contains the "family name first"
-         * version of the full name.  Otherwise, it is the same as DISPLAY_NAME_PRIMARY.
+         * the Western full name style, then the field contains the "family name first"
+         * version of the full name.
+         * Other cases may be added later.
+         * </p>
          */
         public static final String DISPLAY_NAME_ALTERNATIVE = "display_name_alt";
 
@@ -1076,6 +1102,33 @@
          * <P>Type: INTEGER</P>
          */
         public static final String DELETED = "deleted";
+
+        /**
+         * The "name_verified" flag: "1" means that the name fields on this raw
+         * contact can be trusted and therefore should be used for the entire
+         * aggregated contact.
+         * <p>
+         * If an aggregated contact contains more than one raw contact with a
+         * verified name, one of those verified names is chosen at random.
+         * If an aggregated contact contains no verified names, the
+         * name is chosen randomly from the constituent raw contacts.
+         * </p>
+         * <p>
+         * Updating this flag from "0" to "1" automatically resets it to "0" on
+         * all other raw contacts in the same aggregated contact.
+         * </p>
+         * <p>
+         * Sync adapters should only specify a value for this column when
+         * inserting a raw contact and leave it out when doing an update.
+         * </p>
+         * <p>
+         * The default value is "0"
+         * </p>
+         * <p>Type: INTEGER</p>
+         *
+         * @hide
+         */
+        public static final String NAME_VERIFIED = "name_verified";
     }
 
     /**
@@ -1239,9 +1292,6 @@
      * </dd>
      * </dl>
      * <h2>Columns</h2>
-     * TODO: include {@link #DISPLAY_NAME_PRIMARY}, {@link #DISPLAY_NAME_ALTERNATIVE},
-     * {@link #DISPLAY_NAME_SOURCE}, {@link #PHONETIC_NAME}, {@link #PHONETIC_NAME_STYLE},
-     * {@link #SORT_KEY_PRIMARY}, {@link #SORT_KEY_ALTERNATIVE}?
      *
      * <table class="jd-sumtable">
      * <tr>
@@ -1650,6 +1700,7 @@
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, CONTACT_ID);
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, STARRED);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, IS_RESTRICTED);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, NAME_VERIFIED);
                 android.content.Entity contact = new android.content.Entity(cv);
 
                 // read data rows until the contact id changes
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/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 763f273..49c2d0e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2268,7 +2268,7 @@
         addInArray(child, index);
 
         child.mParent = this;
-        child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;
+        child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK & ~DRAWING_CACHE_VALID) | DRAWN;
 
         if (child.hasFocus()) {
             requestChildFocus(child, child.findFocus());
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/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index bb9a672..d53a442 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1331,20 +1331,26 @@
         final int maxHeight = mPopup.getMaxAvailableHeight(
                 getDropDownAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations);
 
+        // getMaxAvailableHeight() subtracts the padding, so we put it back,
+        // to get the available height for the whole window
+        int padding = 0;
+        Drawable background = mPopup.getBackground();
+        if (background != null) {
+            background.getPadding(mTempRect);
+            padding = mTempRect.top + mTempRect.bottom;
+        }
+
         if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
-            // getMaxAvailableHeight() subtracts the padding, so we put it back,
-            // to get the available height for the whole window
-            int padding = 0;
-            Drawable background = mPopup.getBackground();
-            if (background != null) {
-                background.getPadding(mTempRect);
-                padding = mTempRect.top + mTempRect.bottom;
-            }
             return maxHeight + padding;
         }
 
-        return mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
-                0, ListView.NO_POSITION, maxHeight - otherHeights, 2) + otherHeights;
+        final int listContent = mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
+                0, ListView.NO_POSITION, maxHeight - otherHeights, 2);
+        // add padding only if the list has items in it, that way we don't show
+        // the popup if it is not needed
+        if (listContent > 0) otherHeights += padding;
+
+        return listContent + otherHeights;
     }
 
     private View getHintView(Context context) {
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
new file mode 100644
index 0000000..9067e26
--- /dev/null
+++ b/core/java/android/widget/DateTimeView.java
@@ -0,0 +1,258 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.BroadcastReceiver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.text.format.Time;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.widget.TextView;
+import android.widget.RemoteViews.RemoteView;
+
+import com.android.internal.R;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+//
+// TODO
+// - listen for the next threshold time to update the view.
+// - listen for date format pref changed
+// - put the AM/PM in a smaller font
+//
+
+/**
+ * Displays a given time in a convenient human-readable foramt.
+ *
+ * @hide
+ */
+@RemoteView
+public class DateTimeView extends TextView {
+    private static final String TAG = "DateTimeView";
+
+    private static final long TWELVE_HOURS_IN_MINUTES = 12 * 60;
+    private static final long TWENTY_FOUR_HOURS_IN_MILLIS = 24 * 60 * 60 * 1000;
+
+    private static final int SHOW_TIME = 0;
+    private static final int SHOW_MONTH_DAY_YEAR = 1;
+
+    Date mTime;
+    long mTimeMillis;
+
+    int mLastDisplay = -1;
+    DateFormat mLastFormat;
+
+    private boolean mAttachedToWindow;
+    private long mUpdateTimeMillis;
+
+    public DateTimeView(Context context) {
+        super(context);
+    }
+
+    public DateTimeView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onDetachedFromWindow();
+        registerReceivers();
+        mAttachedToWindow = true;
+    }
+        
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        unregisterReceivers();
+        mAttachedToWindow = false;
+    }
+
+    @android.view.RemotableViewMethod
+    public void setTime(long time) {
+        Time t = new Time();
+        t.set(time);
+        t.second = 0;
+        mTimeMillis = t.toMillis(false);
+        mTime = new Date(t.year-1900, t.month, t.monthDay, t.hour, t.minute, 0);
+        update();
+    }
+
+    void update() {
+        if (mTime == null) {
+            return;
+        }
+
+        long start = System.nanoTime();
+
+        int display;
+        Date time = mTime;
+
+        Time t = new Time();
+        t.set(mTimeMillis);
+        t.second = 0;
+
+        t.hour -= 12;
+        long twelveHoursBefore = t.toMillis(false);
+        t.hour += 12;
+        long twelveHoursAfter = t.toMillis(false);
+        t.hour = 0;
+        t.minute = 0;
+        long midnightBefore = t.toMillis(false);
+        t.monthDay++;
+        long midnightAfter = t.toMillis(false);
+
+        long nowMillis = System.currentTimeMillis();
+        t.set(nowMillis);
+        t.second = 0;
+        nowMillis = t.normalize(false);
+
+        // Choose the display mode
+        choose_display: {
+            if ((nowMillis >= midnightBefore && nowMillis < midnightAfter)
+                    || (nowMillis >= twelveHoursBefore && nowMillis < twelveHoursAfter)) {
+                display = SHOW_TIME;
+                break choose_display;
+            }
+            // Else, show month day and year.
+            display = SHOW_MONTH_DAY_YEAR;
+            break choose_display;
+        }
+
+        // Choose the format
+        DateFormat format;
+        if (display == mLastDisplay && mLastFormat != null) {
+            // use cached format
+            format = mLastFormat;
+        } else {
+            switch (display) {
+                case SHOW_TIME:
+                    format = getTimeFormat();
+                    break;
+                case SHOW_MONTH_DAY_YEAR:
+                    format = getDateFormat();
+                    break;
+                default:
+                    throw new RuntimeException("unknown display value: " + display);
+            }
+            mLastFormat = format;
+        }
+
+        // Set the text
+        String text = format.format(mTime);
+        setText(text);
+
+        // Schedule the next update
+        if (display == SHOW_TIME) {
+            // Currently showing the time, update at the later of twelve hours after or midnight.
+            mUpdateTimeMillis = twelveHoursAfter > midnightAfter ? twelveHoursAfter : midnightAfter;
+        } else {
+            // Currently showing the date
+            if (mTimeMillis < nowMillis) {
+                // If the time is in the past, don't schedule an update
+                mUpdateTimeMillis = 0;
+            } else {
+                // If hte time is in the future, schedule one at the earlier of twelve hours
+                // before or midnight before.
+                mUpdateTimeMillis = twelveHoursBefore < midnightBefore
+                        ? twelveHoursBefore : midnightBefore;
+            }
+        }
+        if (false) {
+            Log.d(TAG, "update needed for '" + time + "' at '" + new Date(mUpdateTimeMillis)
+                    + "' - text=" + text);
+        }
+
+        long finish = System.nanoTime();
+    }
+
+    private DateFormat getTimeFormat() {
+        int res;
+        Context context = getContext();
+        if (android.text.format.DateFormat.is24HourFormat(context)) {
+            res = R.string.twenty_four_hour_time_format;
+        } else {
+            res = R.string.twelve_hour_time_format;
+        }
+        String format = context.getString(res);
+        return new SimpleDateFormat(format);
+    }
+
+    private DateFormat getDateFormat() {
+        String format = Settings.System.getString(getContext().getContentResolver(),
+                Settings.System.DATE_FORMAT);
+        if ("".equals(format)) {
+            return DateFormat.getDateInstance(DateFormat.SHORT);
+        } else {
+            return new SimpleDateFormat(format);
+        }
+    }
+
+    private void registerReceivers() {
+        Context context = getContext();
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_TIME_TICK);
+        filter.addAction(Intent.ACTION_TIME_CHANGED);
+        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+        context.registerReceiver(mBroadcastReceiver, filter);
+
+        Uri uri = Settings.System.getUriFor(Settings.System.DATE_FORMAT);
+        context.getContentResolver().registerContentObserver(uri, true, mContentObserver);
+    }
+
+    private void unregisterReceivers() {
+        Context context = getContext();
+        context.unregisterReceiver(mBroadcastReceiver);
+        context.getContentResolver().unregisterContentObserver(mContentObserver);
+    }
+
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_TIME_TICK.equals(action)) {
+                if (System.currentTimeMillis() < mUpdateTimeMillis) {
+                    // The update() function takes a few milliseconds to run because of
+                    // all of the time conversions it needs to do, so we can't do that
+                    // every minute.
+                    return;
+                }
+            }
+            // ACTION_TIME_CHANGED can also signal a change of 12/24 hr. format.
+            mLastFormat = null;
+            update();
+        }
+    };
+
+    private ContentObserver mContentObserver = new ContentObserver(new Handler()) {
+        @Override
+        public void onChange(boolean selfChange) {
+            mLastFormat = null;
+            update();
+        }
+    };
+}
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/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index c63774a..401e7ff 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2781,10 +2781,10 @@
             while (first.getBottom() < listTop) {
                 AbsListView.LayoutParams layoutParams = (LayoutParams) first.getLayoutParams();
                 if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) {
-                    removeViewInLayout(first);
+                    detachViewFromParent(first);
                     recycleBin.addScrapView(first);
                 } else {
-                    detachViewFromParent(first);
+                    removeViewInLayout(first);
                 }
                 first = getChildAt(0);
                 mFirstPosition++;
@@ -2812,10 +2812,10 @@
             while (last.getTop() > listBottom) {
                 AbsListView.LayoutParams layoutParams = (LayoutParams) last.getLayoutParams();
                 if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) {
-                    removeViewInLayout(last);
+                    detachViewFromParent(last);
                     recycleBin.addScrapView(last);
                 } else {
-                    detachViewFromParent(last);
+                    removeViewInLayout(last);
                 }
                 last = getChildAt(--lastIndex);
             }
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/layout/status_bar_latest_event_content.xml b/core/res/res/layout/status_bar_latest_event_content.xml
index 2f7036f..c3aa041 100644
--- a/core/res/res/layout/status_bar_latest_event_content.xml
+++ b/core/res/res/layout/status_bar_latest_event_content.xml
@@ -45,7 +45,7 @@
             android:textSize="14sp"
             android:paddingLeft="4dp"
             />
-        <TextView android:id="@+id/time"
+        <android.widget.DateTimeView android:id="@+id/time"
             android:layout_marginLeft="4dp"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
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/docs/html/sdk/download.jd b/docs/html/sdk/download.jd
index 47505e6..029de21 100644
--- a/docs/html/sdk/download.jd
+++ b/docs/html/sdk/download.jd
@@ -58,10 +58,8 @@
   <h2>Thank you for downloading the Android SDK!</h2>
   <p>Your download should be underway. If not, <a id="click-download">click here to start the download</a>.</p>
   <p>To set up your Android development environment, please read the guide to
-    <a href="installing.html">Installing the Android SDK</a>.
-    Once you have completed the installation, see the
-    <a href="/guide/index.html">Dev Guide</a> for documentation about
-    developing Android applications.</p>
+    <a href="installing.html">Installing the Android SDK</a> and ensure that your development
+    machine meets the system requirements linked on that page.</p>
 </div>
 
 <script type="text/javascript">
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index c87007c..f935bb9 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -244,6 +244,8 @@
                 DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
                 DEVICE_OUT_BLUETOOTH_SCO_CARKIT | DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
                 DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | DEVICE_OUT_DEFAULT),
+        DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+                DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
 
         // input devices
         DEVICE_IN_COMMUNICATION = 0x10000,
diff --git a/include/media/IMediaDeathNotifier.h b/include/media/IMediaDeathNotifier.h
new file mode 100644
index 0000000..bb3d0d8
--- /dev/null
+++ b/include/media/IMediaDeathNotifier.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_IMEDIADEATHNOTIFIER_H
+#define ANDROID_IMEDIADEATHNOTIFIER_H
+
+#include <utils/threads.h>
+#include <media/IMediaPlayerService.h>
+#include <utils/SortedVector.h>
+
+namespace android {
+
+class IMediaDeathNotifier: virtual public RefBase
+{
+public:
+    IMediaDeathNotifier() { addObitRecipient(this); }
+    virtual ~IMediaDeathNotifier() { removeObitRecipient(this); }
+
+    virtual void died() = 0;
+    static const sp<IMediaPlayerService>& getMediaPlayerService();
+
+private:
+    IMediaDeathNotifier &operator=(const IMediaDeathNotifier &);
+    IMediaDeathNotifier(const IMediaDeathNotifier &);
+
+    static void addObitRecipient(const wp<IMediaDeathNotifier>& recipient);
+    static void removeObitRecipient(const wp<IMediaDeathNotifier>& recipient);
+
+    class DeathNotifier: public IBinder::DeathRecipient
+    {
+    public:
+                DeathNotifier() {}
+        virtual ~DeathNotifier();
+
+        virtual void binderDied(const wp<IBinder>& who);
+    };
+
+    friend class DeathNotifier;
+
+    static  Mutex                                   sServiceLock;
+    static  sp<IMediaPlayerService>                 sMediaPlayerService;
+    static  sp<DeathNotifier>                       sDeathNotifier;
+    static  SortedVector< wp<IMediaDeathNotifier> > sObitRecipients;
+};
+
+}; // namespace android
+
+#endif // ANDROID_IMEDIADEATHNOTIFIER_H
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 39bd5b1..d38c177 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -42,6 +42,11 @@
     typedef void *buffer_id;
     typedef void *node_id;
 
+    // Given the calling process' pid, returns true iff
+    // the implementation of the OMX interface lives in the same
+    // process.
+    virtual bool livesLocally(pid_t pid) = 0;
+
     struct ComponentInfo {
         String8 mName;
         List<String8> mRoles;
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 7132b18..87d23f6 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -21,8 +21,7 @@
 #include <ui/Surface.h>
 #include <media/IMediaPlayerClient.h>
 #include <media/IMediaPlayer.h>
-#include <media/IMediaPlayerService.h>
-#include <utils/SortedVector.h>
+#include <media/IMediaDeathNotifier.h>
 
 namespace android {
 
@@ -123,12 +122,13 @@
     virtual void notify(int msg, int ext1, int ext2) = 0;
 };
 
-class MediaPlayer : public BnMediaPlayerClient
+class MediaPlayer : public BnMediaPlayerClient,
+                    public virtual IMediaDeathNotifier
 {
 public:
     MediaPlayer();
     ~MediaPlayer();
-            void            onFirstRef();
+            void            died();
             void            disconnect();
             status_t        setDataSource(const char *url);
             status_t        setDataSource(int fd, int64_t offset, int64_t length);
@@ -164,19 +164,6 @@
             status_t        getDuration_l(int *msec);
             status_t        setDataSource(const sp<IMediaPlayer>& player);
 
-    static const sp<IMediaPlayerService>& getMediaPlayerService();
-    static void addObitRecipient(const wp<MediaPlayer>& recipient);
-    static void removeObitRecipient(const wp<MediaPlayer>& recipient);
-
-    class DeathNotifier: public IBinder::DeathRecipient
-    {
-    public:
-                DeathNotifier() {}
-        virtual ~DeathNotifier();
-
-        virtual void binderDied(const wp<IBinder>& who);
-    };
-
     sp<IMediaPlayer>            mPlayer;
     thread_id_t                 mLockThreadId;
     Mutex                       mLock;
@@ -196,13 +183,6 @@
     float                       mRightVolume;
     int                         mVideoWidth;
     int                         mVideoHeight;
-
-    friend class DeathNotifier;
-
-    static  Mutex                           sServiceLock;
-    static  sp<IMediaPlayerService>         sMediaPlayerService;
-    static  sp<DeathNotifier>               sDeathNotifier;
-    static  SortedVector< wp<MediaPlayer> > sObitRecipients;
 };
 
 }; // namespace android
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 8c7392b..9ea6c7b 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -23,6 +23,7 @@
 #include <utils/List.h>
 #include <utils/Errors.h>
 #include <media/IMediaPlayerClient.h>
+#include <media/IMediaDeathNotifier.h>
 
 namespace android {
 
@@ -145,12 +146,14 @@
     virtual void notify(int msg, int ext1, int ext2) = 0;
 };
 
-class MediaRecorder : public BnMediaPlayerClient
+class MediaRecorder : public BnMediaPlayerClient,
+                      public virtual IMediaDeathNotifier
 {
 public:
     MediaRecorder();
     ~MediaRecorder();
 
+    void        died();
     status_t    initCheck();
     status_t    setCamera(const sp<ICamera>& camera);
     status_t    setPreviewSurface(const sp<Surface>& surface);
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 2bc2734..3b21468 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -60,6 +60,7 @@
     kKeyAuthor            = 'auth',  // cstring
     kKeyCDTrackNumber     = 'cdtr',  // cstring
     kKeyDate              = 'date',  // cstring
+    kKeyWriter            = 'writ',  // cstring
 };
 
 enum {
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 351763c..2c32386 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -109,6 +109,7 @@
     };
 
     sp<IOMX> mOMX;
+    bool mOMXLivesLocally;
     IOMX::node_id mNode;
     uint32_t mQuirks;
     bool mIsEncoder;
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/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk
index f5c03bb..b68bfc1 100644
--- a/libs/audioflinger/Android.mk
+++ b/libs/audioflinger/Android.mk
@@ -47,7 +47,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=               \
-    AudioPolicyManagerGeneric.cpp
+    AudioPolicyManagerBase.cpp
 
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
@@ -60,7 +60,7 @@
  LOCAL_SHARED_LIBRARIES += libdl
 endif
 
-LOCAL_MODULE:= libaudiopolicygeneric
+LOCAL_MODULE:= libaudiopolicybase
 
 ifeq ($(BOARD_HAVE_BLUETOOTH),true)
   LOCAL_CFLAGS += -DWITH_A2DP
@@ -70,7 +70,7 @@
   LOCAL_CFLAGS += -DAUDIO_POLICY_TEST
 endif
 
-include $(BUILD_SHARED_LIBRARY)
+include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 
@@ -87,11 +87,10 @@
     libutils \
 	libbinder \
     libmedia \
-    libhardware_legacy \
-    libaudiopolicygeneric
+    libhardware_legacy
 
 ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
-  LOCAL_STATIC_LIBRARIES += libaudiointerface
+  LOCAL_STATIC_LIBRARIES += libaudiointerface libaudiopolicybase
   LOCAL_CFLAGS += -DGENERIC_AUDIO
 else
   LOCAL_SHARED_LIBRARIES += libaudio libaudiopolicy
diff --git a/libs/audioflinger/AudioPolicyManagerBase.cpp b/libs/audioflinger/AudioPolicyManagerBase.cpp
new file mode 100644
index 0000000..055dbca
--- /dev/null
+++ b/libs/audioflinger/AudioPolicyManagerBase.cpp
@@ -0,0 +1,1925 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AudioPolicyManagerBase"
+//
+#define LOG_NDEBUG 0
+#include <utils/Log.h>
+#include <hardware_legacy/AudioPolicyManagerBase.h>
+#include <media/mediarecorder.h>
+
+namespace android {
+
+
+// ----------------------------------------------------------------------------
+// AudioPolicyInterface implementation
+// ----------------------------------------------------------------------------
+
+
+status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_devices device,
+                                                  AudioSystem::device_connection_state state,
+                                                  const char *device_address)
+{
+
+    LOGV("setDeviceConnectionState() device: %x, state %d, address %s", device, state, device_address);
+
+    // connect/disconnect only 1 device at a time
+    if (AudioSystem::popCount(device) != 1) return BAD_VALUE;
+
+    if (strlen(device_address) >= MAX_DEVICE_ADDRESS_LEN) {
+        LOGE("setDeviceConnectionState() invalid address: %s", device_address);
+        return BAD_VALUE;
+    }
+
+    // handle output devices
+    if (AudioSystem::isOutputDevice(device)) {
+
+#ifndef WITH_A2DP
+        if (AudioSystem::isA2dpDevice(device)) {
+            LOGE("setDeviceConnectionState() invalid device: %x", device);
+            return BAD_VALUE;
+        }
+#endif
+
+        switch (state)
+        {
+        // handle output device connection
+        case AudioSystem::DEVICE_STATE_AVAILABLE:
+            if (mAvailableOutputDevices & device) {
+                LOGW("setDeviceConnectionState() device already connected: %x", device);
+                return INVALID_OPERATION;
+            }
+            LOGV("setDeviceConnectionState() connecting device %x", device);
+
+            // register new device as available
+            mAvailableOutputDevices |= device;
+
+#ifdef WITH_A2DP
+            // handle A2DP device connection
+            if (AudioSystem::isA2dpDevice(device)) {
+                status_t status = handleA2dpConnection(device, device_address);
+                if (status != NO_ERROR) {
+                    mAvailableOutputDevices &= ~device;
+                    return status;
+                }
+            } else
+#endif
+            {
+                if (AudioSystem::isBluetoothScoDevice(device)) {
+                    LOGV("setDeviceConnectionState() BT SCO  device, address %s", device_address);
+                    // keep track of SCO device address
+                    mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
+#ifdef WITH_A2DP
+                    if ((mA2dpDeviceAddress == mScoDeviceAddress) &&
+                        (mPhoneState != AudioSystem::MODE_NORMAL)) {
+                        mpClientInterface->suspendOutput(mA2dpOutput);
+                    }
+#endif
+                }
+            }
+            break;
+        // handle output device disconnection
+        case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
+            if (!(mAvailableOutputDevices & device)) {
+                LOGW("setDeviceConnectionState() device not connected: %x", device);
+                return INVALID_OPERATION;
+            }
+
+
+            LOGV("setDeviceConnectionState() disconnecting device %x", device);
+            // remove device from available output devices
+            mAvailableOutputDevices &= ~device;
+
+#ifdef WITH_A2DP
+            // handle A2DP device disconnection
+            if (AudioSystem::isA2dpDevice(device)) {
+                status_t status = handleA2dpDisconnection(device, device_address);
+                if (status != NO_ERROR) {
+                    mAvailableOutputDevices |= device;
+                    return status;
+                }
+            } else
+#endif
+            {
+                if (AudioSystem::isBluetoothScoDevice(device)) {
+                    mScoDeviceAddress = "";
+#ifdef WITH_A2DP
+                    if ((mA2dpDeviceAddress == mScoDeviceAddress) &&
+                        (mPhoneState != AudioSystem::MODE_NORMAL)) {
+                        mpClientInterface->restoreOutput(mA2dpOutput);
+                    }
+#endif
+                }
+            }
+            } break;
+
+        default:
+            LOGE("setDeviceConnectionState() invalid state: %x", state);
+            return BAD_VALUE;
+        }
+
+        // request routing change if necessary
+        uint32_t newDevice = getNewDevice(mHardwareOutput, false);
+#ifdef WITH_A2DP
+        checkOutputForAllStrategies(newDevice);
+        // A2DP outputs must be closed after checkOutputForAllStrategies() is executed
+        if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) {
+            closeA2dpOutputs();
+        }
+#endif
+        updateDeviceForStrategy();
+        setOutputDevice(mHardwareOutput, newDevice);
+
+        if (device == AudioSystem::DEVICE_OUT_WIRED_HEADSET) {
+            device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
+        } else if (device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO ||
+                   device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET ||
+                   device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
+            device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+        } else {
+            return NO_ERROR;
+        }
+    }
+    // handle input devices
+    if (AudioSystem::isInputDevice(device)) {
+
+        switch (state)
+        {
+        // handle input device connection
+        case AudioSystem::DEVICE_STATE_AVAILABLE: {
+            if (mAvailableInputDevices & device) {
+                LOGW("setDeviceConnectionState() device already connected: %d", device);
+                return INVALID_OPERATION;
+            }
+            mAvailableInputDevices |= device;
+            }
+            break;
+
+        // handle input device disconnection
+        case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
+            if (!(mAvailableInputDevices & device)) {
+                LOGW("setDeviceConnectionState() device not connected: %d", device);
+                return INVALID_OPERATION;
+            }
+            mAvailableInputDevices &= ~device;
+            } break;
+
+        default:
+            LOGE("setDeviceConnectionState() invalid state: %x", state);
+            return BAD_VALUE;
+        }
+
+        audio_io_handle_t activeInput = getActiveInput();
+        if (activeInput != 0) {
+            AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput);
+            uint32_t newDevice = getDeviceForInputSource(inputDesc->mInputSource);
+            if (newDevice != inputDesc->mDevice) {
+                LOGV("setDeviceConnectionState() changing device from %x to %x for input %d",
+                        inputDesc->mDevice, newDevice, activeInput);
+                inputDesc->mDevice = newDevice;
+                AudioParameter param = AudioParameter();
+                param.addInt(String8(AudioParameter::keyRouting), (int)newDevice);
+                mpClientInterface->setParameters(activeInput, param.toString());
+            }
+        }
+
+        return NO_ERROR;
+    }
+
+    LOGW("setDeviceConnectionState() invalid device: %x", device);
+    return BAD_VALUE;
+}
+
+AudioSystem::device_connection_state AudioPolicyManagerBase::getDeviceConnectionState(AudioSystem::audio_devices device,
+                                                  const char *device_address)
+{
+    AudioSystem::device_connection_state state = AudioSystem::DEVICE_STATE_UNAVAILABLE;
+    String8 address = String8(device_address);
+    if (AudioSystem::isOutputDevice(device)) {
+        if (device & mAvailableOutputDevices) {
+#ifdef WITH_A2DP
+            if (AudioSystem::isA2dpDevice(device) &&
+                address != "" && mA2dpDeviceAddress != address) {
+                return state;
+            }
+#endif
+            if (AudioSystem::isBluetoothScoDevice(device) &&
+                address != "" && mScoDeviceAddress != address) {
+                return state;
+            }
+            state = AudioSystem::DEVICE_STATE_AVAILABLE;
+        }
+    } else if (AudioSystem::isInputDevice(device)) {
+        if (device & mAvailableInputDevices) {
+            state = AudioSystem::DEVICE_STATE_AVAILABLE;
+        }
+    }
+
+    return state;
+}
+
+void AudioPolicyManagerBase::setPhoneState(int state)
+{
+    LOGV("setPhoneState() state %d", state);
+    uint32_t newDevice = 0;
+    if (state < 0 || state >= AudioSystem::NUM_MODES) {
+        LOGW("setPhoneState() invalid state %d", state);
+        return;
+    }
+
+    if (state == mPhoneState ) {
+        LOGW("setPhoneState() setting same state %d", state);
+        return;
+    }
+
+    // if leaving call state, handle special case of active streams
+    // pertaining to sonification strategy see handleIncallSonification()
+    if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+        LOGV("setPhoneState() in call state management: new state is %d", state);
+        for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+            handleIncallSonification(stream, false, true);
+        }
+    }
+
+    // store previous phone state for management of sonification strategy below
+    int oldState = mPhoneState;
+    mPhoneState = state;
+    bool force = false;
+
+    // are we entering or starting a call
+    if ((oldState != AudioSystem::MODE_IN_CALL) && (state == AudioSystem::MODE_IN_CALL)) {
+        LOGV("  Entering call in setPhoneState()");
+        // force routing command to audio hardware when starting a call
+        // even if no device change is needed
+        force = true;
+    } else if ((oldState == AudioSystem::MODE_IN_CALL) && (state != AudioSystem::MODE_IN_CALL)) {
+        LOGV("  Exiting call in setPhoneState()");
+        // force routing command to audio hardware when exiting a call
+        // even if no device change is needed
+        force = true;
+    }
+
+    // check for device and output changes triggered by new phone state
+    newDevice = getNewDevice(mHardwareOutput, false);
+#ifdef WITH_A2DP
+    checkOutputForAllStrategies(newDevice);
+    // suspend A2DP output if SCO device address is the same as A2DP device address.
+    // no need to check that a SCO device is actually connected as mScoDeviceAddress == ""
+    // if none is connected and the test below will fail.
+    if (mA2dpDeviceAddress == mScoDeviceAddress) {
+        if (oldState == AudioSystem::MODE_NORMAL) {
+            mpClientInterface->suspendOutput(mA2dpOutput);
+        } else if (state == AudioSystem::MODE_NORMAL) {
+            mpClientInterface->restoreOutput(mA2dpOutput);
+        }
+    }
+#endif
+    updateDeviceForStrategy();
+
+    AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
+
+    // force routing command to audio hardware when ending call
+    // even if no device change is needed
+    if (oldState == AudioSystem::MODE_IN_CALL && newDevice == 0) {
+        newDevice = hwOutputDesc->device();
+    }
+    // change routing is necessary
+    setOutputDevice(mHardwareOutput, newDevice, force);
+
+    // if entering in call state, handle special case of active streams
+    // pertaining to sonification strategy see handleIncallSonification()
+    if (state == AudioSystem::MODE_IN_CALL) {
+        LOGV("setPhoneState() in call state management: new state is %d", state);
+        for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+            handleIncallSonification(stream, true, true);
+        }
+    }
+
+    // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE
+    if (state == AudioSystem::MODE_RINGTONE &&
+        (hwOutputDesc->mRefCount[AudioSystem::MUSIC] ||
+        (systemTime() - mMusicStopTime) < seconds(SONIFICATION_HEADSET_MUSIC_DELAY))) {
+        mLimitRingtoneVolume = true;
+    } else {
+        mLimitRingtoneVolume = false;
+    }
+}
+
+void AudioPolicyManagerBase::setRingerMode(uint32_t mode, uint32_t mask)
+{
+    LOGV("setRingerMode() mode %x, mask %x", mode, mask);
+
+    mRingerMode = mode;
+}
+
+void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
+{
+    LOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState);
+
+    switch(usage) {
+    case AudioSystem::FOR_COMMUNICATION:
+        if (config != AudioSystem::FORCE_SPEAKER && config != AudioSystem::FORCE_BT_SCO &&
+            config != AudioSystem::FORCE_NONE) {
+            LOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config);
+            return;
+        }
+        mForceUse[usage] = config;
+        break;
+    case AudioSystem::FOR_MEDIA:
+        if (config != AudioSystem::FORCE_HEADPHONES && config != AudioSystem::FORCE_BT_A2DP &&
+            config != AudioSystem::FORCE_WIRED_ACCESSORY && config != AudioSystem::FORCE_NONE) {
+            LOGW("setForceUse() invalid config %d for FOR_MEDIA", config);
+            return;
+        }
+        mForceUse[usage] = config;
+        break;
+    case AudioSystem::FOR_RECORD:
+        if (config != AudioSystem::FORCE_BT_SCO && config != AudioSystem::FORCE_WIRED_ACCESSORY &&
+            config != AudioSystem::FORCE_NONE) {
+            LOGW("setForceUse() invalid config %d for FOR_RECORD", config);
+            return;
+        }
+        mForceUse[usage] = config;
+        break;
+    case AudioSystem::FOR_DOCK:
+        if (config != AudioSystem::FORCE_NONE && config != AudioSystem::FORCE_BT_CAR_DOCK &&
+            config != AudioSystem::FORCE_BT_DESK_DOCK && config != AudioSystem::FORCE_WIRED_ACCESSORY) {
+            LOGW("setForceUse() invalid config %d for FOR_DOCK", config);
+        }
+        mForceUse[usage] = config;
+        break;
+    default:
+        LOGW("setForceUse() invalid usage %d", usage);
+        break;
+    }
+
+    // check for device and output changes triggered by new phone state
+    uint32_t newDevice = getNewDevice(mHardwareOutput, false);
+#ifdef WITH_A2DP
+    checkOutputForAllStrategies(newDevice);
+#endif
+    updateDeviceForStrategy();
+    setOutputDevice(mHardwareOutput, newDevice);
+}
+
+AudioSystem::forced_config AudioPolicyManagerBase::getForceUse(AudioSystem::force_use usage)
+{
+    return mForceUse[usage];
+}
+
+void AudioPolicyManagerBase::setSystemProperty(const char* property, const char* value)
+{
+    LOGV("setSystemProperty() property %s, value %s", property, value);
+    if (strcmp(property, "ro.camera.sound.forced") == 0) {
+        if (atoi(value)) {
+            LOGV("ENFORCED_AUDIBLE cannot be muted");
+            mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = false;
+        } else {
+            LOGV("ENFORCED_AUDIBLE can be muted");
+            mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = true;
+        }
+    }
+}
+
+audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_type stream,
+                                    uint32_t samplingRate,
+                                    uint32_t format,
+                                    uint32_t channels,
+                                    AudioSystem::output_flags flags)
+{
+    audio_io_handle_t output = 0;
+    uint32_t latency = 0;
+    routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
+    uint32_t device = getDeviceForStrategy(strategy);
+    LOGV("getOutput() stream %d, samplingRate %d, format %d, channels %x, flags %x", stream, samplingRate, format, channels, flags);
+
+#ifdef AUDIO_POLICY_TEST
+    if (mCurOutput != 0) {
+        LOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channels %x, mDirectOutput %d",
+                mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput);
+
+        if (mTestOutputs[mCurOutput] == 0) {
+            LOGV("getOutput() opening test output");
+            AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+            outputDesc->mDevice = mTestDevice;
+            outputDesc->mSamplingRate = mTestSamplingRate;
+            outputDesc->mFormat = mTestFormat;
+            outputDesc->mChannels = mTestChannels;
+            outputDesc->mLatency = mTestLatencyMs;
+            outputDesc->mFlags = (AudioSystem::output_flags)(mDirectOutput ? AudioSystem::OUTPUT_FLAG_DIRECT : 0);
+            outputDesc->mRefCount[stream] = 0;
+            mTestOutputs[mCurOutput] = mpClientInterface->openOutput(&outputDesc->mDevice,
+                                            &outputDesc->mSamplingRate,
+                                            &outputDesc->mFormat,
+                                            &outputDesc->mChannels,
+                                            &outputDesc->mLatency,
+                                            outputDesc->mFlags);
+            if (mTestOutputs[mCurOutput]) {
+                AudioParameter outputCmd = AudioParameter();
+                outputCmd.addInt(String8("set_id"),mCurOutput);
+                mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString());
+                addOutput(mTestOutputs[mCurOutput], outputDesc);
+            }
+        }
+        return mTestOutputs[mCurOutput];
+    }
+#endif //AUDIO_POLICY_TEST
+
+    // open a direct output if:
+    // 1 a direct output is explicitely requested
+    // 2 the audio format is compressed
+    if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
+         (format !=0 && !AudioSystem::isLinearPCM(format))) {
+
+        LOGV("getOutput() opening direct output device %x", device);
+        AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+        outputDesc->mDevice = device;
+        outputDesc->mSamplingRate = samplingRate;
+        outputDesc->mFormat = format;
+        outputDesc->mChannels = channels;
+        outputDesc->mLatency = 0;
+        outputDesc->mFlags = (AudioSystem::output_flags)(flags | AudioSystem::OUTPUT_FLAG_DIRECT);
+        outputDesc->mRefCount[stream] = 1;
+        output = mpClientInterface->openOutput(&outputDesc->mDevice,
+                                        &outputDesc->mSamplingRate,
+                                        &outputDesc->mFormat,
+                                        &outputDesc->mChannels,
+                                        &outputDesc->mLatency,
+                                        outputDesc->mFlags);
+
+        // only accept an output with the requeted parameters
+        if (output == 0 ||
+            (samplingRate != 0 && samplingRate != outputDesc->mSamplingRate) ||
+            (format != 0 && format != outputDesc->mFormat) ||
+            (channels != 0 && channels != outputDesc->mChannels)) {
+            LOGV("getOutput() failed opening direct output: samplingRate %d, format %d, channels %d",
+                    samplingRate, format, channels);
+            if (output != 0) {
+                mpClientInterface->closeOutput(output);
+            }
+            delete outputDesc;
+            return 0;
+        }
+        addOutput(output, outputDesc);
+        return output;
+    }
+
+    if (channels != 0 && channels != AudioSystem::CHANNEL_OUT_MONO &&
+        channels != AudioSystem::CHANNEL_OUT_STEREO) {
+        return 0;
+    }
+    // open a non direct output
+
+    // get which output is suitable for the specified stream. The actual routing change will happen
+    // when startOutput() will be called
+    uint32_t a2dpDevice = device & AudioSystem::DEVICE_OUT_ALL_A2DP;
+    if (AudioSystem::popCount((AudioSystem::audio_devices)device) == 2) {
+#ifdef WITH_A2DP
+        if (a2dpUsedForSonification() && a2dpDevice != 0) {
+            // if playing on 2 devices among which one is A2DP, use duplicated output
+            LOGV("getOutput() using duplicated output");
+            LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device in multiple %x selected but A2DP output not opened", device);
+            output = mDuplicatedOutput;
+        } else
+#endif
+        {
+            // if playing on 2 devices among which none is A2DP, use hardware output
+            output = mHardwareOutput;
+        }
+        LOGV("getOutput() using output %d for 2 devices %x", output, device);
+    } else {
+#ifdef WITH_A2DP
+        if (a2dpDevice != 0) {
+            // if playing on A2DP device, use a2dp output
+            LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device %x selected but A2DP output not opened", device);
+            output = mA2dpOutput;
+        } else
+#endif
+        {
+            // if playing on not A2DP device, use hardware output
+            output = mHardwareOutput;
+        }
+    }
+
+
+    LOGW_IF((output ==0), "getOutput() could not find output for stream %d, samplingRate %d, format %d, channels %x, flags %x",
+                stream, samplingRate, format, channels, flags);
+
+    return output;
+}
+
+status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+    LOGV("startOutput() output %d, stream %d", output, stream);
+    ssize_t index = mOutputs.indexOfKey(output);
+    if (index < 0) {
+        LOGW("startOutput() unknow output %d", output);
+        return BAD_VALUE;
+    }
+
+    AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+    routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
+
+#ifdef WITH_A2DP
+    if (mA2dpOutput != 0  && !a2dpUsedForSonification() && strategy == STRATEGY_SONIFICATION) {
+        setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
+    }
+#endif
+
+    // incremenent usage count for this stream on the requested output:
+    // NOTE that the usage count is the same for duplicated output and hardware output which is
+    // necassary for a correct control of hardware output routing by startOutput() and stopOutput()
+    outputDesc->changeRefCount(stream, 1);
+
+    setOutputDevice(output, getNewDevice(output));
+
+    // handle special case for sonification while in call
+    if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+        handleIncallSonification(stream, true, false);
+    }
+
+    // apply volume rules for current stream and device if necessary
+    checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, outputDesc->device());
+
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+    LOGV("stopOutput() output %d, stream %d", output, stream);
+    ssize_t index = mOutputs.indexOfKey(output);
+    if (index < 0) {
+        LOGW("stopOutput() unknow output %d", output);
+        return BAD_VALUE;
+    }
+
+    AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+    routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
+
+    // handle special case for sonification while in call
+    if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+        handleIncallSonification(stream, false, false);
+    }
+
+    if (outputDesc->mRefCount[stream] > 0) {
+        // decrement usage count of this stream on the output
+        outputDesc->changeRefCount(stream, -1);
+        // store time at which the last music track was stopped - see computeVolume()
+        if (stream == AudioSystem::MUSIC) {
+            mMusicStopTime = systemTime();
+        }
+
+        setOutputDevice(output, getNewDevice(output));
+
+#ifdef WITH_A2DP
+        if (mA2dpOutput != 0 && !a2dpUsedForSonification() && strategy == STRATEGY_SONIFICATION) {
+            setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput, mOutputs.valueFor(mHardwareOutput)->mLatency*2);
+        }
+#endif
+        return NO_ERROR;
+    } else {
+        LOGW("stopOutput() refcount is already 0 for output %d", output);
+        return INVALID_OPERATION;
+    }
+}
+
+void AudioPolicyManagerBase::releaseOutput(audio_io_handle_t output)
+{
+    LOGV("releaseOutput() %d", output);
+    ssize_t index = mOutputs.indexOfKey(output);
+    if (index < 0) {
+        LOGW("releaseOutput() releasing unknown output %d", output);
+        return;
+    }
+
+#ifdef AUDIO_POLICY_TEST
+    int testIndex = testOutputIndex(output);
+    if (testIndex != 0) {
+        AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+        if (outputDesc->refCount() == 0) {
+            mpClientInterface->closeOutput(output);
+            delete mOutputs.valueAt(index);
+            mOutputs.removeItem(output);
+            mTestOutputs[testIndex] = 0;
+        }
+        return;
+    }
+#endif //AUDIO_POLICY_TEST
+
+    if (mOutputs.valueAt(index)->mFlags & AudioSystem::OUTPUT_FLAG_DIRECT) {
+        mpClientInterface->closeOutput(output);
+        delete mOutputs.valueAt(index);
+        mOutputs.removeItem(output);
+    }
+}
+
+audio_io_handle_t AudioPolicyManagerBase::getInput(int inputSource,
+                                    uint32_t samplingRate,
+                                    uint32_t format,
+                                    uint32_t channels,
+                                    AudioSystem::audio_in_acoustics acoustics)
+{
+    audio_io_handle_t input = 0;
+    uint32_t device = getDeviceForInputSource(inputSource);
+
+    LOGV("getInput() inputSource %d, samplingRate %d, format %d, channels %x, acoustics %x", inputSource, samplingRate, format, channels, acoustics);
+
+    if (device == 0) {
+        return 0;
+    }
+
+    // adapt channel selection to input source
+    switch(inputSource) {
+    case AUDIO_SOURCE_VOICE_UPLINK:
+        channels = AudioSystem::CHANNEL_IN_VOICE_UPLINK;
+        break;
+    case AUDIO_SOURCE_VOICE_DOWNLINK:
+        channels = AudioSystem::CHANNEL_IN_VOICE_DNLINK;
+        break;
+    case AUDIO_SOURCE_VOICE_CALL:
+        channels = (AudioSystem::CHANNEL_IN_VOICE_UPLINK | AudioSystem::CHANNEL_IN_VOICE_DNLINK);
+        break;
+    default:
+        break;
+    }
+
+    AudioInputDescriptor *inputDesc = new AudioInputDescriptor();
+
+    inputDesc->mInputSource = inputSource;
+    inputDesc->mDevice = device;
+    inputDesc->mSamplingRate = samplingRate;
+    inputDesc->mFormat = format;
+    inputDesc->mChannels = channels;
+    inputDesc->mAcoustics = acoustics;
+    inputDesc->mRefCount = 0;
+    input = mpClientInterface->openInput(&inputDesc->mDevice,
+                                    &inputDesc->mSamplingRate,
+                                    &inputDesc->mFormat,
+                                    &inputDesc->mChannels,
+                                    inputDesc->mAcoustics);
+
+    // only accept input with the exact requested set of parameters
+    if (input == 0 ||
+        (samplingRate != inputDesc->mSamplingRate) ||
+        (format != inputDesc->mFormat) ||
+        (channels != inputDesc->mChannels)) {
+        LOGV("getInput() failed opening input: samplingRate %d, format %d, channels %d",
+                samplingRate, format, channels);
+        if (input != 0) {
+            mpClientInterface->closeInput(input);
+        }
+        delete inputDesc;
+        return 0;
+    }
+    mInputs.add(input, inputDesc);
+    return input;
+}
+
+status_t AudioPolicyManagerBase::startInput(audio_io_handle_t input)
+{
+    LOGV("startInput() input %d", input);
+    ssize_t index = mInputs.indexOfKey(input);
+    if (index < 0) {
+        LOGW("startInput() unknow input %d", input);
+        return BAD_VALUE;
+    }
+    AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
+
+#ifdef AUDIO_POLICY_TEST
+    if (mTestInput == 0)
+#endif //AUDIO_POLICY_TEST
+    {
+        // refuse 2 active AudioRecord clients at the same time
+        if (getActiveInput() != 0) {
+            LOGW("startInput() input %d failed: other input already started", input);
+            return INVALID_OPERATION;
+        }
+    }
+
+    AudioParameter param = AudioParameter();
+    param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice);
+
+    // use Voice Recognition mode or not for this input based on input source
+    int vr_enabled = inputDesc->mInputSource == AUDIO_SOURCE_VOICE_RECOGNITION ? 1 : 0;
+    param.addInt(String8("vr_mode"), vr_enabled);
+    LOGV("AudioPolicyManager::startInput(%d), setting vr_mode to %d", inputDesc->mInputSource, vr_enabled);
+
+    mpClientInterface->setParameters(input, param.toString());
+
+    inputDesc->mRefCount = 1;
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManagerBase::stopInput(audio_io_handle_t input)
+{
+    LOGV("stopInput() input %d", input);
+    ssize_t index = mInputs.indexOfKey(input);
+    if (index < 0) {
+        LOGW("stopInput() unknow input %d", input);
+        return BAD_VALUE;
+    }
+    AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
+
+    if (inputDesc->mRefCount == 0) {
+        LOGW("stopInput() input %d already stopped", input);
+        return INVALID_OPERATION;
+    } else {
+        AudioParameter param = AudioParameter();
+        param.addInt(String8(AudioParameter::keyRouting), 0);
+        mpClientInterface->setParameters(input, param.toString());
+        inputDesc->mRefCount = 0;
+        return NO_ERROR;
+    }
+}
+
+void AudioPolicyManagerBase::releaseInput(audio_io_handle_t input)
+{
+    LOGV("releaseInput() %d", input);
+    ssize_t index = mInputs.indexOfKey(input);
+    if (index < 0) {
+        LOGW("releaseInput() releasing unknown input %d", input);
+        return;
+    }
+    mpClientInterface->closeInput(input);
+    delete mInputs.valueAt(index);
+    mInputs.removeItem(input);
+    LOGV("releaseInput() exit");
+}
+
+void AudioPolicyManagerBase::initStreamVolume(AudioSystem::stream_type stream,
+                                            int indexMin,
+                                            int indexMax)
+{
+    LOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax);
+    if (indexMin < 0 || indexMin >= indexMax) {
+        LOGW("initStreamVolume() invalid index limits for stream %d, min %d, max %d", stream , indexMin, indexMax);
+        return;
+    }
+    mStreams[stream].mIndexMin = indexMin;
+    mStreams[stream].mIndexMax = indexMax;
+}
+
+status_t AudioPolicyManagerBase::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
+{
+
+    if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) {
+        return BAD_VALUE;
+    }
+
+    // Force max volume if stream cannot be muted
+    if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax;
+
+    LOGV("setStreamVolumeIndex() stream %d, index %d", stream, index);
+    mStreams[stream].mIndexCur = index;
+
+    // compute and apply stream volume on all outputs according to connected device
+    status_t status = NO_ERROR;
+    for (size_t i = 0; i < mOutputs.size(); i++) {
+        status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), mOutputs.valueAt(i)->device());
+        if (volStatus != NO_ERROR) {
+            status = volStatus;
+        }
+    }
+    return status;
+}
+
+status_t AudioPolicyManagerBase::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index)
+{
+    if (index == 0) {
+        return BAD_VALUE;
+    }
+    LOGV("getStreamVolumeIndex() stream %d", stream);
+    *index =  mStreams[stream].mIndexCur;
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManagerBase::dump(int fd)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Hardware Output: %d\n", mHardwareOutput);
+    result.append(buffer);
+#ifdef WITH_A2DP
+    snprintf(buffer, SIZE, " A2DP Output: %d\n", mA2dpOutput);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Duplicated Output: %d\n", mDuplicatedOutput);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " A2DP device address: %s\n", mA2dpDeviceAddress.string());
+    result.append(buffer);
+#endif
+    snprintf(buffer, SIZE, " SCO device address: %s\n", mScoDeviceAddress.string());
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Output devices: %08x\n", mAvailableOutputDevices);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Input devices: %08x\n", mAvailableInputDevices);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Phone state: %d\n", mPhoneState);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Ringer mode: %d\n", mRingerMode);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Force use for communications %d\n", mForceUse[AudioSystem::FOR_COMMUNICATION]);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Force use for media %d\n", mForceUse[AudioSystem::FOR_MEDIA]);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Force use for record %d\n", mForceUse[AudioSystem::FOR_RECORD]);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Force use for dock %d\n", mForceUse[AudioSystem::FOR_DOCK]);
+    result.append(buffer);
+    write(fd, result.string(), result.size());
+
+    snprintf(buffer, SIZE, "\nOutputs dump:\n");
+    write(fd, buffer, strlen(buffer));
+    for (size_t i = 0; i < mOutputs.size(); i++) {
+        snprintf(buffer, SIZE, "- Output %d dump:\n", mOutputs.keyAt(i));
+        write(fd, buffer, strlen(buffer));
+        mOutputs.valueAt(i)->dump(fd);
+    }
+
+    snprintf(buffer, SIZE, "\nInputs dump:\n");
+    write(fd, buffer, strlen(buffer));
+    for (size_t i = 0; i < mInputs.size(); i++) {
+        snprintf(buffer, SIZE, "- Input %d dump:\n", mInputs.keyAt(i));
+        write(fd, buffer, strlen(buffer));
+        mInputs.valueAt(i)->dump(fd);
+    }
+
+    snprintf(buffer, SIZE, "\nStreams dump:\n");
+    write(fd, buffer, strlen(buffer));
+    snprintf(buffer, SIZE, " Stream  Index Min  Index Max  Index Cur  Can be muted\n");
+    write(fd, buffer, strlen(buffer));
+    for (size_t i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
+        snprintf(buffer, SIZE, " %02d", i);
+        mStreams[i].dump(buffer + 3, SIZE);
+        write(fd, buffer, strlen(buffer));
+    }
+
+    return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+// AudioPolicyManagerBase
+// ----------------------------------------------------------------------------
+
+AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)
+    :
+#ifdef AUDIO_POLICY_TEST
+    Thread(false),
+#endif //AUDIO_POLICY_TEST
+    mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), mLimitRingtoneVolume(false)
+{
+    mpClientInterface = clientInterface;
+
+    for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
+        mForceUse[i] = AudioSystem::FORCE_NONE;
+    }
+
+    // devices available by default are speaker, ear piece and microphone
+    mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
+                        AudioSystem::DEVICE_OUT_SPEAKER;
+    mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;
+
+#ifdef WITH_A2DP
+    mA2dpOutput = 0;
+    mDuplicatedOutput = 0;
+    mA2dpDeviceAddress = String8("");
+#endif
+    mScoDeviceAddress = String8("");
+
+    // open hardware output
+    AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+    outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
+    mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
+                                    &outputDesc->mSamplingRate,
+                                    &outputDesc->mFormat,
+                                    &outputDesc->mChannels,
+                                    &outputDesc->mLatency,
+                                    outputDesc->mFlags);
+
+    if (mHardwareOutput == 0) {
+        LOGE("Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d",
+                outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
+    } else {
+        addOutput(mHardwareOutput, outputDesc);
+        setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
+    }
+
+    updateDeviceForStrategy();
+#ifdef AUDIO_POLICY_TEST
+    AudioParameter outputCmd = AudioParameter();
+    outputCmd.addInt(String8("set_id"), 0);
+    mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
+
+    mTestDevice = AudioSystem::DEVICE_OUT_SPEAKER;
+    mTestSamplingRate = 44100;
+    mTestFormat = AudioSystem::PCM_16_BIT;
+    mTestChannels =  AudioSystem::CHANNEL_OUT_STEREO;
+    mTestLatencyMs = 0;
+    mCurOutput = 0;
+    mDirectOutput = false;
+    for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
+        mTestOutputs[i] = 0;
+    }
+
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    snprintf(buffer, SIZE, "AudioPolicyManagerTest");
+    run(buffer, ANDROID_PRIORITY_AUDIO);
+#endif //AUDIO_POLICY_TEST
+}
+
+AudioPolicyManagerBase::~AudioPolicyManagerBase()
+{
+#ifdef AUDIO_POLICY_TEST
+    exit();
+#endif //AUDIO_POLICY_TEST
+   for (size_t i = 0; i < mOutputs.size(); i++) {
+        mpClientInterface->closeOutput(mOutputs.keyAt(i));
+        delete mOutputs.valueAt(i);
+   }
+   mOutputs.clear();
+   for (size_t i = 0; i < mInputs.size(); i++) {
+        mpClientInterface->closeInput(mInputs.keyAt(i));
+        delete mInputs.valueAt(i);
+   }
+   mInputs.clear();
+}
+
+#ifdef AUDIO_POLICY_TEST
+bool AudioPolicyManagerBase::threadLoop()
+{
+    LOGV("entering threadLoop()");
+    while (!exitPending())
+    {
+        String8 command;
+        int valueInt;
+        String8 value;
+
+        Mutex::Autolock _l(mLock);
+        mWaitWorkCV.waitRelative(mLock, milliseconds(50));
+
+        command = mpClientInterface->getParameters(0, String8("test_cmd_policy"));
+        AudioParameter param = AudioParameter(command);
+
+        if (param.getInt(String8("test_cmd_policy"), valueInt) == NO_ERROR &&
+            valueInt != 0) {
+            LOGV("Test command %s received", command.string());
+            String8 target;
+            if (param.get(String8("target"), target) != NO_ERROR) {
+                target = "Manager";
+            }
+            if (param.getInt(String8("test_cmd_policy_output"), valueInt) == NO_ERROR) {
+                param.remove(String8("test_cmd_policy_output"));
+                mCurOutput = valueInt;
+            }
+            if (param.get(String8("test_cmd_policy_direct"), value) == NO_ERROR) {
+                param.remove(String8("test_cmd_policy_direct"));
+                if (value == "false") {
+                    mDirectOutput = false;
+                } else if (value == "true") {
+                    mDirectOutput = true;
+                }
+            }
+            if (param.getInt(String8("test_cmd_policy_input"), valueInt) == NO_ERROR) {
+                param.remove(String8("test_cmd_policy_input"));
+                mTestInput = valueInt;
+            }
+
+            if (param.get(String8("test_cmd_policy_format"), value) == NO_ERROR) {
+                param.remove(String8("test_cmd_policy_format"));
+                int format = AudioSystem::INVALID_FORMAT;
+                if (value == "PCM 16 bits") {
+                    format = AudioSystem::PCM_16_BIT;
+                } else if (value == "PCM 8 bits") {
+                    format = AudioSystem::PCM_8_BIT;
+                } else if (value == "Compressed MP3") {
+                    format = AudioSystem::MP3;
+                }
+                if (format != AudioSystem::INVALID_FORMAT) {
+                    if (target == "Manager") {
+                        mTestFormat = format;
+                    } else if (mTestOutputs[mCurOutput] != 0) {
+                        AudioParameter outputParam = AudioParameter();
+                        outputParam.addInt(String8("format"), format);
+                        mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
+                    }
+                }
+            }
+            if (param.get(String8("test_cmd_policy_channels"), value) == NO_ERROR) {
+                param.remove(String8("test_cmd_policy_channels"));
+                int channels = 0;
+
+                if (value == "Channels Stereo") {
+                    channels =  AudioSystem::CHANNEL_OUT_STEREO;
+                } else if (value == "Channels Mono") {
+                    channels =  AudioSystem::CHANNEL_OUT_MONO;
+                }
+                if (channels != 0) {
+                    if (target == "Manager") {
+                        mTestChannels = channels;
+                    } else if (mTestOutputs[mCurOutput] != 0) {
+                        AudioParameter outputParam = AudioParameter();
+                        outputParam.addInt(String8("channels"), channels);
+                        mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
+                    }
+                }
+            }
+            if (param.getInt(String8("test_cmd_policy_sampleRate"), valueInt) == NO_ERROR) {
+                param.remove(String8("test_cmd_policy_sampleRate"));
+                if (valueInt >= 0 && valueInt <= 96000) {
+                    int samplingRate = valueInt;
+                    if (target == "Manager") {
+                        mTestSamplingRate = samplingRate;
+                    } else if (mTestOutputs[mCurOutput] != 0) {
+                        AudioParameter outputParam = AudioParameter();
+                        outputParam.addInt(String8("sampling_rate"), samplingRate);
+                        mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
+                    }
+                }
+            }
+
+            if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) {
+                param.remove(String8("test_cmd_policy_reopen"));
+
+                mpClientInterface->closeOutput(mHardwareOutput);
+                delete mOutputs.valueFor(mHardwareOutput);
+                mOutputs.removeItem(mHardwareOutput);
+
+                AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+                outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
+                mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
+                                                &outputDesc->mSamplingRate,
+                                                &outputDesc->mFormat,
+                                                &outputDesc->mChannels,
+                                                &outputDesc->mLatency,
+                                                outputDesc->mFlags);
+                if (mHardwareOutput == 0) {
+                    LOGE("Failed to reopen hardware output stream, samplingRate: %d, format %d, channels %d",
+                            outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
+                } else {
+                    AudioParameter outputCmd = AudioParameter();
+                    outputCmd.addInt(String8("set_id"), 0);
+                    mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
+                    addOutput(mHardwareOutput, outputDesc);
+                }
+            }
+
+
+            mpClientInterface->setParameters(0, String8("test_cmd_policy="));
+        }
+    }
+    return false;
+}
+
+void AudioPolicyManagerBase::exit()
+{
+    {
+        AutoMutex _l(mLock);
+        requestExit();
+        mWaitWorkCV.signal();
+    }
+    requestExitAndWait();
+}
+
+int AudioPolicyManagerBase::testOutputIndex(audio_io_handle_t output)
+{
+    for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
+        if (output == mTestOutputs[i]) return i;
+    }
+    return 0;
+}
+#endif //AUDIO_POLICY_TEST
+
+// ---
+
+void AudioPolicyManagerBase::addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc)
+{
+    outputDesc->mId = id;
+    mOutputs.add(id, outputDesc);
+}
+
+
+#ifdef WITH_A2DP
+status_t AudioPolicyManagerBase::handleA2dpConnection(AudioSystem::audio_devices device,
+                                                 const char *device_address)
+{
+    // when an A2DP device is connected, open an A2DP and a duplicated output
+    LOGV("opening A2DP output for device %s", device_address);
+    AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+    outputDesc->mDevice = device;
+    mA2dpOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
+                                            &outputDesc->mSamplingRate,
+                                            &outputDesc->mFormat,
+                                            &outputDesc->mChannels,
+                                            &outputDesc->mLatency,
+                                            outputDesc->mFlags);
+    if (mA2dpOutput) {
+        // add A2DP output descriptor
+        addOutput(mA2dpOutput, outputDesc);
+        // set initial stream volume for A2DP device
+        applyStreamVolumes(mA2dpOutput, device);
+        if (a2dpUsedForSonification()) {
+            mDuplicatedOutput = mpClientInterface->openDuplicateOutput(mA2dpOutput, mHardwareOutput);
+        }
+        if (mDuplicatedOutput != 0 ||
+            !a2dpUsedForSonification()) {
+            // If both A2DP and duplicated outputs are open, send device address to A2DP hardware
+            // interface
+            AudioParameter param;
+            param.add(String8("a2dp_sink_address"), String8(device_address));
+            mpClientInterface->setParameters(mA2dpOutput, param.toString());
+            mA2dpDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
+
+            if (a2dpUsedForSonification()) {
+                // add duplicated output descriptor
+                AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor();
+                dupOutputDesc->mOutput1 = mOutputs.valueFor(mHardwareOutput);
+                dupOutputDesc->mOutput2 = mOutputs.valueFor(mA2dpOutput);
+                dupOutputDesc->mSamplingRate = outputDesc->mSamplingRate;
+                dupOutputDesc->mFormat = outputDesc->mFormat;
+                dupOutputDesc->mChannels = outputDesc->mChannels;
+                dupOutputDesc->mLatency = outputDesc->mLatency;
+                addOutput(mDuplicatedOutput, dupOutputDesc);
+                applyStreamVolumes(mDuplicatedOutput, device);
+            }
+        } else {
+            LOGW("getOutput() could not open duplicated output for %d and %d",
+                    mHardwareOutput, mA2dpOutput);
+            mpClientInterface->closeOutput(mA2dpOutput);
+            mOutputs.removeItem(mA2dpOutput);
+            mA2dpOutput = 0;
+            delete outputDesc;
+            return NO_INIT;
+        }
+    } else {
+        LOGW("setDeviceConnectionState() could not open A2DP output for device %x", device);
+        delete outputDesc;
+        return NO_INIT;
+    }
+    AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
+
+    if (mA2dpDeviceAddress == mScoDeviceAddress) {
+        // It is normal to suspend twice if we are both in call,
+        // and have the hardware audio output routed to BT SCO
+        if (mPhoneState != AudioSystem::MODE_NORMAL) {
+            mpClientInterface->suspendOutput(mA2dpOutput);
+        }
+        if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)hwOutputDesc->device())) {
+            mpClientInterface->suspendOutput(mA2dpOutput);
+        }
+    }
+
+    if (!a2dpUsedForSonification()) {
+        // mute music on A2DP output if a notification or ringtone is playing
+        uint32_t refCount = hwOutputDesc->strategyRefCount(STRATEGY_SONIFICATION);
+        for (uint32_t i = 0; i < refCount; i++) {
+            setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManagerBase::handleA2dpDisconnection(AudioSystem::audio_devices device,
+                                                    const char *device_address)
+{
+    if (mA2dpOutput == 0) {
+        LOGW("setDeviceConnectionState() disconnecting A2DP and no A2DP output!");
+        return INVALID_OPERATION;
+    }
+
+    if (mA2dpDeviceAddress != device_address) {
+        LOGW("setDeviceConnectionState() disconnecting unknow A2DP sink address %s", device_address);
+        return INVALID_OPERATION;
+    }
+
+    // mute media during 2 seconds to avoid outputing sound on hardware output while music stream
+    // is switched from A2DP output and before music is paused by music application
+    setStrategyMute(STRATEGY_MEDIA, true, mHardwareOutput);
+    setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, 2000);
+
+    if (!a2dpUsedForSonification()) {
+        // unmute music on A2DP output if a notification or ringtone is playing
+        uint32_t refCount = mOutputs.valueFor(mHardwareOutput)->strategyRefCount(STRATEGY_SONIFICATION);
+        for (uint32_t i = 0; i < refCount; i++) {
+            setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput);
+        }
+    }
+    mA2dpDeviceAddress = "";
+    return NO_ERROR;
+}
+
+void AudioPolicyManagerBase::closeA2dpOutputs()
+{
+    LOGV("setDeviceConnectionState() closing A2DP and duplicated output!");
+
+    if (mDuplicatedOutput != 0) {
+        mpClientInterface->closeOutput(mDuplicatedOutput);
+        delete mOutputs.valueFor(mDuplicatedOutput);
+        mOutputs.removeItem(mDuplicatedOutput);
+        mDuplicatedOutput = 0;
+    }
+    if (mA2dpOutput != 0) {
+        AudioParameter param;
+        param.add(String8("closing"), String8("true"));
+        mpClientInterface->setParameters(mA2dpOutput, param.toString());
+        mpClientInterface->closeOutput(mA2dpOutput);
+        delete mOutputs.valueFor(mA2dpOutput);
+        mOutputs.removeItem(mA2dpOutput);
+        mA2dpOutput = 0;
+    }
+}
+
+void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy, uint32_t &newDevice)
+{
+    uint32_t prevDevice = getDeviceForStrategy(strategy);
+    uint32_t curDevice = getDeviceForStrategy(strategy, false);
+    bool a2dpWasUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(prevDevice & ~AudioSystem::DEVICE_OUT_SPEAKER));
+    bool a2dpIsUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(curDevice & ~AudioSystem::DEVICE_OUT_SPEAKER));
+    AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
+    AudioOutputDescriptor *a2dpOutputDesc;
+
+    if (a2dpWasUsed && !a2dpIsUsed) {
+        bool dupUsed = a2dpUsedForSonification() && a2dpWasUsed && (AudioSystem::popCount(prevDevice) == 2);
+
+        if (dupUsed) {
+            LOGV("checkOutputForStrategy() moving strategy %d to duplicated", strategy);
+            a2dpOutputDesc = mOutputs.valueFor(mDuplicatedOutput);
+        } else {
+            LOGV("checkOutputForStrategy() moving strategy %d to a2dp", strategy);
+            a2dpOutputDesc = mOutputs.valueFor(mA2dpOutput);
+        }
+
+        for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+            if (getStrategy((AudioSystem::stream_type)i) == strategy) {
+                mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mHardwareOutput);
+                int refCount = a2dpOutputDesc->mRefCount[i];
+                // in the case of duplicated output, the ref count is first incremented
+                // and then decremented on hardware output tus keeping its value
+                hwOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount);
+                a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount);
+            }
+        }
+        // do not change newDevice is it was already set before this call by a previous call to
+        // getNewDevice() or checkOutputForStrategy() for a strategy with higher priority
+        if (newDevice == 0 && hwOutputDesc->isUsedByStrategy(strategy)) {
+            newDevice = getDeviceForStrategy(strategy, false);
+        }
+    }
+    if (a2dpIsUsed && !a2dpWasUsed) {
+        bool dupUsed = a2dpUsedForSonification() && a2dpIsUsed && (AudioSystem::popCount(curDevice) == 2);
+        audio_io_handle_t a2dpOutput;
+
+        if (dupUsed) {
+            LOGV("checkOutputForStrategy() moving strategy %d from duplicated", strategy);
+            a2dpOutputDesc = mOutputs.valueFor(mDuplicatedOutput);
+            a2dpOutput = mDuplicatedOutput;
+        } else {
+            LOGV("checkOutputForStrategy() moving strategy %d from a2dp", strategy);
+            a2dpOutputDesc = mOutputs.valueFor(mA2dpOutput);
+            a2dpOutput = mA2dpOutput;
+        }
+
+        for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+            if (getStrategy((AudioSystem::stream_type)i) == strategy) {
+                mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, a2dpOutput);
+                int refCount = hwOutputDesc->mRefCount[i];
+                // in the case of duplicated output, the ref count is first incremented
+                // and then decremented on hardware output tus keeping its value
+                a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount);
+                hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount);
+            }
+        }
+    }
+}
+
+void AudioPolicyManagerBase::checkOutputForAllStrategies(uint32_t &newDevice)
+{
+    // Check strategies in order of priority so that once newDevice is set
+    // for a given strategy it is not modified by subsequent calls to
+    // checkOutputForStrategy()
+    checkOutputForStrategy(STRATEGY_PHONE, newDevice);
+    checkOutputForStrategy(STRATEGY_SONIFICATION, newDevice);
+    checkOutputForStrategy(STRATEGY_MEDIA, newDevice);
+    checkOutputForStrategy(STRATEGY_DTMF, newDevice);
+}
+
+#endif
+
+uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache)
+{
+    uint32_t device = 0;
+
+    AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+    // check the following by order of priority to request a routing change if necessary:
+    // 1: we are in call or the strategy phone is active on the hardware output:
+    //      use device for strategy phone
+    // 2: the strategy sonification is active on the hardware output:
+    //      use device for strategy sonification
+    // 3: the strategy media is active on the hardware output:
+    //      use device for strategy media
+    // 4: the strategy DTMF is active on the hardware output:
+    //      use device for strategy DTMF
+    if (mPhoneState == AudioSystem::MODE_IN_CALL ||
+        outputDesc->isUsedByStrategy(STRATEGY_PHONE)) {
+        device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
+    } else if (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) {
+        device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
+    } else if (outputDesc->isUsedByStrategy(STRATEGY_MEDIA)) {
+        device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache);
+    } else if (outputDesc->isUsedByStrategy(STRATEGY_DTMF)) {
+        device = getDeviceForStrategy(STRATEGY_DTMF, fromCache);
+    }
+
+    LOGV("getNewDevice() selected device %x", device);
+    return device;
+}
+
+AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy(AudioSystem::stream_type stream)
+{
+    // stream to strategy mapping
+    switch (stream) {
+    case AudioSystem::VOICE_CALL:
+    case AudioSystem::BLUETOOTH_SCO:
+        return STRATEGY_PHONE;
+    case AudioSystem::RING:
+    case AudioSystem::NOTIFICATION:
+    case AudioSystem::ALARM:
+    case AudioSystem::ENFORCED_AUDIBLE:
+        return STRATEGY_SONIFICATION;
+    case AudioSystem::DTMF:
+        return STRATEGY_DTMF;
+    default:
+        LOGE("unknown stream type");
+    case AudioSystem::SYSTEM:
+        // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs
+        // while key clicks are played produces a poor result
+    case AudioSystem::TTS:
+    case AudioSystem::MUSIC:
+        return STRATEGY_MEDIA;
+    }
+}
+
+uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache)
+{
+    uint32_t device = 0;
+
+    if (fromCache) {
+        LOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]);
+        return mDeviceForStrategy[strategy];
+    }
+
+    switch (strategy) {
+    case STRATEGY_DTMF:
+        if (mPhoneState != AudioSystem::MODE_IN_CALL) {
+            // when off call, DTMF strategy follows the same rules as MEDIA strategy
+            device = getDeviceForStrategy(STRATEGY_MEDIA, false);
+            break;
+        }
+        // when in call, DTMF and PHONE strategies follow the same rules
+        // FALL THROUGH
+
+    case STRATEGY_PHONE:
+        // for phone strategy, we first consider the forced use and then the available devices by order
+        // of priority
+        switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) {
+        case AudioSystem::FORCE_BT_SCO:
+            if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) {
+                device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+                if (device) break;
+            }
+            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+            if (device) break;
+            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO;
+            if (device) break;
+            // if SCO device is requested but no SCO device is available, fall back to default case
+            // FALL THROUGH
+
+        default:    // FORCE_NONE
+            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
+            if (device) break;
+            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
+            if (device) break;
+            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE;
+            if (device == 0) {
+                LOGE("getDeviceForStrategy() earpiece device not found");
+            }
+            break;
+
+        case AudioSystem::FORCE_SPEAKER:
+            if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) {
+                device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+                if (device) break;
+            }
+            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
+            if (device == 0) {
+                LOGE("getDeviceForStrategy() speaker device not found");
+            }
+            break;
+        }
+    break;
+
+    case STRATEGY_SONIFICATION:
+
+        // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by
+        // handleIncallSonification().
+        if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+            device = getDeviceForStrategy(STRATEGY_PHONE, false);
+            break;
+        }
+        device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
+        if (device == 0) {
+            LOGE("getDeviceForStrategy() speaker device not found");
+        }
+        // The second device used for sonification is the same as the device used by media strategy
+        // FALL THROUGH
+
+    case STRATEGY_MEDIA: {
+        uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
+#ifdef WITH_A2DP
+        if (mA2dpOutput != 0) {
+            if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) {
+                break;
+            }
+            if (device2 == 0) {
+                device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
+            }
+            if (device2 == 0) {
+                device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
+            }
+            if (device2 == 0) {
+                device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
+            }
+        }
+#endif
+        if (device2 == 0) {
+            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
+        }
+        if (device2 == 0) {
+            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
+        }
+        if (device2 == 0) {
+            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
+        }
+
+        // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION, 0 otherwise
+        device |= device2;
+        if (device == 0) {
+            LOGE("getDeviceForStrategy() speaker device not found");
+        }
+        } break;
+
+    default:
+        LOGW("getDeviceForStrategy() unknown strategy: %d", strategy);
+        break;
+    }
+
+    LOGV("getDeviceForStrategy() strategy %d, device %x", strategy, device);
+    return device;
+}
+
+void AudioPolicyManagerBase::updateDeviceForStrategy()
+{
+    for (int i = 0; i < NUM_STRATEGIES; i++) {
+        mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);
+    }
+}
+
+void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)
+{
+    LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs);
+    AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+
+
+    if (outputDesc->isDuplicated()) {
+        setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);
+        setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);
+        return;
+    }
+#ifdef WITH_A2DP
+    // filter devices according to output selected
+    if (output == mHardwareOutput) {
+        device &= ~AudioSystem::DEVICE_OUT_ALL_A2DP;
+    } else {
+        device &= AudioSystem::DEVICE_OUT_ALL_A2DP;
+    }
+#endif
+
+    uint32_t prevDevice = (uint32_t)outputDesc->device();
+    // Do not change the routing if:
+    //  - the requestede device is 0
+    //  - the requested device is the same as current device and force is not specified.
+    // Doing this check here allows the caller to call setOutputDevice() without conditions
+    if (device == 0 ||
+        (device == prevDevice && !force)) {
+        LOGV("setOutputDevice() setting same device %x or null device for output %d", device, output);
+        return;
+    }
+
+    outputDesc->mDevice = device;
+    // mute media streams if both speaker and headset are selected
+    if (output == mHardwareOutput && AudioSystem::popCount(device) == 2) {
+        setStrategyMute(STRATEGY_MEDIA, true, output);
+        // wait for the PCM output buffers to empty before proceeding with the rest of the command
+        usleep(outputDesc->mLatency*2*1000);
+    }
+#ifdef WITH_A2DP
+    // suspend A2D output if SCO device is selected
+    if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) {
+         if (mA2dpOutput && mScoDeviceAddress == mA2dpDeviceAddress) {
+             mpClientInterface->suspendOutput(mA2dpOutput);
+         }
+    }
+#endif
+    // do the routing
+    AudioParameter param = AudioParameter();
+    param.addInt(String8(AudioParameter::keyRouting), (int)device);
+    mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs);
+    // update stream volumes according to new device
+    applyStreamVolumes(output, device, delayMs);
+
+#ifdef WITH_A2DP
+    // if disconnecting SCO device, restore A2DP output
+    if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)prevDevice)) {
+         if (mA2dpOutput && mScoDeviceAddress == mA2dpDeviceAddress) {
+             LOGV("restore A2DP output");
+             mpClientInterface->restoreOutput(mA2dpOutput);
+         }
+    }
+#endif
+    // if changing from a combined headset + speaker route, unmute media streams
+    if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) {
+        setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);
+    }
+}
+
+uint32_t AudioPolicyManagerBase::getDeviceForInputSource(int inputSource)
+{
+    uint32_t device;
+
+    switch(inputSource) {
+    case AUDIO_SOURCE_DEFAULT:
+    case AUDIO_SOURCE_MIC:
+    case AUDIO_SOURCE_VOICE_RECOGNITION:
+        if (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO &&
+            mAvailableInputDevices & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
+            device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+        } else if (mAvailableInputDevices & AudioSystem::DEVICE_IN_WIRED_HEADSET) {
+            device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
+        } else {
+            device = AudioSystem::DEVICE_IN_BUILTIN_MIC;
+        }
+        break;
+    case AUDIO_SOURCE_CAMCORDER:
+        if (hasBackMicrophone()) {
+            device = AudioSystem::DEVICE_IN_BACK_MIC;
+        } else {
+            device = AudioSystem::DEVICE_IN_BUILTIN_MIC;
+        }
+        break;
+    case AUDIO_SOURCE_VOICE_UPLINK:
+    case AUDIO_SOURCE_VOICE_DOWNLINK:
+    case AUDIO_SOURCE_VOICE_CALL:
+        device = AudioSystem::DEVICE_IN_VOICE_CALL;
+        break;
+    default:
+        LOGW("getInput() invalid input source %d", inputSource);
+        device = 0;
+        break;
+    }
+    LOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device);
+    return device;
+}
+
+audio_io_handle_t AudioPolicyManagerBase::getActiveInput()
+{
+    for (size_t i = 0; i < mInputs.size(); i++) {
+        if (mInputs.valueAt(i)->mRefCount > 0) {
+            return mInputs.keyAt(i);
+        }
+    }
+    return 0;
+}
+
+float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device)
+{
+    float volume = 1.0;
+    AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+    StreamDescriptor &streamDesc = mStreams[stream];
+
+    if (device == 0) {
+        device = outputDesc->device();
+    }
+
+    int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin);
+    volume = AudioSystem::linearToLog(volInt);
+
+    // if a heaset is connected, apply the following rules to ring tones and notifications
+    // to avoid sound level bursts in user's ears:
+    // - always attenuate ring tones and notifications volume by 6dB
+    // - if music is playing, always limit the volume to current music volume,
+    // with a minimum threshold at -36dB so that notification is always perceived.
+    if ((device &
+        (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP |
+        AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+        AudioSystem::DEVICE_OUT_WIRED_HEADSET |
+        AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) &&
+        (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) &&
+        streamDesc.mCanBeMuted) {
+        volume *= SONIFICATION_HEADSET_VOLUME_FACTOR;
+        // when the phone is ringing we must consider that music could have been paused just before
+        // by the music application and behave as if music was active if the last music track was
+        // just stopped
+        if (outputDesc->mRefCount[AudioSystem::MUSIC] || mLimitRingtoneVolume) {
+            float musicVol = computeVolume(AudioSystem::MUSIC, mStreams[AudioSystem::MUSIC].mIndexCur, output, device);
+            float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ? musicVol : SONIFICATION_HEADSET_VOLUME_MIN;
+            if (volume > minVol) {
+                volume = minVol;
+                LOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol);
+            }
+        }
+    }
+
+    return volume;
+}
+
+status_t AudioPolicyManagerBase::checkAndSetVolume(int stream, int index, audio_io_handle_t output, uint32_t device, int delayMs, bool force)
+{
+
+    // do not change actual stream volume if the stream is muted
+    if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) {
+        LOGV("checkAndSetVolume() stream %d muted count %d", stream, mOutputs.valueFor(output)->mMuteCount[stream]);
+        return NO_ERROR;
+    }
+
+    // do not change in call volume if bluetooth is connected and vice versa
+    if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
+        (stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) {
+        LOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm",
+             stream, mForceUse[AudioSystem::FOR_COMMUNICATION]);
+        return INVALID_OPERATION;
+    }
+
+    float volume = computeVolume(stream, index, output, device);
+    // do not set volume if the float value did not change
+    if (volume != mOutputs.valueFor(output)->mCurVolume[stream] || force) {
+        mOutputs.valueFor(output)->mCurVolume[stream] = volume;
+        LOGV("setStreamVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
+        if (stream == AudioSystem::VOICE_CALL ||
+            stream == AudioSystem::DTMF ||
+            stream == AudioSystem::BLUETOOTH_SCO) {
+            float voiceVolume = -1.0;
+            // offset value to reflect actual hardware volume that never reaches 0
+            // 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java)
+            volume = 0.01 + 0.99 * volume;
+            if (stream == AudioSystem::VOICE_CALL) {
+                voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
+            } else if (stream == AudioSystem::BLUETOOTH_SCO) {
+                voiceVolume = 1.0;
+            }
+            if (voiceVolume >= 0 && output == mHardwareOutput) {
+                mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
+            }
+        }
+        mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);
+    }
+
+    return NO_ERROR;
+}
+
+void AudioPolicyManagerBase::applyStreamVolumes(audio_io_handle_t output, uint32_t device, int delayMs)
+{
+    LOGV("applyStreamVolumes() for output %d and device %x", output, device);
+
+    for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+        checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, device, delayMs);
+    }
+}
+
+void AudioPolicyManagerBase::setStrategyMute(routing_strategy strategy, bool on, audio_io_handle_t output, int delayMs)
+{
+    LOGV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output);
+    for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+        if (getStrategy((AudioSystem::stream_type)stream) == strategy) {
+            setStreamMute(stream, on, output, delayMs);
+        }
+    }
+}
+
+void AudioPolicyManagerBase::setStreamMute(int stream, bool on, audio_io_handle_t output, int delayMs)
+{
+    StreamDescriptor &streamDesc = mStreams[stream];
+    AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+
+    LOGV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d", stream, on, output, outputDesc->mMuteCount[stream]);
+
+    if (on) {
+        if (outputDesc->mMuteCount[stream] == 0) {
+            if (streamDesc.mCanBeMuted) {
+                checkAndSetVolume(stream, 0, output, outputDesc->device(), delayMs);
+            }
+        }
+        // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
+        outputDesc->mMuteCount[stream]++;
+    } else {
+        if (outputDesc->mMuteCount[stream] == 0) {
+            LOGW("setStreamMute() unmuting non muted stream!");
+            return;
+        }
+        if (--outputDesc->mMuteCount[stream] == 0) {
+            checkAndSetVolume(stream, streamDesc.mIndexCur, output, outputDesc->device(), delayMs);
+        }
+    }
+}
+
+void AudioPolicyManagerBase::handleIncallSonification(int stream, bool starting, bool stateChange)
+{
+    // if the stream pertains to sonification strategy and we are in call we must
+    // mute the stream if it is low visibility. If it is high visibility, we must play a tone
+    // in the device used for phone strategy and play the tone if the selected device does not
+    // interfere with the device used for phone strategy
+    // if stateChange is true, we are called from setPhoneState() and we must mute or unmute as
+    // many times as there are active tracks on the output
+
+    if (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) {
+        AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mHardwareOutput);
+        LOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d",
+                stream, starting, outputDesc->mDevice, stateChange);
+        if (outputDesc->mRefCount[stream]) {
+            int muteCount = 1;
+            if (stateChange) {
+                muteCount = outputDesc->mRefCount[stream];
+            }
+            if (AudioSystem::isLowVisibility((AudioSystem::stream_type)stream)) {
+                LOGV("handleIncallSonification() low visibility, muteCount %d", muteCount);
+                for (int i = 0; i < muteCount; i++) {
+                    setStreamMute(stream, starting, mHardwareOutput);
+                }
+            } else {
+                LOGV("handleIncallSonification() high visibility");
+                if (outputDesc->device() & getDeviceForStrategy(STRATEGY_PHONE)) {
+                    LOGV("handleIncallSonification() high visibility muted, muteCount %d", muteCount);
+                    for (int i = 0; i < muteCount; i++) {
+                        setStreamMute(stream, starting, mHardwareOutput);
+                    }
+                }
+                if (starting) {
+                    mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL);
+                } else {
+                    mpClientInterface->stopTone();
+                }
+            }
+        }
+    }
+}
+
+// --- AudioOutputDescriptor class implementation
+
+AudioPolicyManagerBase::AudioOutputDescriptor::AudioOutputDescriptor()
+    : mId(0), mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0),
+    mFlags((AudioSystem::output_flags)0), mDevice(0), mOutput1(0), mOutput2(0)
+{
+    // clear usage count for all stream types
+    for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
+        mRefCount[i] = 0;
+        mCurVolume[i] = -1.0;
+        mMuteCount[i] = 0;
+    }
+}
+
+uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::device()
+{
+    uint32_t device = 0;
+    if (isDuplicated()) {
+        device = mOutput1->mDevice | mOutput2->mDevice;
+    } else {
+        device = mDevice;
+    }
+    return device;
+}
+
+void AudioPolicyManagerBase::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int delta)
+{
+    // forward usage count change to attached outputs
+    if (isDuplicated()) {
+        mOutput1->changeRefCount(stream, delta);
+        mOutput2->changeRefCount(stream, delta);
+    }
+    if ((delta + (int)mRefCount[stream]) < 0) {
+        LOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]);
+        mRefCount[stream] = 0;
+        return;
+    }
+    mRefCount[stream] += delta;
+    LOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);
+}
+
+uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::refCount()
+{
+    uint32_t refcount = 0;
+    for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+        refcount += mRefCount[i];
+    }
+    return refcount;
+}
+
+uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::strategyRefCount(routing_strategy strategy)
+{
+    uint32_t refCount = 0;
+    for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+        if (getStrategy((AudioSystem::stream_type)i) == strategy) {
+            refCount += mRefCount[i];
+        }
+    }
+    return refCount;
+}
+
+
+status_t AudioPolicyManagerBase::AudioOutputDescriptor::dump(int fd)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Format: %d\n", mFormat);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Channels: %08x\n", mChannels);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Latency: %d\n", mLatency);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Flags %08x\n", mFlags);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Devices %08x\n", device());
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Stream volume refCount muteCount\n");
+    result.append(buffer);
+    for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
+        snprintf(buffer, SIZE, " %02d     %.03f     %02d       %02d\n", i, mCurVolume[i], mRefCount[i], mMuteCount[i]);
+        result.append(buffer);
+    }
+    write(fd, result.string(), result.size());
+
+    return NO_ERROR;
+}
+
+// --- AudioInputDescriptor class implementation
+
+AudioPolicyManagerBase::AudioInputDescriptor::AudioInputDescriptor()
+    : mSamplingRate(0), mFormat(0), mChannels(0),
+     mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0)
+{
+}
+
+status_t AudioPolicyManagerBase::AudioInputDescriptor::dump(int fd)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Format: %d\n", mFormat);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Channels: %08x\n", mChannels);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Acoustics %08x\n", mAcoustics);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Devices %08x\n", mDevice);
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount);
+    result.append(buffer);
+    write(fd, result.string(), result.size());
+
+    return NO_ERROR;
+}
+
+// --- StreamDescriptor class implementation
+
+void AudioPolicyManagerBase::StreamDescriptor::dump(char* buffer, size_t size)
+{
+    snprintf(buffer, size, "      %02d         %02d         %02d         %d\n",
+            mIndexMin,
+            mIndexMax,
+            mIndexCur,
+            mCanBeMuted);
+}
+
+
+}; // namespace android
diff --git a/libs/audioflinger/AudioPolicyManagerGeneric.cpp b/libs/audioflinger/AudioPolicyManagerGeneric.cpp
deleted file mode 100644
index 8cfc204..0000000
--- a/libs/audioflinger/AudioPolicyManagerGeneric.cpp
+++ /dev/null
@@ -1,945 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "AudioPolicyManagerGeneric"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-#include "AudioPolicyManagerGeneric.h"
-#include <media/mediarecorder.h>
-
-namespace android {
-
-
-// ----------------------------------------------------------------------------
-// AudioPolicyInterface implementation
-// ----------------------------------------------------------------------------
-
-
-status_t AudioPolicyManagerGeneric::setDeviceConnectionState(AudioSystem::audio_devices device,
-                                                  AudioSystem::device_connection_state state,
-                                                  const char *device_address)
-{
-
-    LOGV("setDeviceConnectionState() device: %x, state %d, address %s", device, state, device_address);
-
-    // connect/disconnect only 1 device at a time
-    if (AudioSystem::popCount(device) != 1) return BAD_VALUE;
-
-    if (strlen(device_address) >= MAX_DEVICE_ADDRESS_LEN) {
-        LOGE("setDeviceConnectionState() invalid address: %s", device_address);
-        return BAD_VALUE;
-    }
-
-    // handle output devices
-    if (AudioSystem::isOutputDevice(device)) {
-        switch (state)
-        {
-        // handle output device connection
-        case AudioSystem::DEVICE_STATE_AVAILABLE:
-            if (mAvailableOutputDevices & device) {
-                LOGW("setDeviceConnectionState() device already connected: %x", device);
-                return INVALID_OPERATION;
-            }
-            LOGV("setDeviceConnectionState() connecting device %x", device);
-
-            // register new device as available
-            mAvailableOutputDevices |= device;
-            break;
-        // handle output device disconnection
-        case AudioSystem::DEVICE_STATE_UNAVAILABLE:
-            if (!(mAvailableOutputDevices & device)) {
-                LOGW("setDeviceConnectionState() device not connected: %x", device);
-                return INVALID_OPERATION;
-            }
-            LOGV("setDeviceConnectionState() disconnecting device %x", device);
-            // remove device from available output devices
-            mAvailableOutputDevices &= ~device;
-            break;
-
-        default:
-            LOGE("setDeviceConnectionState() invalid state: %x", state);
-            return BAD_VALUE;
-        }
-        return NO_ERROR;
-    }
-    // handle input devices
-    if (AudioSystem::isInputDevice(device)) {
-        switch (state)
-        {
-        // handle input device connection
-        case AudioSystem::DEVICE_STATE_AVAILABLE:
-            if (mAvailableInputDevices & device) {
-                LOGW("setDeviceConnectionState() device already connected: %d", device);
-                return INVALID_OPERATION;
-            }
-            mAvailableInputDevices |= device;
-            break;
-
-        // handle input device disconnection
-        case AudioSystem::DEVICE_STATE_UNAVAILABLE:
-            if (!(mAvailableInputDevices & device)) {
-                LOGW("setDeviceConnectionState() device not connected: %d", device);
-                return INVALID_OPERATION;
-            }
-            mAvailableInputDevices &= ~device;
-            break;
-
-        default:
-            LOGE("setDeviceConnectionState() invalid state: %x", state);
-            return BAD_VALUE;
-        }
-        return NO_ERROR;
-    }
-
-    LOGW("setDeviceConnectionState() invalid device: %x", device);
-    return BAD_VALUE;
-}
-
-AudioSystem::device_connection_state AudioPolicyManagerGeneric::getDeviceConnectionState(AudioSystem::audio_devices device,
-                                                  const char *device_address)
-{
-    AudioSystem::device_connection_state state = AudioSystem::DEVICE_STATE_UNAVAILABLE;
-    String8 address = String8(device_address);
-    if (AudioSystem::isOutputDevice(device)) {
-        if (device & mAvailableOutputDevices) {
-            state = AudioSystem::DEVICE_STATE_AVAILABLE;
-        }
-    } else if (AudioSystem::isInputDevice(device)) {
-        if (device & mAvailableInputDevices) {
-            state = AudioSystem::DEVICE_STATE_AVAILABLE;
-        }
-    }
-
-    return state;
-}
-
-void AudioPolicyManagerGeneric::setPhoneState(int state)
-{
-    LOGV("setPhoneState() state %d", state);
-    uint32_t newDevice = 0;
-    if (state < 0 || state >= AudioSystem::NUM_MODES) {
-        LOGW("setPhoneState() invalid state %d", state);
-        return;
-    }
-
-    if (state == mPhoneState ) {
-        LOGW("setPhoneState() setting same state %d", state);
-        return;
-    }
-    // store previous phone state for management of sonification strategy below
-    int oldState = mPhoneState;
-    mPhoneState = state;
-
-    // if leaving or entering in call state, handle special case of active streams
-    // pertaining to sonification strategy see handleIncallSonification()
-    if (state == AudioSystem::MODE_IN_CALL ||
-        oldState == AudioSystem::MODE_IN_CALL) {
-        bool starting = (state == AudioSystem::MODE_IN_CALL) ? true : false;
-        LOGV("setPhoneState() in call state management: new state is %d", state);
-        for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
-            handleIncallSonification(stream, starting);
-        }
-    }
-}
-
-void AudioPolicyManagerGeneric::setRingerMode(uint32_t mode, uint32_t mask)
-{
-    LOGV("setRingerMode() mode %x, mask %x", mode, mask);
-
-    mRingerMode = mode;
-}
-
-void AudioPolicyManagerGeneric::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
-{
-    LOGV("setForceUse) usage %d, config %d, mPhoneState %d", usage, config, mPhoneState);
-    mForceUse[usage] = config;
-}
-
-AudioSystem::forced_config AudioPolicyManagerGeneric::getForceUse(AudioSystem::force_use usage)
-{
-    return mForceUse[usage];
-}
-
-void AudioPolicyManagerGeneric::setSystemProperty(const char* property, const char* value)
-{
-    LOGV("setSystemProperty() property %s, value %s", property, value);
-    if (strcmp(property, "ro.camera.sound.forced") == 0) {
-        if (atoi(value)) {
-            LOGV("ENFORCED_AUDIBLE cannot be muted");
-            mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = false;
-        } else {
-            LOGV("ENFORCED_AUDIBLE can be muted");
-            mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = true;
-        }
-    }
-}
-
-audio_io_handle_t AudioPolicyManagerGeneric::getOutput(AudioSystem::stream_type stream,
-                                    uint32_t samplingRate,
-                                    uint32_t format,
-                                    uint32_t channels,
-                                    AudioSystem::output_flags flags)
-{
-    LOGV("getOutput() stream %d, samplingRate %d, format %d, channels %x, flags %x", stream, samplingRate, format, channels, flags);
-
-#ifdef AUDIO_POLICY_TEST
-    if (mCurOutput != 0) {
-        LOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channels %x, mDirectOutput %d",
-                mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput);
-
-        if (mTestOutputs[mCurOutput] == 0) {
-            LOGV("getOutput() opening test output");
-            AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
-            outputDesc->mDevice = mTestDevice;
-            outputDesc->mSamplingRate = mTestSamplingRate;
-            outputDesc->mFormat = mTestFormat;
-            outputDesc->mChannels = mTestChannels;
-            outputDesc->mLatency = mTestLatencyMs;
-            outputDesc->mFlags = (AudioSystem::output_flags)(mDirectOutput ? AudioSystem::OUTPUT_FLAG_DIRECT : 0);
-            outputDesc->mRefCount[stream] = 0;
-            mTestOutputs[mCurOutput] = mpClientInterface->openOutput(&outputDesc->mDevice,
-                                            &outputDesc->mSamplingRate,
-                                            &outputDesc->mFormat,
-                                            &outputDesc->mChannels,
-                                            &outputDesc->mLatency,
-                                            outputDesc->mFlags);
-            if (mTestOutputs[mCurOutput]) {
-                AudioParameter outputCmd = AudioParameter();
-                outputCmd.addInt(String8("set_id"),mCurOutput);
-                mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString());
-                mOutputs.add(mTestOutputs[mCurOutput], outputDesc);
-            }
-        }
-        return mTestOutputs[mCurOutput];
-    }
-#endif //AUDIO_POLICY_TEST
-    if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
-        (format != 0 && !AudioSystem::isLinearPCM(format)) ||
-        (channels != 0 && channels != AudioSystem::CHANNEL_OUT_MONO && channels != AudioSystem::CHANNEL_OUT_STEREO)) {
-        return 0;
-    }
-
-    return mHardwareOutput;
-}
-
-status_t AudioPolicyManagerGeneric::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
-{
-    LOGV("startOutput() output %d, stream %d", output, stream);
-    ssize_t index = mOutputs.indexOfKey(output);
-    if (index < 0) {
-        LOGW("startOutput() unknow output %d", output);
-        return BAD_VALUE;
-    }
-
-    AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
-
-    // handle special case for sonification while in call
-    if (mPhoneState == AudioSystem::MODE_IN_CALL) {
-        handleIncallSonification(stream, true);
-    }
-
-    // incremenent usage count for this stream on the requested output:
-    outputDesc->changeRefCount(stream, 1);
-    return NO_ERROR;
-}
-
-status_t AudioPolicyManagerGeneric::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
-{
-    LOGV("stopOutput() output %d, stream %d", output, stream);
-    ssize_t index = mOutputs.indexOfKey(output);
-    if (index < 0) {
-        LOGW("stopOutput() unknow output %d", output);
-        return BAD_VALUE;
-    }
-
-    AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
-
-    // handle special case for sonification while in call
-    if (mPhoneState == AudioSystem::MODE_IN_CALL) {
-        handleIncallSonification(stream, false);
-    }
-
-    if (outputDesc->isUsedByStream(stream)) {
-        // decrement usage count of this stream on the output
-        outputDesc->changeRefCount(stream, -1);
-        return NO_ERROR;
-    } else {
-        LOGW("stopOutput() refcount is already 0 for output %d", output);
-        return INVALID_OPERATION;
-    }
-}
-
-void AudioPolicyManagerGeneric::releaseOutput(audio_io_handle_t output)
-{
-    LOGV("releaseOutput() %d", output);
-    ssize_t index = mOutputs.indexOfKey(output);
-    if (index < 0) {
-        LOGW("releaseOutput() releasing unknown output %d", output);
-        return;
-    }
-
-#ifdef AUDIO_POLICY_TEST
-    int testIndex = testOutputIndex(output);
-    if (testIndex != 0) {
-        AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
-        if (outputDesc->refCount() == 0) {
-            mpClientInterface->closeOutput(output);
-            delete mOutputs.valueAt(index);
-            mOutputs.removeItem(output);
-            mTestOutputs[testIndex] = 0;
-        }
-    }
-#endif //AUDIO_POLICY_TEST
-}
-
-audio_io_handle_t AudioPolicyManagerGeneric::getInput(int inputSource,
-                                    uint32_t samplingRate,
-                                    uint32_t format,
-                                    uint32_t channels,
-                                    AudioSystem::audio_in_acoustics acoustics)
-{
-    audio_io_handle_t input = 0;
-    uint32_t device;
-
-    LOGV("getInput() inputSource %d, samplingRate %d, format %d, channels %x, acoustics %x", inputSource, samplingRate, format, channels, acoustics);
-
-    AudioInputDescriptor *inputDesc = new AudioInputDescriptor();
-    inputDesc->mDevice = AudioSystem::DEVICE_IN_BUILTIN_MIC;
-    inputDesc->mSamplingRate = samplingRate;
-    inputDesc->mFormat = format;
-    inputDesc->mChannels = channels;
-    inputDesc->mAcoustics = acoustics;
-    inputDesc->mRefCount = 0;
-    input = mpClientInterface->openInput(&inputDesc->mDevice,
-                                    &inputDesc->mSamplingRate,
-                                    &inputDesc->mFormat,
-                                    &inputDesc->mChannels,
-                                    inputDesc->mAcoustics);
-
-    // only accept input with the exact requested set of parameters
-    if ((samplingRate != inputDesc->mSamplingRate) ||
-        (format != inputDesc->mFormat) ||
-        (channels != inputDesc->mChannels)) {
-        LOGV("getOutput() failed opening input: samplingRate %d, format %d, channels %d",
-                samplingRate, format, channels);
-        mpClientInterface->closeInput(input);
-        delete inputDesc;
-        return 0;
-    }
-    mInputs.add(input, inputDesc);
-    return input;
-}
-
-status_t AudioPolicyManagerGeneric::startInput(audio_io_handle_t input)
-{
-    LOGV("startInput() input %d", input);
-    ssize_t index = mInputs.indexOfKey(input);
-    if (index < 0) {
-        LOGW("startInput() unknow input %d", input);
-        return BAD_VALUE;
-    }
-    AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
-
-#ifdef AUDIO_POLICY_TEST
-    if (mTestInput == 0)
-#endif //AUDIO_POLICY_TEST
-    {
-        // refuse 2 active AudioRecord clients at the same time
-        for (size_t i = 0; i < mInputs.size(); i++) {
-            if (mInputs.valueAt(i)->mRefCount > 0) {
-                LOGW("startInput() input %d, other input %d already started", input, mInputs.keyAt(i));
-                return INVALID_OPERATION;
-            }
-        }
-    }
-
-    inputDesc->mRefCount = 1;
-    return NO_ERROR;
-}
-
-status_t AudioPolicyManagerGeneric::stopInput(audio_io_handle_t input)
-{
-    LOGV("stopInput() input %d", input);
-    ssize_t index = mInputs.indexOfKey(input);
-    if (index < 0) {
-        LOGW("stopInput() unknow input %d", input);
-        return BAD_VALUE;
-    }
-    AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
-
-    if (inputDesc->mRefCount == 0) {
-        LOGW("stopInput() input %d already stopped", input);
-        return INVALID_OPERATION;
-    } else {
-        inputDesc->mRefCount = 0;
-        return NO_ERROR;
-    }
-}
-
-void AudioPolicyManagerGeneric::releaseInput(audio_io_handle_t input)
-{
-    LOGV("releaseInput() %d", input);
-    ssize_t index = mInputs.indexOfKey(input);
-    if (index < 0) {
-        LOGW("releaseInput() releasing unknown input %d", input);
-        return;
-    }
-    mpClientInterface->closeInput(input);
-    delete mInputs.valueAt(index);
-    mInputs.removeItem(input);
-}
-
-
-
-void AudioPolicyManagerGeneric::initStreamVolume(AudioSystem::stream_type stream,
-                                            int indexMin,
-                                            int indexMax)
-{
-    LOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax);
-    mStreams[stream].mIndexMin = indexMin;
-    mStreams[stream].mIndexMax = indexMax;
-}
-
-status_t AudioPolicyManagerGeneric::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
-{
-
-    if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) {
-        return BAD_VALUE;
-    }
-
-    LOGV("setStreamVolumeIndex() stream %d, index %d", stream, index);
-    mStreams[stream].mIndexCur = index;
-
-    // do not change actual stream volume if the stream is muted
-    if (mStreams[stream].mMuteCount != 0) {
-        return NO_ERROR;
-    }
-
-    // Do not changed in call volume if bluetooth is connected and vice versa
-    if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
-        (stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) {
-        LOGV("setStreamVolumeIndex() cannot set stream %d volume with force use = %d for comm",
-             stream, mForceUse[AudioSystem::FOR_COMMUNICATION]);
-        return INVALID_OPERATION;
-    }
-
-    // compute and apply stream volume on all outputs according to connected device
-    for (size_t i = 0; i < mOutputs.size(); i++) {
-        AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i);
-        uint32_t device = outputDesc->device();
-
-        float volume = computeVolume((int)stream, index, device);
-
-        LOGV("setStreamVolume() for output %d stream %d, volume %f", mOutputs.keyAt(i), stream, volume);
-        mpClientInterface->setStreamVolume(stream, volume, mOutputs.keyAt(i));
-    }
-    return NO_ERROR;
-}
-
-status_t AudioPolicyManagerGeneric::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index)
-{
-    if (index == 0) {
-        return BAD_VALUE;
-    }
-    LOGV("getStreamVolumeIndex() stream %d", stream);
-    *index =  mStreams[stream].mIndexCur;
-    return NO_ERROR;
-}
-
-status_t AudioPolicyManagerGeneric::dump(int fd)
-{
-    const size_t SIZE = 256;
-    char buffer[SIZE];
-    String8 result;
-
-    snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Hardware Output: %d\n", mHardwareOutput);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Output devices: %08x\n", mAvailableOutputDevices);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Input devices: %08x\n", mAvailableInputDevices);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Phone state: %d\n", mPhoneState);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Ringer mode: %d\n", mRingerMode);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Force use for communications %d\n", mForceUse[AudioSystem::FOR_COMMUNICATION]);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Force use for media %d\n", mForceUse[AudioSystem::FOR_MEDIA]);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Force use for record %d\n", mForceUse[AudioSystem::FOR_RECORD]);
-    result.append(buffer);
-    write(fd, result.string(), result.size());
-
-    snprintf(buffer, SIZE, "\nOutputs dump:\n");
-    write(fd, buffer, strlen(buffer));
-    for (size_t i = 0; i < mOutputs.size(); i++) {
-        snprintf(buffer, SIZE, "- Output %d dump:\n", mOutputs.keyAt(i));
-        write(fd, buffer, strlen(buffer));
-        mOutputs.valueAt(i)->dump(fd);
-    }
-
-    snprintf(buffer, SIZE, "\nInputs dump:\n");
-    write(fd, buffer, strlen(buffer));
-    for (size_t i = 0; i < mInputs.size(); i++) {
-        snprintf(buffer, SIZE, "- Input %d dump:\n", mInputs.keyAt(i));
-        write(fd, buffer, strlen(buffer));
-        mInputs.valueAt(i)->dump(fd);
-    }
-
-    snprintf(buffer, SIZE, "\nStreams dump:\n");
-    write(fd, buffer, strlen(buffer));
-    snprintf(buffer, SIZE, " Stream  Index Min  Index Max  Index Cur  Mute Count  Can be muted\n");
-    write(fd, buffer, strlen(buffer));
-    for (size_t i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
-        snprintf(buffer, SIZE, " %02d", i);
-        mStreams[i].dump(buffer + 3, SIZE);
-        write(fd, buffer, strlen(buffer));
-    }
-
-    return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-// AudioPolicyManagerGeneric
-// ----------------------------------------------------------------------------
-
-// ---  class factory
-
-AudioPolicyManagerGeneric::AudioPolicyManagerGeneric(AudioPolicyClientInterface *clientInterface)
-    :
-#ifdef AUDIO_POLICY_TEST
-    Thread(false),
-#endif //AUDIO_POLICY_TEST
-    mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0)
-{
-    mpClientInterface = clientInterface;
-
-    for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
-        mForceUse[i] = AudioSystem::FORCE_NONE;
-    }
-
-    // devices available by default are speaker, ear piece and microphone
-    mAvailableOutputDevices = AudioSystem::DEVICE_OUT_SPEAKER;
-    mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;
-
-    // open hardware output
-    AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
-    outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
-    mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
-                                    &outputDesc->mSamplingRate,
-                                    &outputDesc->mFormat,
-                                    &outputDesc->mChannels,
-                                    &outputDesc->mLatency,
-                                    outputDesc->mFlags);
-
-    if (mHardwareOutput == 0) {
-        LOGE("Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d",
-                outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
-    } else {
-        mOutputs.add(mHardwareOutput, outputDesc);
-    }
-
-#ifdef AUDIO_POLICY_TEST
-    AudioParameter outputCmd = AudioParameter();
-    outputCmd.addInt(String8("set_id"), 0);
-    mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
-
-    mTestDevice = AudioSystem::DEVICE_OUT_SPEAKER;
-    mTestSamplingRate = 44100;
-    mTestFormat = AudioSystem::PCM_16_BIT;
-    mTestChannels =  AudioSystem::CHANNEL_OUT_STEREO;
-    mTestLatencyMs = 0;
-    mCurOutput = 0;
-    mDirectOutput = false;
-    for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
-        mTestOutputs[i] = 0;
-    }
-
-    const size_t SIZE = 256;
-    char buffer[SIZE];
-    snprintf(buffer, SIZE, "AudioPolicyManagerTest");
-    run(buffer, ANDROID_PRIORITY_AUDIO);
-#endif //AUDIO_POLICY_TEST
-}
-
-AudioPolicyManagerGeneric::~AudioPolicyManagerGeneric()
-{
-#ifdef AUDIO_POLICY_TEST
-    exit();
-#endif //AUDIO_POLICY_TEST
-
-   for (size_t i = 0; i < mOutputs.size(); i++) {
-        mpClientInterface->closeOutput(mOutputs.keyAt(i));
-        delete mOutputs.valueAt(i);
-   }
-   mOutputs.clear();
-   for (size_t i = 0; i < mInputs.size(); i++) {
-        mpClientInterface->closeInput(mInputs.keyAt(i));
-        delete mInputs.valueAt(i);
-   }
-   mInputs.clear();
-}
-
-#ifdef AUDIO_POLICY_TEST
-bool AudioPolicyManagerGeneric::threadLoop()
-{
-    LOGV("entering threadLoop()");
-    while (!exitPending())
-    {
-        String8 command;
-        int valueInt;
-        String8 value;
-
-        Mutex::Autolock _l(mLock);
-        mWaitWorkCV.waitRelative(mLock, milliseconds(50));
-
-        command = mpClientInterface->getParameters(0, String8("test_cmd_policy"));
-        AudioParameter param = AudioParameter(command);
-
-        if (param.getInt(String8("test_cmd_policy"), valueInt) == NO_ERROR &&
-            valueInt != 0) {
-            LOGV("Test command %s received", command.string());
-            String8 target;
-            if (param.get(String8("target"), target) != NO_ERROR) {
-                target = "Manager";
-            }
-            if (param.getInt(String8("test_cmd_policy_output"), valueInt) == NO_ERROR) {
-                param.remove(String8("test_cmd_policy_output"));
-                mCurOutput = valueInt;
-            }
-            if (param.get(String8("test_cmd_policy_direct"), value) == NO_ERROR) {
-                param.remove(String8("test_cmd_policy_direct"));
-                if (value == "false") {
-                    mDirectOutput = false;
-                } else if (value == "true") {
-                    mDirectOutput = true;
-                }
-            }
-            if (param.getInt(String8("test_cmd_policy_input"), valueInt) == NO_ERROR) {
-                param.remove(String8("test_cmd_policy_input"));
-                mTestInput = valueInt;
-            }
-
-            if (param.get(String8("test_cmd_policy_format"), value) == NO_ERROR) {
-                param.remove(String8("test_cmd_policy_format"));
-                int format = AudioSystem::INVALID_FORMAT;
-                if (value == "PCM 16 bits") {
-                    format = AudioSystem::PCM_16_BIT;
-                } else if (value == "PCM 8 bits") {
-                    format = AudioSystem::PCM_8_BIT;
-                } else if (value == "Compressed MP3") {
-                    format = AudioSystem::MP3;
-                }
-                if (format != AudioSystem::INVALID_FORMAT) {
-                    if (target == "Manager") {
-                        mTestFormat = format;
-                    } else if (mTestOutputs[mCurOutput] != 0) {
-                        AudioParameter outputParam = AudioParameter();
-                        outputParam.addInt(String8("format"), format);
-                        mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
-                    }
-                }
-            }
-            if (param.get(String8("test_cmd_policy_channels"), value) == NO_ERROR) {
-                param.remove(String8("test_cmd_policy_channels"));
-                int channels = 0;
-
-                if (value == "Channels Stereo") {
-                    channels =  AudioSystem::CHANNEL_OUT_STEREO;
-                } else if (value == "Channels Mono") {
-                    channels =  AudioSystem::CHANNEL_OUT_MONO;
-                }
-                if (channels != 0) {
-                    if (target == "Manager") {
-                        mTestChannels = channels;
-                    } else if (mTestOutputs[mCurOutput] != 0) {
-                        AudioParameter outputParam = AudioParameter();
-                        outputParam.addInt(String8("channels"), channels);
-                        mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
-                    }
-                }
-            }
-            if (param.getInt(String8("test_cmd_policy_sampleRate"), valueInt) == NO_ERROR) {
-                param.remove(String8("test_cmd_policy_sampleRate"));
-                if (valueInt >= 0 && valueInt <= 96000) {
-                    int samplingRate = valueInt;
-                    if (target == "Manager") {
-                        mTestSamplingRate = samplingRate;
-                    } else if (mTestOutputs[mCurOutput] != 0) {
-                        AudioParameter outputParam = AudioParameter();
-                        outputParam.addInt(String8("sampling_rate"), samplingRate);
-                        mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
-                    }
-                }
-            }
-
-            if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) {
-                param.remove(String8("test_cmd_policy_reopen"));
-
-                mpClientInterface->closeOutput(mHardwareOutput);
-                delete mOutputs.valueFor(mHardwareOutput);
-                mOutputs.removeItem(mHardwareOutput);
-
-                AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
-                outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
-                mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
-                                                &outputDesc->mSamplingRate,
-                                                &outputDesc->mFormat,
-                                                &outputDesc->mChannels,
-                                                &outputDesc->mLatency,
-                                                outputDesc->mFlags);
-                if (mHardwareOutput == 0) {
-                    LOGE("Failed to reopen hardware output stream, samplingRate: %d, format %d, channels %d",
-                            outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
-                } else {
-                    AudioParameter outputCmd = AudioParameter();
-                    outputCmd.addInt(String8("set_id"), 0);
-                    mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
-                    mOutputs.add(mHardwareOutput, outputDesc);
-                }
-            }
-
-
-            mpClientInterface->setParameters(0, String8("test_cmd_policy="));
-        }
-    }
-    return false;
-}
-
-void AudioPolicyManagerGeneric::exit()
-{
-    {
-        AutoMutex _l(mLock);
-        requestExit();
-        mWaitWorkCV.signal();
-    }
-    requestExitAndWait();
-}
-
-int AudioPolicyManagerGeneric::testOutputIndex(audio_io_handle_t output)
-{
-    for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
-        if (output == mTestOutputs[i]) return i;
-    }
-    return 0;
-}
-#endif //AUDIO_POLICY_TEST
-
-// ---
-
-AudioPolicyManagerGeneric::routing_strategy AudioPolicyManagerGeneric::getStrategy(AudioSystem::stream_type stream)
-{
-    // stream to strategy mapping
-    switch (stream) {
-    case AudioSystem::VOICE_CALL:
-    case AudioSystem::BLUETOOTH_SCO:
-        return STRATEGY_PHONE;
-    case AudioSystem::RING:
-    case AudioSystem::NOTIFICATION:
-    case AudioSystem::ALARM:
-    case AudioSystem::ENFORCED_AUDIBLE:
-        return STRATEGY_SONIFICATION;
-    case AudioSystem::DTMF:
-        return STRATEGY_DTMF;
-    default:
-        LOGE("unknown stream type");
-    case AudioSystem::SYSTEM:
-        // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs
-        // while key clicks are played produces a poor result
-    case AudioSystem::TTS:
-    case AudioSystem::MUSIC:
-        return STRATEGY_MEDIA;
-    }
-}
-
-
-float AudioPolicyManagerGeneric::computeVolume(int stream, int index, uint32_t device)
-{
-    float volume = 1.0;
-
-    StreamDescriptor &streamDesc = mStreams[stream];
-
-    // Force max volume if stream cannot be muted
-    if (!streamDesc.mCanBeMuted) index = streamDesc.mIndexMax;
-
-    int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin);
-    volume = AudioSystem::linearToLog(volInt);
-
-    return volume;
-}
-
-void AudioPolicyManagerGeneric::setStreamMute(int stream, bool on, audio_io_handle_t output)
-{
-    LOGV("setStreamMute() stream %d, mute %d, output %d", stream, on, output);
-
-    StreamDescriptor &streamDesc = mStreams[stream];
-
-    if (on) {
-        if (streamDesc.mMuteCount++ == 0) {
-            if (streamDesc.mCanBeMuted) {
-                mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, 0, output);
-            }
-        }
-    } else {
-        if (streamDesc.mMuteCount == 0) {
-            LOGW("setStreamMute() unmuting non muted stream!");
-            return;
-        }
-        if (--streamDesc.mMuteCount == 0) {
-            uint32_t device = mOutputs.valueFor(output)->mDevice;
-            float volume = computeVolume(stream, streamDesc.mIndexCur, device);
-            mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output);
-        }
-    }
-}
-
-void AudioPolicyManagerGeneric::handleIncallSonification(int stream, bool starting)
-{
-    // if the stream pertains to sonification strategy and we are in call we must
-    // mute the stream if it is low visibility. If it is high visibility, we must play a tone
-    // in the device used for phone strategy and play the tone if the selected device does not
-    // interfere with the device used for phone strategy
-    if (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) {
-        AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mHardwareOutput);
-        LOGV("handleIncallSonification() stream %d starting %d device %x", stream, starting, outputDesc->mDevice);
-        if (outputDesc->isUsedByStream((AudioSystem::stream_type)stream)) {
-            if (AudioSystem::isLowVisibility((AudioSystem::stream_type)stream)) {
-                LOGV("handleIncallSonification() low visibility");
-                setStreamMute(stream, starting, mHardwareOutput);
-            } else {
-                if (starting) {
-                    mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL);
-                } else {
-                    mpClientInterface->stopTone();
-                }
-            }
-        }
-    }
-}
-
-
-// --- AudioOutputDescriptor class implementation
-
-AudioPolicyManagerGeneric::AudioOutputDescriptor::AudioOutputDescriptor()
-    : mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0),
-    mFlags((AudioSystem::output_flags)0), mDevice(0)
-{
-    // clear usage count for all stream types
-    for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
-        mRefCount[i] = 0;
-    }
-}
-
-uint32_t AudioPolicyManagerGeneric::AudioOutputDescriptor::device()
-{
-    return mDevice;
-}
-
-void AudioPolicyManagerGeneric::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int delta)
-{
-    if ((delta + (int)mRefCount[stream]) < 0) {
-        LOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]);
-        mRefCount[stream] = 0;
-        return;
-    }
-    mRefCount[stream] += delta;
-    LOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);
-}
-
-uint32_t AudioPolicyManagerGeneric::AudioOutputDescriptor::refCount()
-{
-    uint32_t refcount = 0;
-    for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
-        refcount += mRefCount[i];
-    }
-    return refcount;
-}
-
-status_t AudioPolicyManagerGeneric::AudioOutputDescriptor::dump(int fd)
-{
-    const size_t SIZE = 256;
-    char buffer[SIZE];
-    String8 result;
-
-    snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Format: %d\n", mFormat);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Channels: %08x\n", mChannels);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Latency: %d\n", mLatency);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Flags %08x\n", mFlags);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Devices %08x\n", mDevice);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Stream refCount\n");
-    result.append(buffer);
-    for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
-        snprintf(buffer, SIZE, " %02d     %d\n", i, mRefCount[i]);
-        result.append(buffer);
-    }
-    write(fd, result.string(), result.size());
-
-    return NO_ERROR;
-}
-
-// --- AudioInputDescriptor class implementation
-
-AudioPolicyManagerGeneric::AudioInputDescriptor::AudioInputDescriptor()
-    : mSamplingRate(0), mFormat(0), mChannels(0),
-     mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0)
-{
-}
-
-status_t AudioPolicyManagerGeneric::AudioInputDescriptor::dump(int fd)
-{
-    const size_t SIZE = 256;
-    char buffer[SIZE];
-    String8 result;
-
-    snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Format: %d\n", mFormat);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Channels: %08x\n", mChannels);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Acoustics %08x\n", mAcoustics);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Devices %08x\n", mDevice);
-    result.append(buffer);
-    snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount);
-    result.append(buffer);
-    write(fd, result.string(), result.size());
-
-    return NO_ERROR;
-}
-
-// --- StreamDescriptor class implementation
-
-void AudioPolicyManagerGeneric::StreamDescriptor::dump(char* buffer, size_t size)
-{
-    snprintf(buffer, size, "      %02d         %02d         %02d         %02d          %d\n",
-            mIndexMin,
-            mIndexMax,
-            mIndexCur,
-            mMuteCount,
-            mCanBeMuted);
-}
-
-}; // namespace android
diff --git a/libs/audioflinger/AudioPolicyManagerGeneric.h b/libs/audioflinger/AudioPolicyManagerGeneric.h
deleted file mode 100644
index 4997cdf..0000000
--- a/libs/audioflinger/AudioPolicyManagerGeneric.h
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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.
- */
-
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/KeyedVector.h>
-#include <hardware_legacy/AudioPolicyInterface.h>
-#include <utils/threads.h>
-
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-#define MAX_DEVICE_ADDRESS_LEN 20
-#define NUM_TEST_OUTPUTS 5
-
-class AudioPolicyManagerGeneric: public AudioPolicyInterface
-#ifdef AUDIO_POLICY_TEST
-    , public Thread
-#endif //AUDIO_POLICY_TEST
-{
-
-public:
-                AudioPolicyManagerGeneric(AudioPolicyClientInterface *clientInterface);
-        virtual ~AudioPolicyManagerGeneric();
-
-        // AudioPolicyInterface
-        virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device,
-                                                          AudioSystem::device_connection_state state,
-                                                          const char *device_address);
-        virtual AudioSystem::device_connection_state getDeviceConnectionState(AudioSystem::audio_devices device,
-                                                                              const char *device_address);
-        virtual void setPhoneState(int state);
-        virtual void setRingerMode(uint32_t mode, uint32_t mask);
-        virtual void setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config);
-        virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage);
-        virtual void setSystemProperty(const char* property, const char* value);
-        virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream,
-                                            uint32_t samplingRate,
-                                            uint32_t format,
-                                            uint32_t channels,
-                                            AudioSystem::output_flags flags);
-        virtual status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
-        virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
-        virtual void releaseOutput(audio_io_handle_t output);
-        virtual audio_io_handle_t getInput(int inputSource,
-                                            uint32_t samplingRate,
-                                            uint32_t format,
-                                            uint32_t channels,
-                                            AudioSystem::audio_in_acoustics acoustics);
-        // indicates to the audio policy manager that the input starts being used.
-        virtual status_t startInput(audio_io_handle_t input);
-        // indicates to the audio policy manager that the input stops being used.
-        virtual status_t stopInput(audio_io_handle_t input);
-        virtual void releaseInput(audio_io_handle_t input);
-        virtual void initStreamVolume(AudioSystem::stream_type stream,
-                                                    int indexMin,
-                                                    int indexMax);
-        virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index);
-        virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index);
-
-        virtual status_t dump(int fd);
-
-private:
-
-        enum routing_strategy {
-            STRATEGY_MEDIA,
-            STRATEGY_PHONE,
-            STRATEGY_SONIFICATION,
-            STRATEGY_DTMF,
-            NUM_STRATEGIES
-        };
-
-        // descriptor for audio outputs. Used to maintain current configuration of each opened audio output
-        // and keep track of the usage of this output by each audio stream type.
-        class AudioOutputDescriptor
-        {
-        public:
-            AudioOutputDescriptor();
-
-            status_t    dump(int fd);
-
-            uint32_t device();
-            void changeRefCount(AudioSystem::stream_type, int delta);
-            bool isUsedByStream(AudioSystem::stream_type stream) { return mRefCount[stream] > 0 ? true : false; }
-            uint32_t refCount();
-
-            uint32_t mSamplingRate;             //
-            uint32_t mFormat;                   //
-            uint32_t mChannels;                 // output configuration
-            uint32_t mLatency;                  //
-            AudioSystem::output_flags mFlags;   //
-            uint32_t mDevice;                   // current device this output is routed to
-            uint32_t mRefCount[AudioSystem::NUM_STREAM_TYPES]; // number of streams of each type using this output
-        };
-
-        // descriptor for audio inputs. Used to maintain current configuration of each opened audio input
-        // and keep track of the usage of this input.
-        class AudioInputDescriptor
-        {
-        public:
-            AudioInputDescriptor();
-
-            status_t    dump(int fd);
-
-            uint32_t mSamplingRate;                     //
-            uint32_t mFormat;                           // input configuration
-            uint32_t mChannels;                         //
-            AudioSystem::audio_in_acoustics mAcoustics; //
-            uint32_t mDevice;                           // current device this input is routed to
-            uint32_t mRefCount;                         // number of AudioRecord clients using this output
-        };
-
-        // stream descriptor used for volume control
-        class StreamDescriptor
-        {
-        public:
-            StreamDescriptor()
-            :   mIndexMin(0), mIndexMax(1), mIndexCur(1), mMuteCount(0), mCanBeMuted(true) {}
-
-            void dump(char* buffer, size_t size);
-
-            int mIndexMin;      // min volume index
-            int mIndexMax;      // max volume index
-            int mIndexCur;      // current volume index
-            int mMuteCount;     // mute request counter
-            bool mCanBeMuted;   // true is the stream can be muted
-        };
-
-        // return the strategy corresponding to a given stream type
-        static routing_strategy getStrategy(AudioSystem::stream_type stream);
-        // return the output handle of an output routed to the specified device, 0 if no output
-        // is routed to the device
-        float computeVolume(int stream, int index, uint32_t device);
-        // Mute or unmute the stream on the specified output
-        void setStreamMute(int stream, bool on, audio_io_handle_t output);
-        // handle special cases for sonification strategy while in call: mute streams or replace by
-        // a special tone in the device used for communication
-        void handleIncallSonification(int stream, bool starting);
-
-
-#ifdef AUDIO_POLICY_TEST
-        virtual     bool        threadLoop();
-                    void        exit();
-        int testOutputIndex(audio_io_handle_t output);
-#endif //AUDIO_POLICY_TEST
-
-
-        AudioPolicyClientInterface *mpClientInterface;  // audio policy client interface
-        audio_io_handle_t mHardwareOutput;              // hardware output handler
-
-        KeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs;   // list ot output descritors
-        KeyedVector<audio_io_handle_t, AudioInputDescriptor *> mInputs;     // list of input descriptors
-        uint32_t mAvailableOutputDevices;                                   // bit field of all available output devices
-        uint32_t mAvailableInputDevices;                                    // bit field of all available input devices
-        int mPhoneState;                                                    // current phone state
-        uint32_t                 mRingerMode;                               // current ringer mode
-        AudioSystem::forced_config mForceUse[AudioSystem::NUM_FORCE_USE];   // current forced use configuration
-
-        StreamDescriptor mStreams[AudioSystem::NUM_STREAM_TYPES];           // stream descriptors for volume control
-
-#ifdef AUDIO_POLICY_TEST
-        Mutex   mLock;
-        Condition mWaitWorkCV;
-
-        int             mCurOutput;
-        bool            mDirectOutput;
-        audio_io_handle_t mTestOutputs[NUM_TEST_OUTPUTS];
-        int             mTestInput;
-        uint32_t        mTestDevice;
-        uint32_t        mTestSamplingRate;
-        uint32_t        mTestFormat;
-        uint32_t        mTestChannels;
-        uint32_t        mTestLatencyMs;
-#endif //AUDIO_POLICY_TEST
-
-};
-
-};
diff --git a/libs/audioflinger/AudioPolicyService.cpp b/libs/audioflinger/AudioPolicyService.cpp
index aa48019..bb3905c 100644
--- a/libs/audioflinger/AudioPolicyService.cpp
+++ b/libs/audioflinger/AudioPolicyService.cpp
@@ -30,9 +30,10 @@
 #include <utils/String16.h>
 #include <utils/threads.h>
 #include "AudioPolicyService.h"
-#include "AudioPolicyManagerGeneric.h"
+#include <hardware_legacy/AudioPolicyManagerBase.h>
 #include <cutils/properties.h>
 #include <dlfcn.h>
+#include <hardware_legacy/power.h>
 
 // ----------------------------------------------------------------------------
 // the sim build doesn't have gettid
@@ -43,8 +44,9 @@
 
 namespace android {
 
-static const char* kDeadlockedString = "AudioPolicyService may be deadlocked\n";
-static const char* kCmdDeadlockedString = "AudioPolicyService command thread may be deadlocked\n";
+
+static const char *kDeadlockedString = "AudioPolicyService may be deadlocked\n";
+static const char *kCmdDeadlockedString = "AudioPolicyService command thread may be deadlocked\n";
 
 static const int kDumpLockRetries = 50;
 static const int kDumpLockSleep = 20000;
@@ -67,18 +69,18 @@
     char value[PROPERTY_VALUE_MAX];
 
     // start tone playback thread
-    mTonePlaybackThread = new AudioCommandThread();
+    mTonePlaybackThread = new AudioCommandThread(String8(""));
     // start audio commands thread
-    mAudioCommandThread = new AudioCommandThread();
+    mAudioCommandThread = new AudioCommandThread(String8("ApmCommandThread"));
 
 #if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST)
-    mpPolicyManager = new AudioPolicyManagerGeneric(this);
+    mpPolicyManager = new AudioPolicyManagerBase(this);
     LOGV("build for GENERIC_AUDIO - using generic audio policy");
 #else
     // if running in emulation - use the emulator driver
     if (property_get("ro.kernel.qemu", value, 0)) {
         LOGV("Running in emulation - using generic audio policy");
-        mpPolicyManager = new AudioPolicyManagerGeneric(this);
+        mpPolicyManager = new AudioPolicyManagerBase(this);
     }
     else {
         LOGV("Using hardware specific audio policy");
@@ -556,8 +558,8 @@
 
 // -----------  AudioPolicyService::AudioCommandThread implementation ----------
 
-AudioPolicyService::AudioCommandThread::AudioCommandThread()
-    :   Thread(false)
+AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name)
+    : Thread(false), mName(name)
 {
     mpToneGenerator = NULL;
 }
@@ -565,18 +567,20 @@
 
 AudioPolicyService::AudioCommandThread::~AudioCommandThread()
 {
+    if (mName != "" && !mAudioCommands.isEmpty()) {
+        release_wake_lock(mName.string());
+    }
     mAudioCommands.clear();
     if (mpToneGenerator != NULL) delete mpToneGenerator;
 }
 
 void AudioPolicyService::AudioCommandThread::onFirstRef()
 {
-    const size_t SIZE = 256;
-    char buffer[SIZE];
-
-    snprintf(buffer, SIZE, "AudioCommandThread");
-
-    run(buffer, ANDROID_PRIORITY_AUDIO);
+    if (mName != "") {
+        run(mName.string(), ANDROID_PRIORITY_AUDIO);
+    } else {
+        run("AudioCommandThread", ANDROID_PRIORITY_AUDIO);
+    }
 }
 
 bool AudioPolicyService::AudioCommandThread::threadLoop()
@@ -657,6 +661,10 @@
                 break;
             }
         }
+        // release delayed commands wake lock
+        if (mName != "" && mAudioCommands.isEmpty()) {
+            release_wake_lock(mName.string());
+        }
         LOGV("AudioCommandThread() going to sleep");
         mWaitWorkCV.waitRelative(mLock, waitTime);
         LOGV("AudioCommandThread() waking up");
@@ -815,6 +823,11 @@
 
     command->mTime = systemTime() + milliseconds(delayMs);
 
+    // acquire wake lock to make sure delayed commands are processed
+    if (mName != "" && mAudioCommands.isEmpty()) {
+        acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string());
+    }
+
     // check same pending commands with later time stamps and eliminate them
     for (i = mAudioCommands.size()-1; i >= 0; i--) {
         AudioCommand *command2 = mAudioCommands[i];
@@ -883,7 +896,7 @@
     removedCommands.clear();
 
     // insert command at the right place according to its time stamp
-    LOGV("inserting command: %d at index %ld, num commands %d", command->mCommand, i+1, mAudioCommands.size());
+    LOGV("inserting command: %d at index %d, num commands %d", command->mCommand, (int)i+1, mAudioCommands.size());
     mAudioCommands.insertAt(command, i + 1);
 }
 
diff --git a/libs/audioflinger/AudioPolicyService.h b/libs/audioflinger/AudioPolicyService.h
index b9234ec..a13d0bd 100644
--- a/libs/audioflinger/AudioPolicyService.h
+++ b/libs/audioflinger/AudioPolicyService.h
@@ -132,7 +132,7 @@
             SET_VOICE_VOLUME
         };
 
-        AudioCommandThread ();
+        AudioCommandThread (String8 name);
         virtual             ~AudioCommandThread();
 
                     status_t    dump(int fd);
@@ -195,7 +195,8 @@
         Condition mWaitWorkCV;
         Vector <AudioCommand *> mAudioCommands; // list of pending commands
         ToneGenerator *mpToneGenerator;     // the tone generator
-        AudioCommand mLastCommand;
+        AudioCommand mLastCommand;          // last processed command (used by dump)
+        String8 mName;                      // string used by wake lock fo delayed commands
     };
 
     // Internal dump utilities.
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index b4ec1a2..1ae2317 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -366,6 +366,25 @@
     }
 }
 
+static void mip8(const Adapter2D &out, const Adapter2D &in)
+{
+    uint32_t w = out.getDimX();
+    uint32_t h = out.getDimY();
+
+    for (uint32_t y=0; y < h; y++) {
+        uint8_t *oPtr = static_cast<uint8_t *>(out.getElement(0, y));
+        const uint8_t *i1 = static_cast<uint8_t *>(in.getElement(0, y*2));
+        const uint8_t *i2 = static_cast<uint8_t *>(in.getElement(0, y*2+1));
+
+        for (uint32_t x=0; x < w; x++) {
+            *oPtr = (uint8_t)(((uint32_t)i1[0] + i1[1] + i2[0] + i2[1]) * 0.25f);
+            oPtr ++;
+            i1 += 2;
+            i2 += 2;
+        }
+    }
+}
+
 static void mip(const Adapter2D &out, const Adapter2D &in)
 {
     switch(out.getBaseType()->getElement()->getSizeBits()) {
@@ -375,6 +394,9 @@
     case 16:
         mip565(out, in);
         break;
+    case 8:
+        mip8(out, in);
+        break;
 
     }
 
diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp
index 2ff6167..dbcb367 100644
--- a/libs/surfaceflinger/LayerBuffer.cpp
+++ b/libs/surfaceflinger/LayerBuffer.cpp
@@ -261,7 +261,7 @@
 // ============================================================================
 
 LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers, ssize_t offset)
-    : mBufferHeap(buffers)
+    : mBufferHeap(buffers), mSupportsCopybit(false)
 {
     NativeBuffer& src(mNativeBuffer);
     src.crop.l = 0;
@@ -283,10 +283,8 @@
                 offset, buffers.heap->base(),
                 &src.img.handle);
 
-        LOGE_IF(err, "CREATE_HANDLE_FROM_BUFFER (heapId=%d, size=%d, "
-             "offset=%ld, base=%p) failed (%s)",
-                buffers.heap->heapID(), buffers.heap->getSize(),
-                offset, buffers.heap->base(), strerror(-err));
+        // we can fail here is the passed buffer is purely software
+        mSupportsCopybit = (err == NO_ERROR);
     }
  }
 
@@ -453,7 +451,7 @@
 #if defined(EGL_ANDROID_image_native_buffer)
     if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) {
         copybit_device_t* copybit = mLayer.mBlitEngine;
-        if (copybit) {
+        if (copybit && ourBuffer->supportsCopybit()) {
             // create our EGLImageKHR the first time
             err = initTempBuffer();
             if (err == NO_ERROR) {
diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h
index 2ca63ac..90f83c4 100644
--- a/libs/surfaceflinger/LayerBuffer.h
+++ b/libs/surfaceflinger/LayerBuffer.h
@@ -99,6 +99,9 @@
     class Buffer : public LightRefBase<Buffer> {
     public:
         Buffer(const ISurface::BufferHeap& buffers, ssize_t offset);
+        inline bool supportsCopybit() const {
+            return mSupportsCopybit;
+        }
         inline status_t getStatus() const {
             return mBufferHeap.heap!=0 ? NO_ERROR : NO_INIT;
         }
@@ -113,6 +116,7 @@
     private:
         ISurface::BufferHeap    mBufferHeap;
         NativeBuffer            mNativeBuffer;
+        bool                    mSupportsCopybit;
     };
 
     class BufferSource : public Source {
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index fc234ee..4ae4ec9b 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -24,7 +24,8 @@
     IAudioPolicyService.cpp \
     MediaScanner.cpp \
     MediaScannerClient.cpp \
-    autodetect.cpp
+    autodetect.cpp \
+    IMediaDeathNotifier.cpp
 
 LOCAL_SHARED_LIBRARIES := \
 	libui libcutils libutils libbinder libsonivox libicuuc
diff --git a/media/libmedia/IMediaDeathNotifier.cpp b/media/libmedia/IMediaDeathNotifier.cpp
new file mode 100644
index 0000000..39ac076
--- /dev/null
+++ b/media/libmedia/IMediaDeathNotifier.cpp
@@ -0,0 +1,111 @@
+/*
+** 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.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "IMediaDeathNotifier"
+#include <utils/Log.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <media/IMediaDeathNotifier.h>
+
+namespace android {
+
+// client singleton for binder interface to services
+Mutex IMediaDeathNotifier::sServiceLock;
+sp<IMediaPlayerService> IMediaDeathNotifier::sMediaPlayerService;
+sp<IMediaDeathNotifier::DeathNotifier> IMediaDeathNotifier::sDeathNotifier;
+SortedVector< wp<IMediaDeathNotifier> > IMediaDeathNotifier::sObitRecipients;
+
+// establish binder interface to MediaPlayerService
+/*static*/const sp<IMediaPlayerService>&
+IMediaDeathNotifier::getMediaPlayerService()
+{
+    LOGV("getMediaPlayerService");
+    Mutex::Autolock _l(sServiceLock);
+    if (sMediaPlayerService.get() == 0) {
+        sp<IServiceManager> sm = defaultServiceManager();
+        sp<IBinder> binder;
+        do {
+            binder = sm->getService(String16("media.player"));
+            if (binder != 0) {
+                break;
+             }
+             LOGW("Media player service not published, waiting...");
+             usleep(500000); // 0.5 s
+        } while(true);
+
+        if (sDeathNotifier == NULL) {
+        sDeathNotifier = new DeathNotifier();
+    }
+    binder->linkToDeath(sDeathNotifier);
+    sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
+    }
+    LOGE_IF(sMediaPlayerService == 0, "no media player service!?");
+    return sMediaPlayerService;
+}
+
+/*static*/ void
+IMediaDeathNotifier::addObitRecipient(const wp<IMediaDeathNotifier>& recipient)
+{
+    LOGV("addObitRecipient");
+    Mutex::Autolock _l(sServiceLock);
+    sObitRecipients.add(recipient);
+}
+
+/*static*/ void
+IMediaDeathNotifier::removeObitRecipient(const wp<IMediaDeathNotifier>& recipient)
+{
+    LOGV("removeObitRecipient");
+    Mutex::Autolock _l(sServiceLock);
+    sObitRecipients.remove(recipient);
+}
+
+void
+IMediaDeathNotifier::DeathNotifier::binderDied(const wp<IBinder>& who) {
+    LOGW("media server died");
+
+    // Need to do this with the lock held
+    SortedVector< wp<IMediaDeathNotifier> > list;
+    {
+        Mutex::Autolock _l(sServiceLock);
+        sMediaPlayerService.clear();
+        list = sObitRecipients;
+    }
+
+    // Notify application when media server dies.
+    // Don't hold the static lock during callback in case app
+    // makes a call that needs the lock.
+    size_t count = list.size();
+    for (size_t iter = 0; iter < count; ++iter) {
+        sp<IMediaDeathNotifier> notifier = list[iter].promote();
+        if (notifier != 0) {
+            notifier->died();
+        }
+    }
+}
+
+IMediaDeathNotifier::DeathNotifier::~DeathNotifier()
+{
+    LOGV("DeathNotifier::~DeathNotifier");
+    Mutex::Autolock _l(sServiceLock);
+    sObitRecipients.clear();
+    if (sMediaPlayerService != 0) {
+        sMediaPlayerService->asBinder()->unlinkToDeath(this);
+    }
+}
+
+}; // namespace android
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index b43e48f..277bce1 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -12,6 +12,7 @@
 
 enum {
     CONNECT = IBinder::FIRST_CALL_TRANSACTION,
+    LIVES_LOCALLY,
     LIST_NODES,
     ALLOCATE_NODE,
     FREE_NODE,
@@ -75,6 +76,15 @@
         : BpInterface<IOMX>(impl) {
     }
 
+    virtual bool livesLocally(pid_t pid) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeInt32(pid);
+        remote()->transact(LIVES_LOCALLY, data, &reply);
+
+        return reply.readInt32() != 0;
+    }
+
     virtual status_t listNodes(List<ComponentInfo> *list) {
         list->clear();
 
@@ -369,6 +379,14 @@
 status_t BnOMX::onTransact(
     uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
     switch (code) {
+        case LIVES_LOCALLY:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+            reply->writeInt32(livesLocally((pid_t)data.readInt32()));
+
+            return OK;
+        }
+
         case LIST_NODES:
         {
             CHECK_INTERFACE(IOMX, data, reply);
@@ -408,7 +426,7 @@
             if (err == OK) {
                 reply->writeIntPtr((intptr_t)node);
             }
-                
+
             return NO_ERROR;
         }
 
@@ -419,7 +437,7 @@
             node_id node = (void*)data.readIntPtr();
 
             reply->writeInt32(freeNode(node));
-                
+
             return NO_ERROR;
         }
 
@@ -631,7 +649,7 @@
 
             node_id node = (void*)data.readIntPtr();
             const char *parameter_name = data.readCString();
-            
+
             OMX_INDEXTYPE index;
             status_t err = getExtensionIndex(node, parameter_name, &index);
 
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 040366b..c0664f3 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -34,48 +34,6 @@
 
 namespace android {
 
-// client singleton for binder interface to service
-Mutex MediaPlayer::sServiceLock;
-sp<IMediaPlayerService> MediaPlayer::sMediaPlayerService;
-sp<MediaPlayer::DeathNotifier> MediaPlayer::sDeathNotifier;
-SortedVector< wp<MediaPlayer> > MediaPlayer::sObitRecipients;
-
-// establish binder interface to service
-const sp<IMediaPlayerService>& MediaPlayer::getMediaPlayerService()
-{
-    Mutex::Autolock _l(sServiceLock);
-    if (sMediaPlayerService.get() == 0) {
-        sp<IServiceManager> sm = defaultServiceManager();
-        sp<IBinder> binder;
-        do {
-            binder = sm->getService(String16("media.player"));
-            if (binder != 0)
-                break;
-            LOGW("MediaPlayerService not published, waiting...");
-            usleep(500000); // 0.5 s
-        } while(true);
-        if (sDeathNotifier == NULL) {
-            sDeathNotifier = new DeathNotifier();
-        }
-        binder->linkToDeath(sDeathNotifier);
-        sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
-    }
-    LOGE_IF(sMediaPlayerService==0, "no MediaPlayerService!?");
-    return sMediaPlayerService;
-}
-
-void MediaPlayer::addObitRecipient(const wp<MediaPlayer>& recipient)
-{
-    Mutex::Autolock _l(sServiceLock);
-    sObitRecipients.add(recipient);
-}
-
-void MediaPlayer::removeObitRecipient(const wp<MediaPlayer>& recipient)
-{
-    Mutex::Autolock _l(sServiceLock);
-    sObitRecipients.remove(recipient);
-}
-
 MediaPlayer::MediaPlayer()
 {
     LOGV("constructor");
@@ -94,15 +52,9 @@
     mLockThreadId = 0;
 }
 
-void MediaPlayer::onFirstRef()
-{
-    addObitRecipient(this);
-}
-
 MediaPlayer::~MediaPlayer()
 {
     LOGV("destructor");
-    removeObitRecipient(this);
     disconnect();
     IPCThreadState::self()->flushCommands();
 }
@@ -630,45 +582,13 @@
     }
 }
 
-void MediaPlayer::DeathNotifier::binderDied(const wp<IBinder>& who) {
-    LOGW("MediaPlayer server died!");
-
-    // Need to do this with the lock held
-    SortedVector< wp<MediaPlayer> > list;
-    {
-        Mutex::Autolock _l(MediaPlayer::sServiceLock);
-        MediaPlayer::sMediaPlayerService.clear();
-        list = sObitRecipients;
-    }
-
-    // Notify application when media server dies.
-    // Don't hold the static lock during callback in case app
-    // makes a call that needs the lock.
-    size_t count = list.size();
-    for (size_t iter = 0; iter < count; ++iter) {
-        sp<MediaPlayer> player = list[iter].promote();
-        if ((player != 0) && (player->mPlayer != 0)) {
-            player->notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0);
-        }
-    }
-}
-
-MediaPlayer::DeathNotifier::~DeathNotifier()
-{
-    Mutex::Autolock _l(sServiceLock);
-    sObitRecipients.clear();
-    if (sMediaPlayerService != 0) {
-        sMediaPlayerService->asBinder()->unlinkToDeath(this);
-    }
-}
-
 /*static*/ sp<IMemory> MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat)
 {
     LOGV("decode(%s)", url);
     sp<IMemory> p;
     const sp<IMediaPlayerService>& service = getMediaPlayerService();
     if (service != 0) {
-        p = sMediaPlayerService->decode(url, pSampleRate, pNumChannels, pFormat);
+        p = service->decode(url, pSampleRate, pNumChannels, pFormat);
     } else {
         LOGE("Unable to locate media service");
     }
@@ -676,13 +596,19 @@
 
 }
 
+void MediaPlayer::died()
+{
+    LOGV("died");
+    notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0);
+}
+
 /*static*/ sp<IMemory> MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat)
 {
     LOGV("decode(%d, %lld, %lld)", fd, offset, length);
     sp<IMemory> p;
     const sp<IMediaPlayerService>& service = getMediaPlayerService();
     if (service != 0) {
-        p = sMediaPlayerService->decode(fd, offset, length, pSampleRate, pNumChannels, pFormat);
+        p = service->decode(fd, offset, length, pSampleRate, pNumChannels, pFormat);
     } else {
         LOGE("Unable to locate media service");
     }
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 6b63931..7b5dabb 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -24,6 +24,7 @@
 #include <utils/String8.h>
 #include <media/IMediaPlayerService.h>
 #include <media/IMediaRecorder.h>
+#include <media/mediaplayer.h>  // for MEDIA_ERROR_SERVER_DIED
 
 namespace android {
 
@@ -576,19 +577,8 @@
 MediaRecorder::MediaRecorder()
 {
     LOGV("constructor");
-    sp<IServiceManager> sm = defaultServiceManager();
-    sp<IBinder> binder;
 
-    do {
-        binder = sm->getService(String16("media.player"));
-        if (binder != NULL) {
-            break;
-        }
-        LOGW("MediaPlayerService not published, waiting...");
-        usleep(500000); // 0.5 s
-    } while(true);
-
-    sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+    const sp<IMediaPlayerService>& service(getMediaPlayerService());
     if (service != NULL) {
         mMediaRecorder = service->createMediaRecorder(getpid());
     }
@@ -637,5 +627,11 @@
     }
 }
 
+void MediaRecorder::died()
+{
+    LOGV("died");
+    notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_ERROR_SERVER_DIED, 0);
+}
+
 }; // namespace android
 
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..ed12b6d 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -154,7 +154,8 @@
       mHaveMetadata(false),
       mHasVideo(false),
       mFirstTrack(NULL),
-      mLastTrack(NULL) {
+      mLastTrack(NULL),
+      mFileMetaData(new MetaData) {
 }
 
 MPEG4Extractor::~MPEG4Extractor() {
@@ -169,20 +170,12 @@
 }
 
 sp<MetaData> MPEG4Extractor::getMetaData() {
-    sp<MetaData> meta = new MetaData;
-
     status_t err;
     if ((err = readMetaData()) != OK) {
-        return meta;
+        return new MetaData;
     }
 
-    if (mHasVideo) {
-        meta->setCString(kKeyMIMEType, "video/mp4");
-    } else {
-        meta->setCString(kKeyMIMEType, "audio/mp4");
-    }
-
-    return meta;
+    return mFileMetaData;
 }
 
 size_t MPEG4Extractor::countTracks() {
@@ -232,8 +225,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);
@@ -255,6 +249,12 @@
     }
 
     if (mHaveMetadata) {
+        if (mHasVideo) {
+            mFileMetaData->setCString(kKeyMIMEType, "video/mp4");
+        } else {
+            mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
+        }
+
         return OK;
     }
 
@@ -269,6 +269,41 @@
     s[4] = '\0';
 }
 
+struct PathAdder {
+    PathAdder(Vector<uint32_t> *path, uint32_t chunkType)
+        : mPath(path) {
+        mPath->push(chunkType);
+    }
+
+    ~PathAdder() {
+        mPath->pop();
+    }
+
+private:
+    Vector<uint32_t> *mPath;
+
+    PathAdder(const PathAdder &);
+    PathAdder &operator=(const PathAdder &);
+};
+
+static bool underMetaDataPath(const Vector<uint32_t> &path) {
+    return path.size() >= 5
+        && path[0] == FOURCC('m', 'o', 'o', 'v')
+        && path[1] == FOURCC('u', 'd', 't', 'a')
+        && path[2] == FOURCC('m', 'e', 't', 'a')
+        && path[3] == FOURCC('i', 'l', 's', 't');
+}
+
+// Given a time in seconds since Jan 1 1904, produce a human-readable string.
+static void convertTimeToDate(int64_t time_1904, String8 *s) {
+    time_t time_1970 = time_1904 - (((66 * 365 + 17) * 24) * 3600);
+
+    char tmp[32];
+    strftime(tmp, sizeof(tmp), "%Y%m%dT%H%M%S.000Z", gmtime(&time_1970));
+
+    s->setTo(tmp);
+}
+
 status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
     uint32_t hdr[2];
     if (mDataSource->readAt(*offset, hdr, 8) < 8) {
@@ -296,7 +331,8 @@
 
     char buffer[256];
     if (chunk_size <= sizeof(buffer)) {
-        if (mDataSource->readAt(*offset, buffer, chunk_size) < chunk_size) {
+        if (mDataSource->readAt(*offset, buffer, chunk_size)
+                < (ssize_t)chunk_size) {
             return ERROR_IO;
         }
 
@@ -304,8 +340,25 @@
     }
 #endif
 
+    PathAdder autoAdder(&mPath, chunk_type);
+
     off_t chunk_data_size = *offset + chunk_size - data_offset;
 
+    if (chunk_type != FOURCC('c', 'p', 'r', 't')
+            && mPath.size() == 5 && underMetaDataPath(mPath)) {
+        off_t stop_offset = *offset + chunk_size;
+        *offset = data_offset;
+        while (*offset < stop_offset) {
+            status_t err = parseChunk(offset, depth + 1);
+            if (err != OK) {
+                return err;
+            }
+        }
+        CHECK_EQ(*offset, stop_offset);
+
+        return OK;
+    }
+
     switch(chunk_type) {
         case FOURCC('m', 'o', 'o', 'v'):
         case FOURCC('t', 'r', 'a', 'k'):
@@ -318,6 +371,8 @@
         case FOURCC('t', 'r', 'a', 'f'):
         case FOURCC('m', 'f', 'r', 'a'):
         case FOURCC('s', 'k', 'i' ,'p'):
+        case FOURCC('u', 'd', 't', 'a'):
+        case FOURCC('i', 'l', 's', 't'):
         {
             off_t stop_offset = *offset + chunk_size;
             *offset = data_offset;
@@ -747,6 +802,76 @@
             break;
         }
 
+        case FOURCC('m', 'e', 't', 'a'):
+        {
+            uint8_t buffer[4];
+            CHECK(chunk_data_size >= (off_t)sizeof(buffer));
+            if (mDataSource->readAt(
+                        data_offset, buffer, 4) < 4) {
+                return ERROR_IO;
+            }
+
+            if (U32_AT(buffer) != 0) {
+                // Should be version 0, flags 0.
+                return ERROR_MALFORMED;
+            }
+
+            off_t stop_offset = *offset + chunk_size;
+            *offset = data_offset + sizeof(buffer);
+            while (*offset < stop_offset) {
+                status_t err = parseChunk(offset, depth + 1);
+                if (err != OK) {
+                    return err;
+                }
+            }
+            CHECK_EQ(*offset, stop_offset);
+            break;
+        }
+
+        case FOURCC('d', 'a', 't', 'a'):
+        {
+            if (mPath.size() == 6 && underMetaDataPath(mPath)) {
+                status_t err = parseMetaData(data_offset, chunk_data_size);
+
+                if (err != OK) {
+                    return err;
+                }
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('m', 'v', 'h', 'd'):
+        {
+            if (chunk_data_size < 12) {
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t header[12];
+            if (mDataSource->readAt(
+                        data_offset, header, sizeof(header))
+                    < (ssize_t)sizeof(header)) {
+                return ERROR_IO;
+            }
+
+            int64_t creationTime;
+            if (header[0] == 1) {
+                creationTime = U64_AT(&header[4]);
+            } else {
+                CHECK_EQ(header[0], 0);
+                creationTime = U32_AT(&header[4]);
+            }
+
+            String8 s;
+            convertTimeToDate(creationTime, &s);
+
+            mFileMetaData->setCString(kKeyDate, s.string());
+
+            *offset += chunk_size;
+            break;
+        }
+
         default:
         {
             *offset += chunk_size;
@@ -757,6 +882,102 @@
     return OK;
 }
 
+status_t MPEG4Extractor::parseMetaData(off_t offset, size_t size) {
+    if (size < 4) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t *buffer = new uint8_t[size + 1];
+    if (mDataSource->readAt(
+                offset, buffer, size) != (ssize_t)size) {
+        delete[] buffer;
+        buffer = NULL;
+
+        return ERROR_IO;
+    }
+
+    uint32_t flags = U32_AT(buffer);
+
+    uint32_t metadataKey = 0;
+    switch (mPath[4]) {
+        case FOURCC(0xa9, 'a', 'l', 'b'):
+        {
+            metadataKey = kKeyAlbum;
+            break;
+        }
+        case FOURCC(0xa9, 'A', 'R', 'T'):
+        {
+            metadataKey = kKeyArtist;
+            break;
+        }
+        case FOURCC(0xa9, 'd', 'a', 'y'):
+        {
+            metadataKey = kKeyYear;
+            break;
+        }
+        case FOURCC(0xa9, 'n', 'a', 'm'):
+        {
+            metadataKey = kKeyTitle;
+            break;
+        }
+        case FOURCC(0xa9, 'w', 'r', 't'):
+        {
+            metadataKey = kKeyWriter;
+            break;
+        }
+        case FOURCC('c', 'o', 'v', 'r'):
+        {
+            metadataKey = kKeyAlbumArt;
+            break;
+        }
+        case FOURCC('g', 'n', 'r', 'e'):
+        {
+            metadataKey = kKeyGenre;
+            break;
+        }
+        case FOURCC('t', 'r', 'k', 'n'):
+        {
+            if (size == 16 && flags == 0) {
+                char tmp[16];
+                sprintf(tmp, "%d/%d",
+                        (int)buffer[size - 5], (int)buffer[size - 3]);
+
+                printf("track: %s\n", tmp);
+                mFileMetaData->setCString(kKeyCDTrackNumber, tmp);
+            }
+            break;
+        }
+        default:
+            break;
+    }
+
+    if (size >= 8 && metadataKey) {
+        if (metadataKey == kKeyAlbumArt) {
+            mFileMetaData->setData(
+                    kKeyAlbumArt, MetaData::TYPE_NONE,
+                    buffer + 8, size - 8);
+        } else if (metadataKey == kKeyGenre) {
+            if (flags == 0) {
+                // uint8_t
+                char genre[10];
+                sprintf(genre, "%d", (int)buffer[size - 1]);
+
+                mFileMetaData->setCString(metadataKey, genre);
+            }
+        } else {
+            buffer[size] = '\0';
+
+            mFileMetaData->setCString(
+                    metadataKey, (const char *)buffer + 8);
+        }
+    }
+
+    delete[] buffer;
+    buffer = NULL;
+
+    return OK;
+}
+
 sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
     status_t err;
     if ((err = readMetaData()) != OK) {
@@ -929,20 +1150,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/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index c4d3b5d..c583b93 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1016,6 +1016,7 @@
         const char *componentName,
         const sp<MediaSource> &source)
     : mOMX(omx),
+      mOMXLivesLocally(omx->livesLocally(getpid())),
       mNode(node),
       mQuirks(quirks),
       mIsEncoder(isEncoder),
@@ -1191,12 +1192,22 @@
         IOMX::buffer_id buffer;
         if (portIndex == kPortIndexInput
                 && (mQuirks & kRequiresAllocateBufferOnInputPorts)) {
-            err = mOMX->allocateBufferWithBackup(
-                    mNode, portIndex, mem, &buffer);
+            if (mOMXLivesLocally) {
+                err = mOMX->allocateBuffer(
+                        mNode, portIndex, def.nBufferSize, &buffer);
+            } else {
+                err = mOMX->allocateBufferWithBackup(
+                        mNode, portIndex, mem, &buffer);
+            }
         } else if (portIndex == kPortIndexOutput
                 && (mQuirks & kRequiresAllocateBufferOnOutputPorts)) {
-            err = mOMX->allocateBufferWithBackup(
-                    mNode, portIndex, mem, &buffer);
+            if (mOMXLivesLocally) {
+                err = mOMX->allocateBuffer(
+                        mNode, portIndex, def.nBufferSize, &buffer);
+            } else {
+                err = mOMX->allocateBufferWithBackup(
+                        mNode, portIndex, mem, &buffer);
+            }
         } else {
             err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
         }
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/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 020887c..313a9ed 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -315,6 +315,7 @@
         { kKeyGenre, METADATA_KEY_GENRE },
         { kKeyTitle, METADATA_KEY_TITLE },
         { kKeyYear, METADATA_KEY_YEAR },
+        { kKeyWriter, METADATA_KEY_WRITER },
     };
     static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
 
@@ -357,7 +358,6 @@
 
     // The duration value is a string representing the duration in ms.
     sprintf(tmp, "%lld", (maxDurationUs + 500) / 1000);
-
     mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
 }
 
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 959a767..da8fe79 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -44,7 +44,8 @@
 struct WAVSource : public MediaSource {
     WAVSource(
             const sp<DataSource> &dataSource,
-            int32_t sampleRate, int32_t numChannels,
+            const sp<MetaData> &meta,
+            int32_t bitsPerSample,
             off_t offset, size_t size);
 
     virtual status_t start(MetaData *params = NULL);
@@ -61,8 +62,10 @@
     static const size_t kMaxFrameSize;
 
     sp<DataSource> mDataSource;
+    sp<MetaData> mMeta;
     int32_t mSampleRate;
     int32_t mNumChannels;
+    int32_t mBitsPerSample;
     off_t mOffset;
     size_t mSize;
     bool mStarted;
@@ -104,7 +107,8 @@
     }
 
     return new WAVSource(
-            mDataSource, mSampleRate, mNumChannels, mDataOffset, mDataSize);
+            mDataSource, mTrackMeta,
+            mBitsPerSample, mDataOffset, mDataSize);
 }
 
 sp<MetaData> WAVExtractor::getTrackMetaData(
@@ -113,17 +117,7 @@
         return NULL;
     }
 
-    sp<MetaData> meta = new MetaData;
-    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
-    meta->setInt32(kKeyChannelCount, mNumChannels);
-    meta->setInt32(kKeySampleRate, mSampleRate);
-
-    int64_t durationUs =
-        1000000LL * (mDataSize / (mNumChannels * 2)) / mSampleRate;
-
-    meta->setInt64(kKeyDuration, durationUs);
-
-    return meta;
+    return mTrackMeta;
 }
 
 status_t WAVExtractor::init() {
@@ -149,7 +143,7 @@
 
         remainingSize -= 8;
         offset += 8;
-        
+
         uint32_t chunkSize = U32_LE_AT(&chunkHeader[4]);
 
         if (chunkSize > remainingSize) {
@@ -178,7 +172,13 @@
 
             mSampleRate = U32_LE_AT(&formatSpec[4]);
 
-            if (U16_LE_AT(&formatSpec[14]) != 16) {
+            if (mSampleRate == 0) {
+                return ERROR_MALFORMED;
+            }
+
+            mBitsPerSample = U16_LE_AT(&formatSpec[14]);
+
+            if (mBitsPerSample != 8 && mBitsPerSample != 16) {
                 return ERROR_UNSUPPORTED;
             }
 
@@ -188,6 +188,19 @@
                 mDataOffset = offset;
                 mDataSize = chunkSize;
 
+                mTrackMeta = new MetaData;
+                mTrackMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+                mTrackMeta->setInt32(kKeyChannelCount, mNumChannels);
+                mTrackMeta->setInt32(kKeySampleRate, mSampleRate);
+
+                size_t bytesPerSample = mBitsPerSample >> 3;
+
+                int64_t durationUs =
+                    1000000LL * (mDataSize / (mNumChannels * bytesPerSample))
+                        / mSampleRate;
+
+                mTrackMeta->setInt64(kKeyDuration, durationUs);
+
                 return OK;
             }
         }
@@ -202,15 +215,20 @@
 
 WAVSource::WAVSource(
         const sp<DataSource> &dataSource,
-        int32_t sampleRate, int32_t numChannels,
+        const sp<MetaData> &meta,
+        int32_t bitsPerSample,
         off_t offset, size_t size)
     : mDataSource(dataSource),
-      mSampleRate(sampleRate),
-      mNumChannels(numChannels),
+      mMeta(meta),
+      mSampleRate(0),
+      mNumChannels(0),
+      mBitsPerSample(bitsPerSample),
       mOffset(offset),
       mSize(size),
       mStarted(false),
       mGroup(NULL) {
+    CHECK(mMeta->findInt32(kKeySampleRate, &mSampleRate));
+    CHECK(mMeta->findInt32(kKeyChannelCount, &mNumChannels));
 }
 
 WAVSource::~WAVSource() {
@@ -227,6 +245,11 @@
     mGroup = new MediaBufferGroup;
     mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
 
+    if (mBitsPerSample == 8) {
+        // As a temporary buffer for 8->16 bit conversion.
+        mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+    }
+
     mCurrentPos = mOffset;
 
     mStarted = true;
@@ -250,17 +273,7 @@
 sp<MetaData> WAVSource::getFormat() {
     LOGV("WAVSource::getFormat");
 
-    sp<MetaData> meta = new MetaData;
-    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
-    meta->setInt32(kKeyChannelCount, mNumChannels);
-    meta->setInt32(kKeySampleRate, mSampleRate);
-
-    int64_t durationUs =
-        1000000LL * (mSize / (mNumChannels * 2)) / mSampleRate;
-
-    meta->setInt64(kKeyDuration, durationUs);
-
-    return meta;
+    return mMeta;
 }
 
 status_t WAVSource::read(
@@ -283,7 +296,8 @@
     }
 
     ssize_t n = mDataSource->readAt(
-            mCurrentPos, buffer->data(), kMaxFrameSize);
+            mCurrentPos, buffer->data(),
+            mBitsPerSample == 8 ? kMaxFrameSize / 2 : kMaxFrameSize);
 
     if (n <= 0) {
         buffer->release();
@@ -295,10 +309,34 @@
     mCurrentPos += n;
 
     buffer->set_range(0, n);
+
+    if (mBitsPerSample == 8) {
+        // Convert 8-bit unsigned samples to 16-bit signed.
+
+        MediaBuffer *tmp;
+        CHECK_EQ(mGroup->acquire_buffer(&tmp), OK);
+
+        // The new buffer holds the sample number of samples, but each
+        // one is 2 bytes wide.
+        tmp->set_range(0, 2 * n);
+
+        int16_t *dst = (int16_t *)tmp->data();
+        const uint8_t *src = (const uint8_t *)buffer->data();
+        while (n-- > 0) {
+            *dst++ = ((int16_t)(*src) - 128) * 256;
+            ++src;
+        }
+
+        buffer->release();
+        buffer = tmp;
+    }
+
+    size_t bytesPerSample = mBitsPerSample >> 3;
+
     buffer->meta_data()->setInt64(
             kKeyTime,
             1000000LL * (mCurrentPos - mOffset)
-                / (mNumChannels * 2) / mSampleRate);
+                / (mNumChannels * bytesPerSample) / mSampleRate);
 
 
     *out = buffer;
diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp
index 2b3ef1a..6d64717 100644
--- a/media/libstagefright/id3/ID3.cpp
+++ b/media/libstagefright/id3/ID3.cpp
@@ -33,7 +33,11 @@
       mSize(0),
       mFirstFrameOffset(0),
       mVersion(ID3_UNKNOWN) {
-    mIsValid = parse(source);
+    mIsValid = parseV2(source);
+
+    if (!mIsValid) {
+        mIsValid = parseV1(source);
+    }
 }
 
 ID3::~ID3() {
@@ -51,7 +55,7 @@
     return mVersion;
 }
 
-bool ID3::parse(const sp<DataSource> &source) {
+bool ID3::parseV2(const sp<DataSource> &source) {
     struct id3_header {
         char id[3];
         uint8_t version_major;
@@ -119,7 +123,7 @@
     }
 
     if (header.flags & 0x80) {
-        LOGI("removing unsynchronization");
+        LOGV("removing unsynchronization");
         removeUnsynchronization();
     }
 
@@ -128,12 +132,18 @@
         // Version 2.3 has an optional extended header.
 
         if (mSize < 4) {
+            free(mData);
+            mData = NULL;
+
             return false;
         }
 
         size_t extendedHeaderSize = U32_AT(&mData[0]) + 4;
 
         if (extendedHeaderSize > mSize) {
+            free(mData);
+            mData = NULL;
+
             return false;
         }
 
@@ -147,6 +157,9 @@
                 size_t paddingSize = U32_AT(&mData[6]);
 
                 if (mFirstFrameOffset + paddingSize > mSize) {
+                    free(mData);
+                    mData = NULL;
+
                     return false;
                 }
 
@@ -154,7 +167,7 @@
             }
 
             if (extendedFlags & 0x8000) {
-                LOGI("have crc");
+                LOGV("have crc");
             }
         }
     }
@@ -221,9 +234,37 @@
 
     if (mParent.mVersion == ID3_V2_2) {
         id->setTo((const char *)&mParent.mData[mOffset], 3);
-    } else {
-        CHECK_EQ(mParent.mVersion, ID3_V2_3);
+    } else if (mParent.mVersion == ID3_V2_3) {
         id->setTo((const char *)&mParent.mData[mOffset], 4);
+    } else {
+        CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
+
+        switch (mOffset) {
+            case 3:
+                id->setTo("TT2");
+                break;
+            case 33:
+                id->setTo("TP1");
+                break;
+            case 63:
+                id->setTo("TAL");
+                break;
+            case 93:
+                id->setTo("TYE");
+                break;
+            case 97:
+                id->setTo("COM");
+                break;
+            case 126:
+                id->setTo("TRK");
+                break;
+            case 127:
+                id->setTo("TCO");
+                break;
+            default:
+                CHECK(!"should not be here.");
+                break;
+        }
     }
 }
 
@@ -273,6 +314,20 @@
         return;
     }
 
+    if (mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1) {
+        if (mOffset == 126 || mOffset == 127) {
+            // Special treatment for the track number and genre.
+            char tmp[16];
+            sprintf(tmp, "%d", (int)*mFrameData);
+
+            id->setTo(tmp);
+            return;
+        }
+
+        id->setTo((const char *)mFrameData, mFrameSize);
+        return;
+    }
+
     size_t n = mFrameSize - getHeaderLength() - 1;
 
     if (*mFrameData == 0x00) {
@@ -280,7 +335,8 @@
         convertISO8859ToString8(mFrameData + 1, n, id);
     } else {
         // UCS-2
-        id->setTo((const char16_t *)(mFrameData + 1), n);
+        // API wants number of characters, not number of bytes...
+        id->setTo((const char16_t *)(mFrameData + 1), n / 2);
     }
 }
 
@@ -299,9 +355,11 @@
 size_t ID3::Iterator::getHeaderLength() const {
     if (mParent.mVersion == ID3_V2_2) {
         return 6;
-    } else {
-        CHECK_EQ(mParent.mVersion, ID3_V2_3);
+    } else if (mParent.mVersion == ID3_V2_3) {
         return 10;
+    } else {
+        CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
+        return 0;
     }
 }
 
@@ -345,9 +403,7 @@
             if (!strcmp(id, mID)) {
                 break;
             }
-        } else {
-            CHECK_EQ(mParent.mVersion, ID3_V2_3);
-
+        } else if (mParent.mVersion == ID3_V2_3) {
             if (mOffset + 10 > mParent.mSize) {
                 return;
             }
@@ -377,6 +433,52 @@
             if (!strcmp(id, mID)) {
                 break;
             }
+        } else {
+            CHECK(mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1);
+
+            if (mOffset >= mParent.mSize) {
+                return;
+            }
+
+            mFrameData = &mParent.mData[mOffset];
+
+            switch (mOffset) {
+                case 3:
+                case 33:
+                case 63:
+                    mFrameSize = 30;
+                    break;
+                case 93:
+                    mFrameSize = 4;
+                    break;
+                case 97:
+                    if (mParent.mVersion == ID3_V1) {
+                        mFrameSize = 30;
+                    } else {
+                        mFrameSize = 29;
+                    }
+                    break;
+                case 126:
+                    mFrameSize = 1;
+                    break;
+                case 127:
+                    mFrameSize = 1;
+                    break;
+                default:
+                    CHECK(!"Should not be here, invalid offset.");
+                    break;
+            }
+
+            if (!mID) {
+                break;
+            }
+
+            String8 id;
+            getID(&id);
+
+            if (id == mID) {
+                break;
+            }
         }
 
         mOffset += mFrameSize;
@@ -461,5 +563,40 @@
     return NULL;
 }
 
+bool ID3::parseV1(const sp<DataSource> &source) {
+    const size_t V1_TAG_SIZE = 128;
+
+    off_t size;
+    if (source->getSize(&size) != OK || size < (off_t)V1_TAG_SIZE) {
+        return false;
+    }
+
+    mData = (uint8_t *)malloc(V1_TAG_SIZE);
+    if (source->readAt(size - V1_TAG_SIZE, mData, V1_TAG_SIZE)
+            != (ssize_t)V1_TAG_SIZE) {
+        free(mData);
+        mData = NULL;
+
+        return false;
+    }
+
+    if (memcmp("TAG", mData, 3)) {
+        free(mData);
+        mData = NULL;
+
+        return false;
+    }
+
+    mSize = V1_TAG_SIZE;
+    mFirstFrameOffset = 3;
+
+    if (mData[V1_TAG_SIZE - 3] != 0) {
+        mVersion = ID3_V1;
+    } else {
+        mVersion = ID3_V1_1;
+    }
+
+    return true;
+}
 
 }  // namespace android
diff --git a/media/libstagefright/id3/testid3.cpp b/media/libstagefright/id3/testid3.cpp
index 305b065..0741045 100644
--- a/media/libstagefright/id3/testid3.cpp
+++ b/media/libstagefright/id3/testid3.cpp
@@ -16,6 +16,8 @@
 
 #include "../include/ID3.h"
 
+#include <sys/stat.h>
+
 #include <ctype.h>
 #include <dirent.h>
 
@@ -108,6 +110,12 @@
 }
 
 void scan(const char *path) {
+    struct stat st;
+    if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) {
+        scanFile(path);
+        return;
+    }
+
     DIR *dir = opendir(path);
 
     if (dir == NULL) {
diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h
index 79931ac..da042a3 100644
--- a/media/libstagefright/include/ID3.h
+++ b/media/libstagefright/include/ID3.h
@@ -28,6 +28,8 @@
 struct ID3 {
     enum Version {
         ID3_UNKNOWN,
+        ID3_V1,
+        ID3_V1_1,
         ID3_V2_2,
         ID3_V2_3
     };
@@ -74,7 +76,8 @@
     size_t mFirstFrameOffset;
     Version mVersion;
 
-    bool parse(const sp<DataSource> &source);
+    bool parseV1(const sp<DataSource> &source);
+    bool parseV2(const sp<DataSource> &source);
     void removeUnsynchronization();
 
     ID3(const ID3 &);
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 0e360e8..1a13446 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -19,6 +19,7 @@
 #define MPEG4_EXTRACTOR_H_
 
 #include <media/stagefright/MediaExtractor.h>
+#include <utils/Vector.h>
 
 namespace android {
 
@@ -55,10 +56,14 @@
 
     Track *mFirstTrack, *mLastTrack;
 
+    sp<MetaData> mFileMetaData;
+
     uint32_t mHandlerType;
+    Vector<uint32_t> mPath;
 
     status_t readMetaData();
     status_t parseChunk(off_t *offset, int depth);
+    status_t parseMetaData(off_t offset, size_t size);
 
     MPEG4Extractor(const MPEG4Extractor &);
     MPEG4Extractor &operator=(const MPEG4Extractor &);
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index ce0b0d55aa..b559101 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -31,6 +31,8 @@
 public:
     OMX();
 
+    virtual bool livesLocally(pid_t pid);
+
     virtual status_t listNodes(List<ComponentInfo> *list);
 
     virtual status_t allocateNode(
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/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h
index 8545efc..9384942 100644
--- a/media/libstagefright/include/WAVExtractor.h
+++ b/media/libstagefright/include/WAVExtractor.h
@@ -45,8 +45,10 @@
     bool mValidFormat;
     uint16_t mNumChannels;
     uint32_t mSampleRate;
+    uint16_t mBitsPerSample;
     off_t mDataOffset;
     size_t mDataSize;
+    sp<MetaData> mTrackMeta;
 
     status_t init();
 
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 8c3f252..2121321 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)),
@@ -208,6 +164,10 @@
     instance->onObserverDied(mMaster);
 }
 
+bool OMX::livesLocally(pid_t pid) {
+    return pid == getpid();
+}
+
 status_t OMX::listNodes(List<ComponentInfo> *list) {
     list->clear();
 
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/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
index ca60e8c..95dbb97 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
@@ -250,7 +250,11 @@
         
         value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
         Log.v(TAG, "Expected = " + meta_data_file[fileIndex][meta.DURATION.ordinal()] + "reult = " + value);
-        assertEquals(TAG, meta_data_file[fileIndex][meta.DURATION.ordinal()], value);
+        // Only require that the returned duration is within 100ms of the expected
+        // one as PV and stagefright differ slightly in their implementation.
+        assertTrue(TAG, Math.abs(Integer.parseInt(
+                        meta_data_file[fileIndex][meta.DURATION.ordinal()])
+                            - Integer.parseInt(value)) < 100);
         
         //METADATA_KEY_NUM_TRACKS should return the total number of tracks in the media
         //include the video and audio
diff --git a/media/tests/omxjpegdecoder/Android.mk b/media/tests/omxjpegdecoder/Android.mk
index f679f19..b7c18bc 100644
--- a/media/tests/omxjpegdecoder/Android.mk
+++ b/media/tests/omxjpegdecoder/Android.mk
@@ -33,7 +33,8 @@
     libskia \
     libstagefright \
     libbinder \
-    libutils
+    libutils \
+    libjpeg
 
 LOCAL_C_INCLUDES := \
     $(JNI_H_INCLUDE) \
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/preloaded-classes b/preloaded-classes
index ec4d74c..90bbb37 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -753,10 +753,8 @@
 java.math.Multiplication
 java.net.ContentHandler
 java.net.InetAddress
-java.net.InetAddress$CacheElement
 java.net.InetAddress$WaitReachable
 java.net.JarURLConnection
-java.net.NegativeCache
 java.net.NetPermission
 java.net.ProxySelectorImpl
 java.net.Socket$ConnectLock
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..0d9f98f 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -28,6 +28,7 @@
 import android.net.Uri;
 import android.os.IMountService;
 import android.os.Environment;
+import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UEventObserver;
 import android.os.Handler;
@@ -127,9 +128,12 @@
 
     private boolean mUmsConnected = false;
     private boolean mUmsEnabled = false;
+    private boolean mUmsEnabling = false;
 
     private String  mLegacyState = Environment.MEDIA_REMOVED;
 
+    private PackageManagerService mPms;
+
     /**
      * Constructs a new MountService instance
      * 
@@ -138,6 +142,7 @@
     public MountService(Context context) {
         mContext = context;
 
+        mPms = (PackageManagerService) ServiceManager.getService("package");
         // Register a BOOT_COMPLETED handler so that we can start
         // our NativeDaemonConnector. We defer the startup so that we don't
         // start processing events before we ought-to
@@ -332,13 +337,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) {
@@ -505,6 +513,8 @@
     void handlePossibleExplicitUnmountBroadcast(String path) {
         if (mMounted) {
             mMounted = false;
+            // Update media status on PackageManagerService to unmount packages on sdcard
+            mPms.updateExternalMediaStatus(false);
             Intent intent = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, 
                     Uri.parse("file://" + path));
             mContext.sendBroadcast(intent);
@@ -594,10 +604,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) {
@@ -731,6 +745,8 @@
 
         updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
 
+        // Update media status on PackageManagerService to unmount packages on sdcard
+        mPms.updateExternalMediaStatus(false);
         if (mShowSafeUnmountNotificationWhenUnmounted) {
             setMediaStorageNotification(
                     com.android.internal.R.string.ext_media_safe_unmount_notification_title,
@@ -792,6 +808,8 @@
     void notifyMediaMounted(String path, boolean readOnly) {
         updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
 
+        // Update media status on PackageManagerService to mount packages on sdcard
+        mPms.updateExternalMediaStatus(true);
         setMediaStorageNotification(0, 0, 0, false, false, null);
         updateUsbMassStorageNotification(false, false);
         Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED, 
@@ -1049,6 +1067,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..cafc804 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;
@@ -4751,13 +4753,18 @@
             ret = deleteInstalledPackageLI (p, deleteCodeAndResources, flags, outInfo);
         }
         if (ret && onSd) {
-            // Post a delayed destroy on the container since there might
-            // be active processes holding open file handles to package
-            // resources which will get killed by the process killer when
-            // destroying the container. This might even kill the current
-            // process and crash the system. Delay the destroy a bit so
-            // that the active processes get to handle the uninstall broadcasts.
-            sendDelayedDestroySdDir(packageName);
+            if (deleteCodeAndResources) {
+                // Post a delayed destroy on the container since there might
+                // be active processes holding open file handles to package
+                // resources which will get killed by the process killer when
+                // destroying the container. This might even kill the current
+                // process and crash the system. Delay the destroy a bit so
+                // that the active processes get to handle the uninstall broadcasts.
+                sendDelayedDestroySdDir(packageName);
+            } else {
+                // Just unmount the directory
+                unMountSdDir(packageName);
+            }
         }
         return ret;
     }
@@ -5864,7 +5871,9 @@
         HashSet<String> loadedPermissions = new HashSet<String>();
 
         GrantedPermissions(int pkgFlags) {
-            this.pkgFlags = pkgFlags & ApplicationInfo.FLAG_SYSTEM;
+            this.pkgFlags = (pkgFlags & ApplicationInfo.FLAG_SYSTEM) |
+                    (pkgFlags & ApplicationInfo.FLAG_FORWARD_LOCK) |
+                    (pkgFlags & ApplicationInfo.FLAG_ON_SDCARD);
         }
     }
 
@@ -6670,9 +6679,8 @@
             if (!pkg.resourcePathString.equals(pkg.codePathString)) {
                 serializer.attribute(null, "resourcePath", pkg.resourcePathString);
             }
-            serializer.attribute(null, "system",
-                    (pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0
-                    ? "true" : "false");
+            serializer.attribute(null, "flags",
+                    Integer.toString(pkg.pkgFlags));
             serializer.attribute(null, "ts", pkg.getTimeStampStr());
             serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
             if (pkg.sharedUser == null) {
@@ -7063,16 +7071,24 @@
                     } catch (NumberFormatException e) {
                     }
                 }
-                systemStr = parser.getAttributeValue(null, "system");
                 installerPackageName = parser.getAttributeValue(null, "installer");
+
+                systemStr = parser.getAttributeValue(null, "flags");
                 if (systemStr != null) {
-                    if ("true".equals(systemStr)) {
-                        pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+                    try {
+                        pkgFlags = Integer.parseInt(systemStr);
+                    } catch (NumberFormatException e) {
                     }
                 } else {
-                    // Old settings that don't specify system...  just treat
-                    // them as system, good enough.
-                    pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+                    // For backward compatibility
+                    systemStr = parser.getAttributeValue(null, "system");
+                    if (systemStr != null) {
+                        pkgFlags |= ("true".equalsIgnoreCase(systemStr)) ? ApplicationInfo.FLAG_SYSTEM : 0;
+                    } else {
+                        // Old settings that don't specify system...  just treat
+                        // them as system, good enough.
+                        pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
+                    }
                 }
                 timeStampStr = parser.getAttributeValue(null, "ts");
                 if (timeStampStr != null) {
@@ -7439,7 +7455,9 @@
 
     // ------- 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 boolean mMediaMounted = false;
 
     private MountService getMountService() {
         return (MountService) ServiceManager.getService("mount");
@@ -7457,10 +7475,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 +7510,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,14 +7520,24 @@
     }
 
    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);
        }
        return null;
    }
 
+   private boolean unMountSdDir(String pkgName) {
+       // STOPSHIP unmount directory
+       return true;
+   }
+
    private String getSdDir(String pkgName) {
        String cachePath = null;
        try {
@@ -7530,6 +7573,15 @@
        }
    }
 
+   private String[] getSecureContainerList() {
+       try {
+           return getMountService().getSecureContainerList();
+       } catch (IllegalStateException e) {
+           Log.i(TAG, "Failed to getSecureContainerList");
+       }
+       return null;
+   }
+
    private void sendDelayedDestroySdDir(String pkgName) {
        if (mHandler.hasMessages(DESTROY_SD_CONTAINER, pkgName)) {
            // Don't have to send message again
@@ -7539,7 +7591,63 @@
        mHandler.sendMessageDelayed(msg, DESTROY_SD_CONTAINER_DELAY);
    }
 
-   public void updateExternalMediaStatus(boolean mediaStatus) {
-       // TODO
+   public void updateExternalMediaStatus(final boolean mediaStatus) {
+       if (mediaStatus == mMediaMounted) {
+           return;
+       }
+       mMediaMounted = mediaStatus;
+        // Queue up an async operation since the package installation may take a little while.
+       mHandler.post(new Runnable() {
+           public void run() {
+               mHandler.removeCallbacks(this);
+               final String list[] = getSecureContainerList();
+               if (list == null || list.length == 0) {
+                   return;
+               }
+               for (int i = 0; i < list.length; i++) {
+                   String mountPkg = list[i];
+                   // TODO compare with default package
+                   synchronized (mPackages) {
+                       PackageSetting ps = mSettings.mPackages.get(mountPkg);
+                       if (ps != null && (ps.pkgFlags & ApplicationInfo.FLAG_ON_SDCARD) != 0) {
+                           if (mediaStatus) {
+                               String pkgPath = getSdDir(mountPkg);
+                               if (pkgPath == null) {
+                                   continue;
+                               }
+                               pkgPath = ps.codePathString;
+                               int parseFlags = PackageParser.PARSE_CHATTY |
+                               PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
+                               PackageParser pp = new PackageParser(pkgPath);
+                               pp.setSeparateProcesses(mSeparateProcesses);
+                               final PackageParser.Package pkg = pp.parsePackage(new File(pkgPath),
+                                       null, mMetrics, parseFlags);
+                               if (pkg == null) {
+                                   Log.w(TAG, "Failed to install package : " + mountPkg + " from sd card");
+                                   continue;
+                               }
+                               int scanMode = SCAN_MONITOR;
+                               // Scan the package
+                               if (scanPackageLI(pkg, parseFlags, scanMode) != null) {
+                                   // Grant permissions
+                                   grantPermissionsLP(pkg, false);
+                                   // Persist settings
+                                   mSettings.writeLP();
+                               } else {
+                                   Log.i(TAG, "Failed to install package: " + mountPkg + " from sdcard");
+                               }
+                           } else {
+                               // Delete package
+                               PackageRemovedInfo outInfo = new PackageRemovedInfo();
+                               boolean res = deletePackageLI(mountPkg, false, PackageManager.DONT_DELETE_DATA, outInfo);
+                               if (!res) {
+                                   Log.e(TAG, "Failed to delete pkg  from sdcard : " + mountPkg);
+                               }
+                           }
+                       }
+                   }
+               }
+           }
+       });
    }
 }
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/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index 42c0254..f5aeaf0 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -88,6 +88,8 @@
 
     // clock
     private Calendar mCalendar;
+    private String mClockFormatString;
+    private SimpleDateFormat mClockFormat;
     private IBinder mClockIcon;
     private IconData mClockData;
 
@@ -546,37 +548,48 @@
             res = R.string.twelve_hour_time_format;
         }
 
-        String format = mContext.getString(res);
-
-        /*
-         * Search for an unquoted "a" in the format string, so we can
-         * add dummy characters around it to let us find it again after
-         * formatting and change its size.
-         */
-        int a = -1;
-        boolean quoted = false;
-        for (int i = 0; i < format.length(); i++) {
-            char c = format.charAt(i);
-
-            if (c == '\'') {
-                quoted = !quoted;
-            }
-
-            if (!quoted && c == 'a') {
-                a = i;
-                break;
-            }
-        }
-
         final char MAGIC1 = '\uEF00';
         final char MAGIC2 = '\uEF01';
 
-        if (a >= 0) {
-            format = format.substring(0, a) + MAGIC1 + "a" + MAGIC2 +
-                     format.substring(a + 1);
-        }
+        SimpleDateFormat sdf;
+        String format = mContext.getString(res);
+        if (!format.equals(mClockFormatString)) {
+            /*
+             * Search for an unquoted "a" in the format string, so we can
+             * add dummy characters around it to let us find it again after
+             * formatting and change its size.
+             */
+            int a = -1;
+            boolean quoted = false;
+            for (int i = 0; i < format.length(); i++) {
+                char c = format.charAt(i);
 
-        String result = new SimpleDateFormat(format).format(mCalendar.getTime());
+                if (c == '\'') {
+                    quoted = !quoted;
+                }
+
+                if (!quoted && c == 'a') {
+                    a = i;
+                    break;
+                }
+            }
+
+            if (a >= 0) {
+                // Move a back so any whitespace before the AM/PM is also in the alternate size.
+                final int b = a;
+                while (a > 0 && Character.isWhitespace(format.charAt(a-1))) {
+                    a--;
+                }
+                format = format.substring(0, a) + MAGIC1 + format.substring(a, b)
+                        + "a" + MAGIC2 + format.substring(b + 1);
+            }
+
+            mClockFormat = sdf = new SimpleDateFormat(format);
+            mClockFormatString = format;
+        } else {
+            sdf = mClockFormat;
+        }
+        String result = sdf.format(mCalendar.getTime());
 
         int magic1 = result.indexOf(MAGIC1);
         int magic2 = result.indexOf(MAGIC2);
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/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index f2ddd0f..ffc2cbc 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -121,6 +121,20 @@
             }
         },
 
+        new Test("Times") {
+            public void run()
+            {
+                long now = System.currentTimeMillis();
+
+                timeNotification(7, "24 hours from now", now+(1000*60*60*24));
+                timeNotification(6, "12:01:00 from now", now+(1000*60*60*12)+(60*1000));
+                timeNotification(5, "12 hours from now", now+(1000*60*60*12));
+                timeNotification(4, "now", now);
+                timeNotification(3, "11:59:00 ago", now-((1000*60*60*12)-(60*1000)));
+                timeNotification(2, "12 hours ago", now-(1000*60*60*12));
+                timeNotification(1, "24 hours ago", now-(1000*60*60*24));
+            }
+        },
         new StateStress("Stress - Ongoing / Latest", 100, 100, new Runnable[] {
                 new Runnable() {
                     public void run() {
@@ -590,5 +604,12 @@
             mHandler.postDelayed(mRunnable, mPause);
         }
     }
+
+    void timeNotification(int n, String label, long time) {
+        mNM.notify(n, new Notification(NotificationTestList.this,
+                    R.drawable.ic_statusbar_missedcall, null,
+                    time, label, "" + new java.util.Date(time), null));
+
+    }
 }
 
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/BitmapShader.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
index 8bf7fcc..ad3974c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import java.awt.Paint;
+
 public class BitmapShader extends Shader {
 
     // we hold on just for the GC, since our native counterpart is using it
@@ -31,11 +33,16 @@
     public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY) {
         mBitmap = bitmap;
     }
-    
+
     //---------- Custom methods
-    
+
     public Bitmap getBitmap() {
         return mBitmap;
     }
+
+    @Override
+    Paint getJavaPaint() {
+        return null;
+    }
 }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
index 4986c77..9f4dfd0 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
@@ -125,13 +125,16 @@
         }
 
         Shader shader = paint.getShader();
-        if (shader instanceof LinearGradient) {
-            g.setPaint(((LinearGradient)shader).getPaint());
-        } else {
-            if (mLogger != null && shader != null) {
-                mLogger.warning(String.format(
-                        "Shader '%1$s' is not supported in the Layout Editor.",
-                        shader.getClass().getCanonicalName()));
+        if (shader != null) {
+            java.awt.Paint shaderPaint = shader.getJavaPaint();
+            if (shaderPaint != null) {
+                g.setPaint(shaderPaint);
+            } else {
+                if (mLogger != null) {
+                    mLogger.warning(String.format(
+                            "Shader '%1$s' is not supported in the Layout Editor.",
+                            shader.getClass().getCanonicalName()));
+                }
             }
         }
 
@@ -236,10 +239,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 +282,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)
      */
@@ -405,7 +412,7 @@
 
         g.setColor(new Color(color));
 
-        getGraphics2d().fillRect(0, 0, getWidth(), getHeight());
+        g.fillRect(0, 0, getWidth(), getHeight());
 
         g.setComposite(composite);
 
@@ -953,10 +960,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 +971,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 +1032,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/ComposeShader.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
index 968a597..863d64a 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import java.awt.Paint;
+
 /** A subclass of shader that returns the composition of two other shaders, combined by
     an {@link android.graphics.Xfermode} subclass.
 */
@@ -42,5 +44,10 @@
     public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) {
         // FIXME Implement shader
     }
+
+    @Override
+    Paint getJavaPaint() {
+        return null;
+    }
 }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
index 1a0dc05..7cb8f26 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
@@ -21,51 +21,63 @@
 import java.awt.Paint;
 
 public class LinearGradient extends Shader {
-    
+
     private GradientPaint mGradientPaint;
 
-    /** Create a shader that draws a linear gradient along a line.
-        @param x0           The x-coordinate for the start of the gradient line
-        @param y0           The y-coordinate for the start of the gradient line
-        @param x1           The x-coordinate for the end of the gradient line
-        @param y1           The y-coordinate for the end of the gradient line
-        @param  colors      The colors to be distributed along the gradient line
-        @param  positions   May be null. The relative positions [0..1] of
-                            each corresponding color in the colors array. If this is null,
-                            the the colors are distributed evenly along the gradient line.
-        @param  tile        The Shader tiling mode
-    */
-    public LinearGradient(float x0, float y0, float x1, float y1,
-                          int colors[], float positions[], TileMode tile) {
+    /**
+     * Create a shader that draws a linear gradient along a line.
+     *
+     * @param x0 The x-coordinate for the start of the gradient line
+     * @param y0 The y-coordinate for the start of the gradient line
+     * @param x1 The x-coordinate for the end of the gradient line
+     * @param y1 The y-coordinate for the end of the gradient line
+     * @param colors The colors to be distributed along the gradient line
+     * @param positions May be null. The relative positions [0..1] of each
+     *            corresponding color in the colors array. If this is null, the
+     *            the colors are distributed evenly along the gradient line.
+     * @param tile The Shader tiling mode
+     */
+    public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
+            TileMode tile) {
         if (colors.length < 2) {
             throw new IllegalArgumentException("needs >= 2 number of colors");
         }
         if (positions != null && colors.length != positions.length) {
             throw new IllegalArgumentException("color and position arrays must be of equal length");
         }
-        
+
         // FIXME implement multi color linear gradient
+        if (colors.length == 2) {
+            // The hasAlpha flag in Color() is only used to enforce alpha to 0xFF if false.
+            // If true the alpha is read from the int.
+            mGradientPaint = new GradientPaint(x0, y0, new Color(colors[0], true /* hasalpha */),
+                    x1, y1, new Color(colors[1], true /* hasalpha */), tile != TileMode.CLAMP);
+        }
     }
 
-    /** Create a shader that draws a linear gradient along a line.
-        @param x0       The x-coordinate for the start of the gradient line
-        @param y0       The y-coordinate for the start of the gradient line
-        @param x1       The x-coordinate for the end of the gradient line
-        @param y1       The y-coordinate for the end of the gradient line
-        @param  color0  The color at the start of the gradient line.
-        @param  color1  The color at the end of the gradient line.
-        @param  tile    The Shader tiling mode
-    */
-    public LinearGradient(float x0, float y0, float x1, float y1,
-                          int color0, int color1, TileMode tile) {
-        mGradientPaint = new GradientPaint(x0, y0, new Color(color0, true /* hasalpha */),
-                x1,y1, new Color(color1, true /* hasalpha */), tile != TileMode.CLAMP);
+    /**
+     * Create a shader that draws a linear gradient along a line.
+     *
+     * @param x0 The x-coordinate for the start of the gradient line
+     * @param y0 The y-coordinate for the start of the gradient line
+     * @param x1 The x-coordinate for the end of the gradient line
+     * @param y1 The y-coordinate for the end of the gradient line
+     * @param color0 The color at the start of the gradient line.
+     * @param color1 The color at the end of the gradient line.
+     * @param tile The Shader tiling mode
+     */
+    public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,
+            TileMode tile) {
+        // The hasAlpha flag in Color() is only used to enforce alpha to 0xFF if false.
+        // If true the alpha is read from the int.
+        mGradientPaint = new GradientPaint(x0, y0, new Color(color0, true /* hasalpha */), x1, y1,
+                new Color(color1, true /* hasalpha */), tile != TileMode.CLAMP);
     }
-    
-    //---------- Custom Methods
-    
-    public Paint getPaint() {
+
+    // ---------- Custom Methods
+
+    @Override
+    public Paint getJavaPaint() {
         return mGradientPaint;
     }
 }
-
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/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java
index f3af133..312dab3 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint.java
@@ -59,23 +59,14 @@
     private final FontRenderContext mFontContext = new FontRenderContext(
             new AffineTransform(), true, true);
 
-    @SuppressWarnings("hiding")
     public static final int ANTI_ALIAS_FLAG       = _Original_Paint.ANTI_ALIAS_FLAG;
-    @SuppressWarnings("hiding")
     public static final int FILTER_BITMAP_FLAG    = _Original_Paint.FILTER_BITMAP_FLAG;
-    @SuppressWarnings("hiding")
     public static final int DITHER_FLAG           = _Original_Paint.DITHER_FLAG;
-    @SuppressWarnings("hiding")
     public static final int UNDERLINE_TEXT_FLAG   = _Original_Paint.UNDERLINE_TEXT_FLAG;
-    @SuppressWarnings("hiding")
     public static final int STRIKE_THRU_TEXT_FLAG = _Original_Paint.STRIKE_THRU_TEXT_FLAG;
-    @SuppressWarnings("hiding")
     public static final int FAKE_BOLD_TEXT_FLAG   = _Original_Paint.FAKE_BOLD_TEXT_FLAG;
-    @SuppressWarnings("hiding")
     public static final int LINEAR_TEXT_FLAG      = _Original_Paint.LINEAR_TEXT_FLAG;
-    @SuppressWarnings("hiding")
     public static final int SUBPIXEL_TEXT_FLAG    = _Original_Paint.SUBPIXEL_TEXT_FLAG;
-    @SuppressWarnings("hiding")
     public static final int DEV_KERN_TEXT_FLAG    = _Original_Paint.DEV_KERN_TEXT_FLAG;
 
     public static class FontMetrics extends _Original_Paint.FontMetrics {
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
index 61b693a..13848c5 100644
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import java.awt.Paint;
+
 public class RadialGradient extends Shader {
 
    /** Create a shader that draws a radial gradient given the center and radius.
@@ -58,5 +60,11 @@
        }
        // FIXME Implement shader
    }
+
+    @Override
+    Paint getJavaPaint() {
+        // TODO Auto-generated method stub
+        return null;
+    }
 }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader.java b/tools/layoutlib/bridge/src/android/graphics/Shader.java
index 3a9fda5..0cc5940 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Shader.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Shader.java
@@ -16,14 +16,16 @@
 
 package android.graphics;
 
+
+
 /**
  * Shader is the based class for objects that return horizontal spans of colors
  * during drawing. A subclass of Shader is installed in a Paint calling
  * paint.setShader(shader). After that any object (other than a bitmap) that is
  * drawn with that paint will get its color(s) from the shader.
  */
-public class Shader {
-    
+public abstract class Shader {
+
     private final Matrix mMatrix = new Matrix();
 
     public enum TileMode {
@@ -41,7 +43,7 @@
          * mirror images so that adjacent images always seam
          */
         MIRROR  (2);
-    
+
         TileMode(int nativeInt) {
             this.nativeInt = nativeInt;
         }
@@ -57,7 +59,7 @@
         if (localM != null) {
             localM.set(mMatrix);
         }
-        
+
         return !mMatrix.isIdentity();
     }
 
@@ -73,4 +75,9 @@
             mMatrix.reset();
         }
     }
+
+    /**
+     * Returns a java.awt.Paint object matching this shader.
+     */
+    abstract java.awt.Paint getJavaPaint();
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
index e79e970..21d8244 100644
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import java.awt.Paint;
+
 public class SweepGradient extends Shader {
 
     /**
@@ -41,7 +43,7 @@
             throw new IllegalArgumentException(
                         "color and position arrays must be of equal length");
         }
-        
+
         // FIXME Implement shader
     }
 
@@ -56,5 +58,11 @@
     public SweepGradient(float cx, float cy, int color0, int color1) {
         // FIXME Implement shader
     }
+
+    @Override
+    Paint getJavaPaint() {
+        // TODO Auto-generated method stub
+        return null;
+    }
 }
 
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",