Merge "Support for 64-bit integer type."
diff --git a/api/current.xml b/api/current.xml
index 403820d..84407e0 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -6928,6 +6928,17 @@
  visibility="public"
 >
 </field>
+<field name="loopViews"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843592"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="manageSpaceActivity"
  type="int"
  transient="false"
@@ -61109,6 +61120,16 @@
  visibility="public"
 >
 </field>
+<field name="filename"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="flags"
  type="int"
  transient="false"
@@ -139862,7 +139883,7 @@
  visibility="public"
 >
 <method name="allowThreadDiskReads"
- return="int"
+ return="android.os.StrictMode.ThreadPolicy"
  abstract="false"
  native="false"
  synchronized="false"
@@ -139873,7 +139894,7 @@
 >
 </method>
 <method name="allowThreadDiskWrites"
- return="int"
+ return="android.os.StrictMode.ThreadPolicy"
  abstract="false"
  native="false"
  synchronized="false"
@@ -139884,7 +139905,18 @@
 >
 </method>
 <method name="getThreadPolicy"
- return="int"
+ return="android.os.StrictMode.ThreadPolicy"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getVmPolicy"
+ return="android.os.StrictMode.VmPolicy"
  abstract="false"
  native="false"
  synchronized="false"
@@ -139904,86 +139936,313 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="policyMask" type="int">
+<parameter name="policy" type="android.os.StrictMode.ThreadPolicy">
 </parameter>
 </method>
-<field name="DISALLOW_DISK_READ"
- type="int"
+<method name="setVmPolicy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="policy" type="android.os.StrictMode.VmPolicy">
+</parameter>
+</method>
+</class>
+<class name="StrictMode.ThreadPolicy"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="LAX"
+ type="android.os.StrictMode.ThreadPolicy"
  transient="false"
  volatile="false"
- value="2"
  static="true"
  final="true"
  deprecated="not deprecated"
  visibility="public"
 >
 </field>
-<field name="DISALLOW_DISK_WRITE"
- type="int"
+</class>
+<class name="StrictMode.ThreadPolicy.Builder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="StrictMode.ThreadPolicy.Builder"
+ type="android.os.StrictMode.ThreadPolicy.Builder"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="StrictMode.ThreadPolicy.Builder"
+ type="android.os.StrictMode.ThreadPolicy.Builder"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="policy" type="android.os.StrictMode.ThreadPolicy">
+</parameter>
+</constructor>
+<method name="build"
+ return="android.os.StrictMode.ThreadPolicy"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="detectAll"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="detectDiskReads"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="detectDiskWrites"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="detectNetwork"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="penaltyDeath"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="penaltyDialog"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="penaltyDropBox"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="penaltyLog"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="permitAll"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="permitDiskReads"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="permitDiskWrites"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="permitNetwork"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="StrictMode.VmPolicy"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="LAX"
+ type="android.os.StrictMode.VmPolicy"
  transient="false"
  volatile="false"
- value="1"
  static="true"
  final="true"
  deprecated="not deprecated"
  visibility="public"
 >
 </field>
-<field name="DISALLOW_NETWORK"
- type="int"
- transient="false"
- volatile="false"
- value="4"
+</class>
+<class name="StrictMode.VmPolicy.Builder"
+ extends="java.lang.Object"
+ abstract="false"
  static="true"
  final="true"
  deprecated="not deprecated"
  visibility="public"
 >
-</field>
-<field name="PENALTY_DEATH"
- type="int"
- transient="false"
- volatile="false"
- value="64"
- static="true"
- final="true"
+<constructor name="StrictMode.VmPolicy.Builder"
+ type="android.os.StrictMode.VmPolicy.Builder"
+ static="false"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
-</field>
-<field name="PENALTY_DIALOG"
- type="int"
- transient="false"
- volatile="false"
- value="32"
- static="true"
- final="true"
+</constructor>
+<method name="build"
+ return="android.os.StrictMode.VmPolicy"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
-</field>
-<field name="PENALTY_DROPBOX"
- type="int"
- transient="false"
- volatile="false"
- value="128"
- static="true"
- final="true"
+</method>
+<method name="detectAll"
+ return="android.os.StrictMode.VmPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
-</field>
-<field name="PENALTY_LOG"
- type="int"
- transient="false"
- volatile="false"
- value="16"
- static="true"
- final="true"
+</method>
+<method name="detectLeakedSqlLiteObjects"
+ return="android.os.StrictMode.VmPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
-</field>
+</method>
+<method name="penaltyDeath"
+ return="android.os.StrictMode.VmPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="penaltyDropBox"
+ return="android.os.StrictMode.VmPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="penaltyLog"
+ return="android.os.StrictMode.VmPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 </class>
 <class name="SystemClock"
  extends="java.lang.Object"
@@ -140545,8 +140804,6 @@
 </parameter>
 <parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
 </parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
 </method>
 <method name="unregisterListener"
  return="void"
@@ -189612,6 +189869,217 @@
 >
 </field>
 </class>
+<class name="DragEvent"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAction"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getClipData"
+ return="android.content.ClipData"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getClipDescription"
+ return="android.content.ClipDescription"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="obtain"
+ return="android.view.DragEvent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="obtain"
+ return="android.view.DragEvent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="action" type="int">
+</parameter>
+<parameter name="x" type="float">
+</parameter>
+<parameter name="y" type="float">
+</parameter>
+<parameter name="description" type="android.content.ClipDescription">
+</parameter>
+<parameter name="data" type="android.content.ClipData">
+</parameter>
+</method>
+<method name="recycle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</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="ACTION_DRAG_ENDED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DRAG_ENTERED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DRAG_EXITED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DRAG_LOCATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DRAG_STARTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DROP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<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="FocusFinder"
  extends="java.lang.Object"
  abstract="false"
@@ -198568,6 +199036,19 @@
 <parameter name="hint" type="int">
 </parameter>
 </method>
+<method name="dispatchDragEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.DragEvent">
+</parameter>
+</method>
 <method name="dispatchDraw"
  return="void"
  abstract="false"
@@ -200533,6 +201014,19 @@
 <parameter name="hint" type="int">
 </parameter>
 </method>
+<method name="onDragEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="event" type="android.view.DragEvent">
+</parameter>
+</method>
 <method name="onDraw"
  return="void"
  abstract="false"
@@ -200546,6 +201040,19 @@
 <parameter name="canvas" type="android.graphics.Canvas">
 </parameter>
 </method>
+<method name="onDrawDragThumbnail"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="canvas" type="android.graphics.Canvas">
+</parameter>
+</method>
 <method name="onDrawScrollBars"
  return="void"
  abstract="false"
@@ -200739,6 +201246,17 @@
 <parameter name="heightMeasureSpec" type="int">
 </parameter>
 </method>
+<method name="onMeasureDragThumbnail"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
 <method name="onRestoreInstanceState"
  return="void"
  abstract="false"
@@ -201404,6 +201922,21 @@
 <parameter name="contentDescription" type="java.lang.CharSequence">
 </parameter>
 </method>
+<method name="setDragThumbnailDimension"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+</method>
 <method name="setDrawingCacheBackgroundColor"
  return="void"
  abstract="false"
@@ -249475,7 +250008,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="n" type="long">
+<parameter name="byteCount" type="long">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 39b3a20..37c8ad0 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -34,7 +34,6 @@
 
     private String[] mArgs;
     private int mNextArg;
-    private String mCurArgData;
 
     public static void main(String[] args) {
         try {
@@ -274,6 +273,10 @@
     }
 
     private void printRestoreSets(RestoreSet[] sets) {
+        if (sets == null || sets.length == 0) {
+            System.out.println("No restore sets");
+            return;
+        }
         for (RestoreSet s : sets) {
             System.out.println("  " + Long.toHexString(s.token) + " : " + s.name);
         }
diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c
index 971a177..afa64f8 100644
--- a/cmds/keystore/keystore.c
+++ b/cmds/keystore/keystore.c
@@ -143,15 +143,20 @@
     send(the_socket, message, length, 0);
 }
 
-/* Here is the file format. Values are encrypted by AES CBC, and MD5 is used to
- * compute their checksums. To make the files portable, the length is stored in
- * network order. Note that the first four bytes are reserved for future use and
- * are always set to zero in this implementation. */
+/* Here is the file format. There are two parts in blob.value, the secret and
+ * the description. The secret is stored in ciphertext, and its original size
+ * can be found in blob.length. The description is stored after the secret in
+ * plaintext, and its size is specified in blob.info. The total size of the two
+ * parts must be no more than VALUE_SIZE bytes. The first three bytes of the
+ * file are reserved for future use and are always set to zero. Fields other
+ * than blob.info, blob.length, and blob.value are modified by encrypt_blob()
+ * and decrypt_blob(). Thus they should not be accessed from outside. */
 
 static int the_entropy = -1;
 
 static struct __attribute__((packed)) {
-    uint32_t reserved;
+    uint8_t reserved[3];
+    uint8_t info;
     uint8_t vector[AES_BLOCK_SIZE];
     uint8_t encrypted[0];
     uint8_t digest[MD5_DIGEST_LENGTH];
@@ -170,9 +175,13 @@
         return SYSTEM_ERROR;
     }
 
-    length = blob.length + blob.value - blob.encrypted;
+    length = blob.length + (blob.value - blob.encrypted);
     length = (length + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE * AES_BLOCK_SIZE;
 
+    if (blob.info != 0) {
+        memmove(&blob.encrypted[length], &blob.value[blob.length], blob.info);
+    }
+
     blob.length = htonl(blob.length);
     MD5(blob.digested, length - (blob.digested - blob.encrypted), blob.digest);
 
@@ -180,8 +189,8 @@
     AES_cbc_encrypt(blob.encrypted, blob.encrypted, length, aes_key, vector,
                     AES_ENCRYPT);
 
-    blob.reserved = 0;
-    length += blob.encrypted - (uint8_t *)&blob;
+    memset(blob.reserved, 0, sizeof(blob.reserved));
+    length += (blob.encrypted - (uint8_t *)&blob) + blob.info;
 
     fd = open(".tmp", O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
     length -= write(fd, &blob, length);
@@ -200,7 +209,7 @@
     length = read(fd, &blob, sizeof(blob));
     close(fd);
 
-    length -= blob.encrypted - (uint8_t *)&blob;
+    length -= (blob.encrypted - (uint8_t *)&blob) + blob.info;
     if (length < blob.value - blob.encrypted || length % AES_BLOCK_SIZE != 0) {
         return VALUE_CORRUPTED;
     }
@@ -215,8 +224,13 @@
 
     length -= blob.value - blob.digested;
     blob.length = ntohl(blob.length);
-    return (blob.length < 0 || blob.length > length) ? VALUE_CORRUPTED :
-           NO_ERROR;
+    if (blob.length < 0 || blob.length > length) {
+        return VALUE_CORRUPTED;
+    }
+    if (blob.info != 0) {
+        memmove(&blob.value[blob.length], &blob.value[length], blob.info);
+    }
+    return NO_ERROR;
 }
 
 /* Here are the actions. Each of them is a function without arguments. All
@@ -266,6 +280,7 @@
     char name[NAME_MAX];
     int n = sprintf(name, "%u_", uid);
     encode_key(&name[n], params[0].value, params[0].length);
+    blob.info = 0;
     blob.length = params[1].length;
     memcpy(blob.value, params[1].value, params[1].length);
     return encrypt_blob(name, &encryption_key);
@@ -336,56 +351,88 @@
 
 #define MASTER_KEY_FILE ".masterkey"
 #define MASTER_KEY_SIZE 16
+#define SALT_SIZE       16
 
-static void generate_key(uint8_t *key, uint8_t *password, int length)
+static void set_key(uint8_t *key, uint8_t *password, int length, uint8_t *salt)
 {
-    PKCS5_PBKDF2_HMAC_SHA1((char *)password, length, (uint8_t *)"keystore",
-                           sizeof("keystore"), 1024, MASTER_KEY_SIZE, key);
+    if (salt) {
+        PKCS5_PBKDF2_HMAC_SHA1((char *)password, length, salt, SALT_SIZE,
+                               8192, MASTER_KEY_SIZE, key);
+    } else {
+        PKCS5_PBKDF2_HMAC_SHA1((char *)password, length, (uint8_t *)"keystore",
+                               sizeof("keystore"), 1024, MASTER_KEY_SIZE, key);
+    }
 }
 
+/* Here is the history. To improve the security, the parameters to generate the
+ * master key has been changed. To make a seamless transition, we update the
+ * file using the same password when the user unlock it for the first time. If
+ * any thing goes wrong during the transition, the new file will not overwrite
+ * the old one. This avoids permanent damages of the existing data. */
+
 static int8_t password()
 {
     uint8_t key[MASTER_KEY_SIZE];
     AES_KEY aes_key;
-    int n;
+    int8_t response = SYSTEM_ERROR;
 
     if (state == UNINITIALIZED) {
-        blob.length = MASTER_KEY_SIZE;
         if (read(the_entropy, blob.value, MASTER_KEY_SIZE) != MASTER_KEY_SIZE) {
            return SYSTEM_ERROR;
         }
     } else {
-        generate_key(key, params[0].value, params[0].length);
+        int fd = open(MASTER_KEY_FILE, O_RDONLY);
+        uint8_t *salt = NULL;
+        if (fd != -1) {
+            int length = read(fd, &blob, sizeof(blob));
+            close(fd);
+            if (length > SALT_SIZE && blob.info == SALT_SIZE) {
+                salt = (uint8_t *)&blob + length - SALT_SIZE;
+            }
+        }
+
+        set_key(key, params[0].value, params[0].length, salt);
         AES_set_decrypt_key(key, MASTER_KEY_SIZE * 8, &aes_key);
-        n = decrypt_blob(MASTER_KEY_FILE, &aes_key);
-        if (n == SYSTEM_ERROR) {
+        response = decrypt_blob(MASTER_KEY_FILE, &aes_key);
+        if (response == SYSTEM_ERROR) {
             return SYSTEM_ERROR;
         }
-        if (n != NO_ERROR || blob.length != MASTER_KEY_SIZE) {
+        if (response != NO_ERROR || blob.length != MASTER_KEY_SIZE) {
             if (retry <= 0) {
                 reset();
                 return UNINITIALIZED;
             }
             return WRONG_PASSWORD + --retry;
         }
+
+        if (!salt && params[1].length == -1) {
+            params[1] = params[0];
+        }
     }
 
     if (params[1].length == -1) {
         memcpy(key, blob.value, MASTER_KEY_SIZE);
     } else {
-        generate_key(key, params[1].value, params[1].length);
+        uint8_t *salt = &blob.value[MASTER_KEY_SIZE];
+        if (read(the_entropy, salt, SALT_SIZE) != SALT_SIZE) {
+            return SYSTEM_ERROR;
+        }
+
+        set_key(key, params[1].value, params[1].length, salt);
         AES_set_encrypt_key(key, MASTER_KEY_SIZE * 8, &aes_key);
         memcpy(key, blob.value, MASTER_KEY_SIZE);
-        n = encrypt_blob(MASTER_KEY_FILE, &aes_key);
+        blob.info = SALT_SIZE;
+        blob.length = MASTER_KEY_SIZE;
+        response = encrypt_blob(MASTER_KEY_FILE, &aes_key);
     }
 
-    if (n == NO_ERROR) {
+    if (response == NO_ERROR) {
         AES_set_encrypt_key(key, MASTER_KEY_SIZE * 8, &encryption_key);
         AES_set_decrypt_key(key, MASTER_KEY_SIZE * 8, &decryption_key);
         state = NO_ERROR;
         retry = MAX_RETRY;
     }
-    return n;
+    return response;
 }
 
 static int8_t lock()
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 6ce5b86..bc5e10d 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -17,32 +17,21 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-#include <utils/Log.h>
-
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-
 #include <binder/IMemory.h>
-#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
 
 using namespace android;
 
 int main(int argc, char** argv)
 {
-    const String16 name("SurfaceFlinger");
-    sp<ISurfaceComposer> composer;
-    if (getService(name, &composer) != NO_ERROR)
+    ScreenshotClient screenshot;
+    if (screenshot.update() != NO_ERROR)
         return 0;
 
-    sp<IMemoryHeap> heap;
-    uint32_t w, h;
-    PixelFormat f;
-    status_t err = composer->captureScreen(0, &heap, &w, &h, &f);
-    if (err != NO_ERROR)
-        return 0;
-
-    uint8_t* base = (uint8_t*)heap->getBase();
+    void const* base = screenshot.getPixels();
+    uint32_t w = screenshot.getWidth();
+    uint32_t h = screenshot.getHeight();
+    uint32_t f = screenshot.getFormat();
     int fd = dup(STDOUT_FILENO);
     write(fd, &w, 4);
     write(fd, &h, 4);
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 9a97284..cbdf119 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -53,6 +53,31 @@
 
 LOCAL_SRC_FILES:=         \
         SineSource.cpp    \
+        recordvideo.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libstagefright liblog libutils libbinder
+
+LOCAL_C_INCLUDES:= \
+	$(JNI_H_INCLUDE) \
+	frameworks/base/media/libstagefright \
+	$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE_TAGS := debug
+
+LOCAL_MODULE:= recordvideo
+
+include $(BUILD_EXECUTABLE)
+
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=         \
+        SineSource.cpp    \
         audioloop.cpp
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp
new file mode 100644
index 0000000..330fbc2
--- /dev/null
+++ b/cmds/stagefright/recordvideo.cpp
@@ -0,0 +1,309 @@
+/*
+ * 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 "SineSource.h"
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
+#include <media/MediaPlayerInterface.h>
+
+using namespace android;
+
+// print usage showing how to use this utility to record videos
+static void usage(const char *me) {
+    fprintf(stderr, "usage: %s\n", me);
+    fprintf(stderr, "       -h(elp)\n");
+    fprintf(stderr, "       -b bit rate in bits per second (default 300000)\n");
+    fprintf(stderr, "       -c YUV420 color format: [0] semi planar or [1] planar (default 1)\n");
+    fprintf(stderr, "       -f frame rate in frames per second (default 30)\n");
+    fprintf(stderr, "       -i I frame interval in seconds (default 1)\n");
+    fprintf(stderr, "       -n number of frames to be recorded (default 300)\n");
+    fprintf(stderr, "       -w width in pixels (default 176)\n");
+    fprintf(stderr, "       -t height in pixels (default 144)\n");
+    fprintf(stderr, "       -v video codec: [0] AVC [1] M4V [2] H263 (default 0)\n");
+    exit(1);
+}
+
+class DummySource : public MediaSource {
+
+public:
+    DummySource(int width, int height, int nFrames, int fps, int colorFormat)
+        : mWidth(width),
+          mHeight(height),
+          mMaxNumFrames(nFrames),
+          mFrameRate(fps),
+          mColorFormat(colorFormat),
+          mSize((width * height * 3) / 2) {
+        mGroup.add_buffer(new MediaBuffer(mSize));
+
+        // Check the color format to make sure
+        // that the buffer size mSize it set correctly above.
+        CHECK(colorFormat == OMX_COLOR_FormatYUV420SemiPlanar ||
+              colorFormat == OMX_COLOR_FormatYUV420Planar);
+    }
+
+    virtual sp<MetaData> getFormat() {
+        sp<MetaData> meta = new MetaData;
+        meta->setInt32(kKeyWidth, mWidth);
+        meta->setInt32(kKeyHeight, mHeight);
+        meta->setInt32(kKeyColorFormat, mColorFormat);
+        meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
+
+        return meta;
+    }
+
+    virtual status_t start(MetaData *params) {
+        mNumFramesOutput = 0;
+        return OK;
+    }
+
+    virtual status_t stop() {
+        return OK;
+    }
+
+    virtual status_t read(
+            MediaBuffer **buffer, const MediaSource::ReadOptions *options) {
+
+        if (mNumFramesOutput % 10 == 0) {
+            fprintf(stderr, ".");
+        }
+        if (mNumFramesOutput == mMaxNumFrames) {
+            return ERROR_END_OF_STREAM;
+        }
+
+        status_t err = mGroup.acquire_buffer(buffer);
+        if (err != OK) {
+            return err;
+        }
+
+        char x = (char)((double)rand() / RAND_MAX * 255);
+        memset((*buffer)->data(), x, mSize);
+        (*buffer)->set_range(0, mSize);
+        (*buffer)->meta_data()->clear();
+        (*buffer)->meta_data()->setInt64(
+                kKeyTime, (mNumFramesOutput * 1000000) / mFrameRate);
+        ++mNumFramesOutput;
+
+        return OK;
+    }
+
+protected:
+    virtual ~DummySource() {}
+
+private:
+    MediaBufferGroup mGroup;
+    int mWidth, mHeight;
+    int mMaxNumFrames;
+    int mFrameRate;
+    int mColorFormat;
+    size_t mSize;
+    int64_t mNumFramesOutput;;
+
+    DummySource(const DummySource &);
+    DummySource &operator=(const DummySource &);
+};
+
+sp<MediaSource> createSource(const char *filename) {
+    sp<MediaSource> source;
+
+    sp<MediaExtractor> extractor =
+        MediaExtractor::Create(new FileSource(filename));
+    if (extractor == NULL) {
+        return NULL;
+    }
+
+    size_t num_tracks = extractor->countTracks();
+
+    sp<MetaData> meta;
+    for (size_t i = 0; i < num_tracks; ++i) {
+        meta = extractor->getTrackMetaData(i);
+        CHECK(meta.get() != NULL);
+
+        const char *mime;
+        if (!meta->findCString(kKeyMIMEType, &mime)) {
+            continue;
+        }
+
+        if (strncasecmp(mime, "video/", 6)) {
+            continue;
+        }
+
+        source = extractor->getTrack(i);
+        break;
+    }
+
+    return source;
+}
+
+enum {
+    kYUV420SP = 0,
+    kYUV420P  = 1,
+};
+
+// returns -1 if mapping of the given color is unsuccessful
+// returns an omx color enum value otherwise
+static int translateColorToOmxEnumValue(int color) {
+    switch (color) {
+        case kYUV420SP:
+            return OMX_COLOR_FormatYUV420SemiPlanar;
+        case kYUV420P:
+            return OMX_COLOR_FormatYUV420Planar;
+        default:
+            fprintf(stderr, "Unsupported color: %d\n", color);
+            return -1;
+    }
+}
+
+int main(int argc, char **argv) {
+
+    // Default values for the program if not overwritten
+    int frameRateFps = 30;
+    int width = 176;
+    int height = 144;
+    int bitRateBps = 300000;
+    int iFramesIntervalSeconds = 1;
+    int colorFormat = OMX_COLOR_FormatYUV420Planar;
+    int nFrames = 300;
+    int codec = 0;
+    const char *fileName = "/sdcard/output.mp4";
+
+    android::ProcessState::self()->startThreadPool();
+    int res;
+    while ((res = getopt(argc, argv, "b:c:f:i:n:w:t:v:o:h")) >= 0) {
+        switch (res) {
+            case 'b':
+            {
+                bitRateBps = atoi(optarg);
+                break;
+            }
+
+            case 'c':
+            {
+                colorFormat = translateColorToOmxEnumValue(atoi(optarg));
+                if (colorFormat == -1) {
+                    usage(argv[0]);
+                }
+                break;
+            }
+
+            case 'f':
+            {
+                frameRateFps = atoi(optarg);
+                break;
+            }
+
+            case 'i':
+            {
+                iFramesIntervalSeconds = atoi(optarg);
+                break;
+            }
+
+            case 'n':
+            {
+                nFrames = atoi(optarg);
+                break;
+            }
+
+            case 'w':
+            {
+                width = atoi(optarg);
+                break;
+            }
+
+            case 't':
+            {
+                height = atoi(optarg);
+                break;
+            }
+
+            case 'v':
+            {
+                codec = atoi(optarg);
+                if (codec < 0 || codec > 2) {
+                    usage(argv[0]);
+                }
+                break;
+            }
+
+            case 'h':
+            default:
+            {
+                usage(argv[0]);
+                break;
+            }
+        }
+    }
+
+    OMXClient client;
+    CHECK_EQ(client.connect(), OK);
+
+    status_t err = OK;
+    sp<MediaSource> decoder = new DummySource(width, height, nFrames, frameRateFps, colorFormat);
+
+    sp<MetaData> enc_meta = new MetaData;
+    switch (codec) {
+        case 1:
+            enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+            break;
+        case 2:
+            enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
+            break;
+        default:
+            enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+            break;
+    }
+    enc_meta->setInt32(kKeyWidth, width);
+    enc_meta->setInt32(kKeyHeight, height);
+    enc_meta->setInt32(kKeySampleRate, frameRateFps);
+    enc_meta->setInt32(kKeyBitRate, bitRateBps);
+    enc_meta->setInt32(kKeyStride, width);
+    enc_meta->setInt32(kKeySliceHeight, height);
+    enc_meta->setInt32(kKeyIFramesInterval, iFramesIntervalSeconds);
+    enc_meta->setInt32(kKeyColorFormat, colorFormat);
+
+    sp<MediaSource> encoder =
+        OMXCodec::Create(
+                client.interface(), enc_meta, true /* createEncoder */, decoder);
+
+    sp<MPEG4Writer> writer = new MPEG4Writer(fileName);
+    writer->addSource(encoder);
+    int64_t start = systemTime();
+    CHECK_EQ(OK, writer->start());
+    while (!writer->reachedEOS()) {
+    }
+    err = writer->stop();
+    int64_t end = systemTime();
+
+    printf("$\n");
+    client.disconnect();
+
+    if (err != OK && err != ERROR_END_OF_STREAM) {
+        fprintf(stderr, "record failed: %d\n", err);
+        return 1;
+    }
+    fprintf(stderr, "encoding %d frames in %lld us\n", nFrames, (end-start)/1000);
+    fprintf(stderr, "encoding speed is: %.2f fps\n", (nFrames * 1E9) / (end-start));
+    return 0;
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b8bbc88..df18ce7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3352,7 +3352,7 @@
         while (i.hasNext()) {
             ProviderInfo cpi = i.next();
             StringBuilder buf = new StringBuilder(128);
-            buf.append("Publishing provider ");
+            buf.append("Pub ");
             buf.append(cpi.authority);
             buf.append(": ");
             buf.append(cpi.name);
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 0398b36..013032c 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -47,6 +47,10 @@
  * Instances of this class should be obtained through
  * {@link android.content.Context#getSystemService(String)} by passing
  * {@link android.content.Context#DOWNLOAD_SERVICE}.
+ *
+ * Apps that request downloads through this API should register a broadcast receiver for
+ * {@link #ACTION_NOTIFICATION_CLICKED} to appropriately handle when the user clicks on a running
+ * download in a notification or from the downloads UI.
  */
 public class DownloadManager {
     /**
@@ -239,8 +243,8 @@
     public final static String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE";
 
     /**
-     * Broadcast intent action sent by the download manager when a running download notification is
-     * clicked.
+     * Broadcast intent action sent by the download manager when the user clicks on a running
+     * download, either from a system notification or from the downloads UI.
      */
     public final static String ACTION_NOTIFICATION_CLICKED =
             "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
@@ -413,7 +417,9 @@
         }
 
         /**
-         * Set the title of this download, to be displayed in notifications (if enabled)
+         * Set the title of this download, to be displayed in notifications (if enabled).  If no
+         * title is given, a default one will be assigned based on the download filename, once the
+         * download starts.
          * @return this object
          */
         public Request setTitle(CharSequence title) {
@@ -923,7 +929,11 @@
 
             if (destinationType == Downloads.Impl.DESTINATION_EXTERNAL) {
                 // return stored destination for legacy external download
-                return Uri.fromFile(new File(getUnderlyingString(Downloads.Impl._DATA))).toString();
+                String localPath = getUnderlyingString(Downloads.Impl._DATA);
+                if (localPath == null) {
+                    return null;
+                }
+                return Uri.fromFile(new File(localPath)).toString();
             }
 
             // return content URI for cache download
diff --git a/core/java/android/bluetooth/AtCommandResult.java b/core/java/android/bluetooth/AtCommandResult.java
index 638be2d..375a6dd 100644
--- a/core/java/android/bluetooth/AtCommandResult.java
+++ b/core/java/android/bluetooth/AtCommandResult.java
@@ -16,8 +16,6 @@
 
 package android.bluetooth;
 
-import java.util.*;
-
 /**
  * The result of execution of an single AT command.<p>
  *
diff --git a/core/java/android/bluetooth/AtParser.java b/core/java/android/bluetooth/AtParser.java
index 1ea3150..328fb2b 100644
--- a/core/java/android/bluetooth/AtParser.java
+++ b/core/java/android/bluetooth/AtParser.java
@@ -16,16 +16,13 @@
 
 package android.bluetooth;
 
-import android.bluetooth.AtCommandHandler;
-import android.bluetooth.AtCommandResult;
-
 import java.util.*;
 
 /**
  * An AT (Hayes command) Parser based on (a subset of) the ITU-T V.250 standard.
  * <p>
  *
- * Conforment with the subset of V.250 required for implementation of the
+ * Conformant with the subset of V.250 required for implementation of the
  * Bluetooth Headset and Handsfree Profiles, as per Bluetooth SIP
  * specifications. Also implements some V.250 features not required by
  * Bluetooth - such as chained commands.<p>
@@ -48,7 +45,7 @@
  * are no arguments for get commands.
  * <li>Set Command. For example "AT+VGM=14". The command name is "VGM", and
  * there is a single integer argument in this case. In the general case then
- * can be zero or more arguments (comma deliminated) each of integer or string
+ * can be zero or more arguments (comma delimited) each of integer or string
  * form.
  * <li>Test Command. For example "AT+VGM=?. No arguments.
  * </ul>
@@ -60,7 +57,7 @@
  * headset/handsfree use this is acceptable, because they only use the basic
  * commands ATA and ATD, which are not allowed to be chained. For general V.250
  * use we would need to improve this class to allow Basic command chaining -
- * however its tricky to get right becuase there is no deliminator for Basic
+ * however it's tricky to get right because there is no delimiter for Basic
  * command chaining.<p>
  *
  * Extended commands can be chained. For example:<p>
@@ -71,7 +68,7 @@
  * AT+CIMI
  * Except that only one final result code is return (although several
  * intermediate responses may be returned), and as soon as one command in the
- * chain fails the rest are abandonded.<p>
+ * chain fails the rest are abandoned.<p>
  *
  * Handlers are registered by there command name via register(Char c, ...) or
  * register(String s, ...). Handlers for Basic command should be registered by
@@ -80,7 +77,7 @@
  *
  * Refer to:<ul>
  * <li>ITU-T Recommendation V.250
- * <li>ETSI TS 127.007  (AT Comannd set for User Equipment, 3GPP TS 27.007)
+ * <li>ETSI TS 127.007  (AT Command set for User Equipment, 3GPP TS 27.007)
  * <li>Bluetooth Headset Profile Spec (K6)
  * <li>Bluetooth Handsfree Profile Spec (HFP 1.5)
  * </ul>
@@ -188,7 +185,7 @@
     }
 
     /**
-     * Break an argument string into individual arguments (comma deliminated).
+     * Break an argument string into individual arguments (comma delimited).
      * Integer arguments are turned into Integer objects. Otherwise a String
      * object is used.
      */
@@ -212,7 +209,7 @@
     }
 
     /**
-     * Return the index of the end of character after the last characeter in
+     * Return the index of the end of character after the last character in
      * the extended command name. Uses the V.250 spec for allowed command
      * names.
      */
@@ -244,7 +241,7 @@
      * Processes an incoming AT command line.<p>
      * This method will invoke zero or one command handler methods for each
      * command in the command line.<p>
-     * @param raw_input The AT input, without EOL deliminator (e.g. <CR>).
+     * @param raw_input The AT input, without EOL delimiter (e.g. <CR>).
      * @return          Result object for this command line. This can be
      *                  converted to a String[] response with toStrings().
      */
@@ -297,8 +294,8 @@
 
             if (c == '+') {
                 // Option 2: Extended Command
-                // Search for first non-name character. Shortcircuit if we dont
-                // handle this command name.
+                // Search for first non-name character. Short-circuit if
+                // we don't handle this command name.
                 int i = findEndExtendedName(input, index + 1);
                 String commandName = input.substring(index, i);
                 if (!mExtHandlers.containsKey(commandName)) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 21a4bd6..c66b2de 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -341,7 +341,7 @@
     private static final int ADDRESS_LENGTH = 17;
 
     /**
-     * Lazyily initialized singleton. Guaranteed final after first object
+     * Lazily initialized singleton. Guaranteed final after first object
      * constructed.
      */
     private static BluetoothAdapter sAdapter;
@@ -466,7 +466,7 @@
      * user action to turn off Bluetooth.
      * <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
      * system services, and powers down the underlying Bluetooth hardware.
-     * <p class="caution"><strong>Bluetooth should never be disbled without
+     * <p class="caution"><strong>Bluetooth should never be disabled without
      * direct user consent</strong>. The {@link #disable()} method is
      * provided only for applications that include a user interface for changing
      * system settings, such as a "power manager" app.</p>
@@ -932,8 +932,8 @@
     public Pair<byte[], byte[]> readOutOfBandData() {
         if (getState() != STATE_ON) return null;
         try {
-            byte[] hash = new byte[16];
-            byte[] randomizer = new byte[16];
+            byte[] hash;
+            byte[] randomizer;
 
             byte[] ret = mService.readOutOfBandData();
 
diff --git a/core/java/android/bluetooth/BluetoothAudioGateway.java b/core/java/android/bluetooth/BluetoothAudioGateway.java
index bc32060..9351393 100644
--- a/core/java/android/bluetooth/BluetoothAudioGateway.java
+++ b/core/java/android/bluetooth/BluetoothAudioGateway.java
@@ -23,7 +23,7 @@
 import android.util.Log;
 
 /**
- * Listen's for incoming RFCOMM connection for the headset / handsfree service.
+ * Listens for incoming RFCOMM connection for the headset / handsfree service.
  *
  * TODO: Use the new generic BluetoothSocket class instead of this legacy code
  *
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index c8381c9..e604e6b 100644
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -34,8 +34,8 @@
  * Bluetooth profiles or services are actually supported by a device. Accurate
  * service discovery is done through SDP requests, which are automatically
  * performed when creating an RFCOMM socket with {@link
- * BluetoothDevice#createRfcommSocketToServiceRecord(UUID)} and {@link
- * BluetoothAdapter#listenUsingRfcommWithServiceRecord(String,UUID)}</p>
+ * BluetoothDevice#createRfcommSocketToServiceRecord} and {@link
+ * BluetoothAdapter#listenUsingRfcommWithServiceRecord}</p>
  *
  * <p>Use {@link BluetoothDevice#getBluetoothClass} to retrieve the class for
  * a remote device.
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index e577ec4..ada3c24 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -32,7 +32,7 @@
 
 /**
  * Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you
- * create a connection with the repective device or query information about
+ * create a connection with the respective device or query information about
  * it, such as the name, address, class, and bonding state.
  *
  * <p>This class is really just a thin wrapper for a Bluetooth hardware
@@ -48,7 +48,7 @@
  * {@link BluetoothAdapter}) or get one from the set of bonded devices
  * returned by {@link BluetoothAdapter#getBondedDevices()
  * BluetoothAdapter.getBondedDevices()}. You can then open a
- * {@link BluetoothSocket} for communciation with the remote device, using
+ * {@link BluetoothSocket} for communication with the remote device, using
  * {@link #createRfcommSocketToServiceRecord(UUID)}.
  *
  * <p class="note"><strong>Note:</strong>
@@ -226,8 +226,8 @@
      * <p>A shared link keys exists locally for the remote device, so
      * communication can be authenticated and encrypted.
      * <p><i>Being bonded (paired) with a remote device does not necessarily
-     * mean the device is currently connected. It just means that the ponding
-     * procedure was compeleted at some earlier time, and the link key is still
+     * mean the device is currently connected. It just means that the pending
+     * procedure was completed at some earlier time, and the link key is still
      * stored locally, ready to use on the next connection.
      * </i>
      */
@@ -283,7 +283,7 @@
      * not respond to pin request in time
      * @hide */
     public static final int UNBOND_REASON_AUTH_FAILED = 1;
-    /** A bond attempt failed because the other side explicilty rejected
+    /** A bond attempt failed because the other side explicitly rejected
      * bonding
      * @hide */
     public static final int UNBOND_REASON_AUTH_REJECTED = 2;
@@ -515,7 +515,7 @@
      * Cancel an in-progress bonding request started with {@link #createBond}.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
      *
-     * @return true on sucess, false on error
+     * @return true on success, false on error
      * @hide
      */
     public boolean cancelBondProcess() {
@@ -532,7 +532,7 @@
      * authentication and encryption.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
      *
-     * @return true on sucess, false on error
+     * @return true on success, false on error
      * @hide
      */
     public boolean removeBond() {
@@ -617,7 +617,7 @@
       *  with the UUIDs supported by the remote end. If there is an error
       *  in getting the SDP records or if the process takes a long time,
       *  an Intent is sent with the UUIDs that is currently present in the
-      *  cache. Clients should use the {@link getUuids} to get UUIDs
+      *  cache. Clients should use the {@link #getUuids} to get UUIDs
       *  is SDP is not to be performed.
       *
       *  @return False if the sanity check fails, True if the process
@@ -693,7 +693,7 @@
      * outgoing connection to this remote device on given channel.
      * <p>The remote device will be authenticated and communication on this
      * socket will be encrypted.
-     * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing
+     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
      * connection.
      * <p>Valid RFCOMM channels are in range 1 to 30.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
@@ -715,7 +715,7 @@
      * <p>This is designed to be used with {@link
      * BluetoothAdapter#listenUsingRfcommWithServiceRecord} for peer-peer
      * Bluetooth applications.
-     * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing
+     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
      * connection. This will also perform an SDP lookup of the given uuid to
      * determine which channel to connect to.
      * <p>The remote device will be authenticated and communication on this
@@ -772,9 +772,9 @@
     /**
      * Check that a pin is valid and convert to byte array.
      *
-     * Bluetooth pin's are 1 to 16 bytes of UTF8 characters.
+     * Bluetooth pin's are 1 to 16 bytes of UTF-8 characters.
      * @param pin pin as java String
-     * @return the pin code as a UTF8 byte array, or null if it is an invalid
+     * @return the pin code as a UTF-8 byte array, or null if it is an invalid
      *         Bluetooth pin.
      * @hide
      */
@@ -784,9 +784,9 @@
         }
         byte[] pinBytes;
         try {
-            pinBytes = pin.getBytes("UTF8");
+            pinBytes = pin.getBytes("UTF-8");
         } catch (UnsupportedEncodingException uee) {
-            Log.e(TAG, "UTF8 not supported?!?");  // this should not happen
+            Log.e(TAG, "UTF-8 not supported?!?");  // this should not happen
             return null;
         }
         if (pinBytes.length <= 0 || pinBytes.length > 16) {
diff --git a/core/java/android/bluetooth/BluetoothDevicePicker.java b/core/java/android/bluetooth/BluetoothDevicePicker.java
index 7415721..c794be2 100644
--- a/core/java/android/bluetooth/BluetoothDevicePicker.java
+++ b/core/java/android/bluetooth/BluetoothDevicePicker.java
@@ -36,7 +36,8 @@
 
     /**
      * Broadcast when one BT device is selected from BT device picker screen.
-     * Selected BT device address is contained in extra string {@link BluetoothIntent}
+     * Selected {@link BluetoothDevice} is returned in extra data named
+     * {@link BluetoothDevice#EXTRA_DEVICE}.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_DEVICE_SELECTED =
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index b48f48e..4be077c 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -197,7 +197,7 @@
 
     /**
      * Disconnects the current Pbap client (PCE). Currently this call blocks,
-     * it may soon be made asynchornous. Returns false if this proxy object is
+     * it may soon be made asynchronous. Returns false if this proxy object is
      * not currently connected to the Pbap service.
      */
     public boolean disconnect() {
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index c9c6c0a..83e59e2 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -29,14 +29,14 @@
  * side, use a {@link BluetoothServerSocket} to create a listening server
  * socket. When a connection is accepted by the {@link BluetoothServerSocket},
  * it will return a new {@link BluetoothSocket} to manage the connection.
- * On the client side, use a single {@link BluetoothSocket} to both intiate
+ * On the client side, use a single {@link BluetoothSocket} to both initiate
  * an outgoing connection and to manage the connection.
  *
  * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
  * supported by the Android APIs. RFCOMM is a connection-oriented, streaming
  * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
  *
- * <p>To create a listenting {@link BluetoothServerSocket} that's ready for
+ * <p>To create a listening {@link BluetoothServerSocket} that's ready for
  * incoming connections, use
  * {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord
  * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call
@@ -70,7 +70,7 @@
      * @param encrypt require the connection to be encrypted
      * @param port    remote port
      * @throws IOException On error, for example Bluetooth not available, or
-     *                     insufficient priveleges
+     *                     insufficient privileges
      */
     /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
             throws IOException {
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index ad03399..719d730 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -35,7 +35,7 @@
  * side, use a {@link BluetoothServerSocket} to create a listening server
  * socket. When a connection is accepted by the {@link BluetoothServerSocket},
  * it will return a new {@link BluetoothSocket} to manage the connection.
- * On the client side, use a single {@link BluetoothSocket} to both intiate
+ * On the client side, use a single {@link BluetoothSocket} to both initiate
  * an outgoing connection and to manage the connection.
  *
  * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
@@ -113,7 +113,7 @@
      * @param port    remote port
      * @param uuid    SDP uuid
      * @throws IOException On error, for example Bluetooth not available, or
-     *                     insufficient priveleges
+     *                     insufficient privileges
      */
     /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
             BluetoothDevice device, int port, ParcelUuid uuid) throws IOException {
@@ -158,7 +158,7 @@
      * @param address remote device that this socket can connect to
      * @param port    remote port
      * @throws IOException On error, for example Bluetooth not available, or
-     *                     insufficient priveleges
+     *                     insufficient privileges
      */
     private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
             int port) throws IOException {
@@ -226,7 +226,7 @@
         }
 
         // all native calls are guaranteed to immediately return after
-        // abortNative(), so this lock should immediatley acquire
+        // abortNative(), so this lock should immediately acquire
         mLock.writeLock().lock();
         try {
             mClosed = true;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index cb6b708..7346561 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -27,8 +27,6 @@
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.os.Environment;
-import android.os.StatFs;
 import android.util.AndroidException;
 import android.util.DisplayMetrics;
 
@@ -1388,11 +1386,18 @@
      * {@link Intent#resolveActivity} finds an activity if a class has not
      * been explicitly specified.
      *
+     * <p><em>Note: if using an implicit Intent (without an explicit ComponentName
+     * specified), be sure to consider whether to set the {@link #MATCH_DEFAULT_ONLY}
+     * only flag.  You need to do so to resolve the activity in the same way
+     * that {@link android.content.Context#startActivity(Intent)} and
+     * {@link android.content.Intent#resolveActivity(PackageManager)
+     * Intent.resolveActivity(PackageManager)} do.</p>
+     * 
      * @param intent An intent containing all of the desired specification
      *               (action, data, type, category, and/or component).
      * @param flags Additional option flags.  The most important is
-     *                    MATCH_DEFAULT_ONLY, to limit the resolution to only
-     *                    those activities that support the CATEGORY_DEFAULT.
+     * {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
+     * those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
      *
      * @return Returns a ResolveInfo containing the final activity intent that
      *         was determined to be the best action.  Returns null if no
@@ -1411,13 +1416,13 @@
      *
      * @param intent The desired intent as per resolveActivity().
      * @param flags Additional option flags.  The most important is
-     *                    MATCH_DEFAULT_ONLY, to limit the resolution to only
-     *                    those activities that support the CATEGORY_DEFAULT.
+     * {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
+     * those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
      *
-     * @return A List<ResolveInfo> containing one entry for each matching
+     * @return A List&lt;ResolveInfo&gt; containing one entry for each matching
      *         Activity. These are ordered from best to worst match -- that
      *         is, the first item in the list is what is returned by
-     *         resolveActivity().  If there are no matching activities, an empty
+     *         {@link #resolveActivity}.  If there are no matching activities, an empty
      *         list is returned.
      *
      * @see #MATCH_DEFAULT_ONLY
@@ -1442,10 +1447,10 @@
      *                  first specific results.  Can be null.
      * @param intent The desired intent as per resolveActivity().
      * @param flags Additional option flags.  The most important is
-     *                    MATCH_DEFAULT_ONLY, to limit the resolution to only
-     *                    those activities that support the CATEGORY_DEFAULT.
+     * {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
+     * those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
      *
-     * @return A List<ResolveInfo> containing one entry for each matching
+     * @return A List&lt;ResolveInfo&gt; containing one entry for each matching
      *         Activity. These are ordered first by all of the intents resolved
      *         in <var>specifics</var> and then any additional activities that
      *         can handle <var>intent</var> but did not get included by one of
@@ -1463,11 +1468,9 @@
      * Retrieve all receivers that can handle a broadcast of the given intent.
      *
      * @param intent The desired intent as per resolveActivity().
-     * @param flags Additional option flags.  The most important is
-     *                    MATCH_DEFAULT_ONLY, to limit the resolution to only
-     *                    those activities that support the CATEGORY_DEFAULT.
+     * @param flags Additional option flags.
      *
-     * @return A List<ResolveInfo> containing one entry for each matching
+     * @return A List&lt;ResolveInfo&gt; containing one entry for each matching
      *         Receiver. These are ordered from first to last in priority.  If
      *         there are no matching receivers, an empty list is returned.
      *
@@ -1500,7 +1503,7 @@
      * @param intent The desired intent as per resolveService().
      * @param flags Additional option flags.
      *
-     * @return A List<ResolveInfo> containing one entry for each matching
+     * @return A List&lt;ResolveInfo&gt; containing one entry for each matching
      *         ServiceInfo. These are ordered from best to worst match -- that
      *         is, the first item in the list is what is returned by
      *         resolveService().  If there are no matching services, an empty
@@ -1537,7 +1540,7 @@
      *        uid owning the requested content providers.
      * @param flags Additional option flags.  Currently should always be 0.
      *
-     * @return A List<ContentProviderInfo> containing one entry for each
+     * @return A List&lt;ContentProviderInfo&gt; containing one entry for each
      *         content provider either patching <var>processName</var> or, if
      *         <var>processName</var> is null, all known content providers.
      *         <em>If there are no matching providers, null is returned.</em>
@@ -1573,7 +1576,7 @@
      *                      returned.
      * @param flags Additional option flags.  Currently should always be 0.
      *
-     * @return A List<InstrumentationInfo> containing one entry for each
+     * @return A List&lt;InstrumentationInfo&gt; containing one entry for each
      *         matching available Instrumentation.  Returns an empty list if
      *         there is no instrumentation available for the given package.
      */
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8ef639b..eaf1e33 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1730,6 +1730,11 @@
 
                 XmlUtils.skipCurrentTag(parser);
 
+            } else if (tagName.equals("uses-package")) {
+                // Dependencies for app installers; we don't currently try to
+                // enforce this.
+                XmlUtils.skipCurrentTag(parser);
+
             } else {
                 if (!RIGID_PARSER) {
                     Log.w(TAG, "Unknown element under <application>: " + tagName
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index d0ba590..406b091 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -18,6 +18,7 @@
 
 import android.content.pm.ApplicationInfo;
 import android.graphics.Canvas;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.util.DisplayMetrics;
@@ -363,6 +364,17 @@
         }
 
         /**
+         * Translate a Point in screen coordinates into the app window's coordinates.
+         */
+        public void translatePointInScreenToAppWindow(PointF point) {
+            final float scale = applicationInvertedScale;
+            if (scale != 1.0f) {
+                point.x *= scale;
+                point.y *= scale;
+            }
+        }
+
+        /**
          * Translate the location of the sub window.
          * @param params
          */
diff --git a/core/java/android/content/res/ObbInfo.java b/core/java/android/content/res/ObbInfo.java
index 7b962e5..5d6ed44 100644
--- a/core/java/android/content/res/ObbInfo.java
+++ b/core/java/android/content/res/ObbInfo.java
@@ -29,6 +29,11 @@
     public static final int OBB_OVERLAY = 1 << 0;
 
     /**
+     * The canonical filename of the OBB.
+     */
+    public String filename;
+
+    /**
      * The name of the package to which the OBB file belongs.
      */
     public String packageName;
@@ -66,6 +71,7 @@
     }
 
     public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeString(filename);
         dest.writeString(packageName);
         dest.writeInt(version);
         dest.writeInt(flags);
@@ -83,6 +89,7 @@
     };
 
     private ObbInfo(Parcel source) {
+        filename = source.readString();
         packageName = source.readString();
         version = source.readInt();
         flags = source.readInt();
diff --git a/core/java/android/content/res/ObbScanner.java b/core/java/android/content/res/ObbScanner.java
index 572b75e..1b38eea 100644
--- a/core/java/android/content/res/ObbScanner.java
+++ b/core/java/android/content/res/ObbScanner.java
@@ -45,9 +45,14 @@
             throw new IllegalArgumentException("OBB file does not exist: " + filePath);
         }
 
+        /*
+         * XXX This will fail to find the real canonical path if bind mounts are
+         * used, but we don't use any bind mounts right now.
+         */
         final String canonicalFilePath = obbFile.getCanonicalPath();
 
         ObbInfo obbInfo = new ObbInfo();
+        obbInfo.filename = canonicalFilePath;
         getObbInfo_native(canonicalFilePath, obbInfo);
 
         return obbInfo;
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index 6ed1a90..a7ad757 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -16,6 +16,7 @@
 
 package android.database.sqlite;
 
+import android.os.StrictMode;
 import android.util.Log;
 
 /**
@@ -106,11 +107,13 @@
             // but if the database itself is not closed and is GC'ed, then
             // all sub-objects attached to the database could end up getting GC'ed too.
             // in that case, don't print any warning.
-            if (mInUse) {
+            if (mInUse && StrictMode.vmSqliteObjectLeaksEnabled()) {
                 int len = mSqlStmt.length();
-                Log.w(TAG, "Releasing statement in a finalizer. Please ensure " +
-                        "that you explicitly call close() on your cursor: " +
-                        mSqlStmt.substring(0, (len > 100) ? 100 : len), mStackTrace);
+                StrictMode.onSqliteObjectLeaked(
+                    "Releasing statement in a finalizer. Please ensure " +
+                    "that you explicitly call close() on your cursor: " +
+                    mSqlStmt.substring(0, (len > 100) ? 100 : len),
+                    mStackTrace);
             }
             releaseSqlStatement();
         } finally {
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index fa7763d..89e8ab7 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -25,6 +25,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
+import android.os.StrictMode;
 import android.util.Config;
 import android.util.Log;
 
@@ -505,11 +506,14 @@
         try {
             // if the cursor hasn't been closed yet, close it first
             if (mWindow != null) {
-                int len = mQuery.mSql.length();
-                Log.e(TAG, "Finalizing a Cursor that has not been deactivated or closed. " +
+                if (StrictMode.vmSqliteObjectLeaksEnabled()) {
+                    int len = mQuery.mSql.length();
+                    StrictMode.onSqliteObjectLeaked(
+                        "Finalizing a Cursor that has not been deactivated or closed. " +
                         "database = " + mQuery.mDatabase.getPath() + ", table = " + mEditTable +
                         ", query = " + mQuery.mSql.substring(0, (len > 100) ? 100 : len),
                         mStackTrace);
+                }
                 close();
                 SQLiteDebug.notifyActiveCursorFinalized();
             } else {
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 3df8ec0..ad7289d 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -45,7 +45,7 @@
 public class MobileDataStateTracker implements NetworkStateTracker {
 
     private static final String TAG = "MobileDataStateTracker";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
 
     private Phone.DataState mMobileDataState;
     private ITelephony mPhoneService;
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 3ddaad9..9494a06 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -30,8 +30,9 @@
 import java.util.HashMap;
 
 /**
- * <p>StrictMode is a developer tool which lets you impose stricter
- * rules under which your application runs.
+ * <p>StrictMode is a developer tool which detects things you might be
+ * doing by accident and brings them to your attention so you can fix
+ * them.
  *
  * <p>StrictMode is most commonly used to catch accidental disk or
  * network access on the application's main thread, where UI
@@ -55,24 +56,33 @@
  * <pre>
  * public void onCreate() {
  *     if (DEVELOPER_MODE) {
- *         StrictMode.setThreadPolicy(StrictMode.DISALLOW_DISK_WRITE |
- *                 StrictMode.DISALLOW_DISK_READ |
- *                 StrictMode.DISALLOW_NETWORK |
- *                 StrictMode.PENALTY_LOG);
+ *         StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
+ *                 .detectDiskReads()
+ *                 .detectDiskWrites()
+ *                 .detectNetwork()   // or .detectAll() for all detectable problems
+ *                 .penaltyLog()
+ *                 .build());
+ *         StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
+ *                 .detectLeakedSqlLiteCursors()
+ *                 .penaltyLog()
+ *                 .penaltyDeath()
+ *                 .build());
  *     }
  *     super.onCreate();
  * }
  * </pre>
  *
- * <p>Then you can watch the output of <code>adb logcat</code> while you
- * use your application.
+ * <p>You can decide what should happen when a violation is detected.
+ * For example, using {@link ThreadPolicy.Builder#penaltyLog} you can
+ * watch the output of <code>adb logcat</code> while you use your
+ * application to see the violations as they happen.
  *
  * <p>If you find violations that you feel are problematic, there are
  * a variety of tools to help solve them: threads, {@link android.os.Handler},
  * {@link android.os.AsyncTask}, {@link android.app.IntentService}, etc.
  * But don't feel compelled to fix everything that StrictMode finds.  In particular,
- * a lot of disk accesses are often necessary during the normal activity lifecycle.  Use
- * StrictMode to find things you did on accident.  Network requests on the UI thread
+ * many cases of disk access are often necessary during the normal activity lifecycle.  Use
+ * StrictMode to find things you did by accident.  Network requests on the UI thread
  * are almost always a problem, though.
  *
  * <p class="note">StrictMode is not a security mechanism and is not
@@ -94,55 +104,50 @@
     // Only show an annoying dialog at most every 30 seconds
     private static final long MIN_DIALOG_INTERVAL_MS = 30000;
 
-    private StrictMode() {}
+    // Thread-policy:
 
     /**
-     * Flag for {@link #setThreadPolicy} to signal that you don't intend for this
-     * thread to write to disk.
+     * @hide
      */
-    public static final int DISALLOW_DISK_WRITE = 0x01;
+    public static final int DETECT_DISK_WRITE = 0x01;  // for ThreadPolicy
 
     /**
-     * Flag for {@link #setThreadPolicy} to signal that you don't intend for this
-     * thread to read from disk.
+      * @hide
      */
-    public static final int DISALLOW_DISK_READ = 0x02;
+    public static final int DETECT_DISK_READ = 0x02;  // for ThreadPolicy
 
     /**
-     * Flag for {@link #setThreadPolicy} to signal that you don't intend for this
-     * thread to access the network.
+     * @hide
      */
-    public static final int DISALLOW_NETWORK = 0x04;
+    public static final int DETECT_NETWORK = 0x04;  // for ThreadPolicy
 
-    /** @hide */
-    public static final int DISALLOW_MASK =
-            DISALLOW_DISK_WRITE | DISALLOW_DISK_READ | DISALLOW_NETWORK;
+    // Process-policy:
 
     /**
-     * Penalty flag for {@link #setThreadPolicy} to log violations to
-     * the system log, visible with <code>adb logcat</code>.
+     * Note, a "VM_" bit, not thread.
+     * @hide
+     */
+    public static final int DETECT_VM_CURSOR_LEAKS = 0x200;  // for ProcessPolicy
+
+    /**
+     * @hide
      */
     public static final int PENALTY_LOG = 0x10;  // normal android.util.Log
 
+    // Used for both process and thread policy:
+
     /**
-     * Penalty flag for {@link #setThreadPolicy} to show an annoying
-     * dialog to the developer, rate-limited to be only a little
-     * annoying.
+     * @hide
      */
     public static final int PENALTY_DIALOG = 0x20;
 
     /**
-     * Penalty flag for {@link #setThreadPolicy} to crash hard if
-     * policy is violated.
+     * @hide
      */
     public static final int PENALTY_DEATH = 0x40;
 
     /**
-     * Penalty flag for {@link #setThreadPolicy} to log a stacktrace
-     * and timing data to the
-     * {@link android.os.DropBoxManager DropBox} on policy violation.
-     * Intended mostly for platform integrators doing beta user field
-     * data collection.
+     * @hide
      */
     public static final int PENALTY_DROPBOX = 0x80;
 
@@ -159,10 +164,321 @@
      */
     public static final int PENALTY_GATHER = 0x100;
 
-    /** @hide */
-    public static final int PENALTY_MASK =
-            PENALTY_LOG | PENALTY_DIALOG |
-            PENALTY_DROPBOX | PENALTY_DEATH;
+    /**
+     * The current VmPolicy in effect.
+     */
+    private static volatile int sVmPolicyMask = 0;
+
+    private StrictMode() {}
+
+    /**
+     * {@link StrictMode} policy applied to a certain thread.
+     *
+     * <p>The policy is enabled by {@link #setThreadPolicy}.  The current policy
+     * can be retrieved with {@link #getThreadPolicy}.
+     *
+     * <p>Note that multiple penalties may be provided and they're run
+     * in order from least to most severe (logging before process
+     * death, for example).  There's currently no mechanism to choose
+     * different penalties for different detected actions.
+     */
+    public static final class ThreadPolicy {
+        /**
+         * The default, lax policy which doesn't catch anything.
+         */
+        public static final ThreadPolicy LAX = new ThreadPolicy(0);
+
+        final int mask;
+
+        private ThreadPolicy(int mask) {
+            this.mask = mask;
+        }
+
+        @Override
+        public String toString() {
+            return "[StrictMode.ThreadPolicy; mask=" + mask + "]";
+        }
+
+        /**
+         * Creates ThreadPolicy instances.  Methods whose names start
+         * with {@code detect} specify what problems we should look
+         * for.  Methods whose names start with {@code penalty} specify what
+         * we should do when we detect a problem.
+         *
+         * <p>You can call as many {@code detect} and {@code penalty}
+         * methods as you like. Currently order is insignificant: all
+         * penalties apply to all detected problems.
+         *
+         * <p>For example, detect everything and log anything that's found:
+         * <pre>
+         * StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
+         *     .detectAll()
+         *     .penaltyLog()
+         *     .build();
+         * StrictMode.setVmPolicy(policy);
+         * </pre>
+         */
+        public static final class Builder {
+            private int mMask = 0;
+
+            /**
+             * Create a Builder that detects nothing and has no
+             * violations.  (but note that {@link #build} will default
+             * to enabling {@link #penaltyLog} if no other penalties
+             * are specified)
+             */
+            public Builder() {
+                mMask = 0;
+            }
+
+            /**
+             * Initialize a Builder from an existing ThreadPolicy.
+             */
+            public Builder(ThreadPolicy policy) {
+                mMask = policy.mask;
+            }
+
+            /**
+             * Detect everything that's potentially suspect.
+             *
+             * <p>As of the Gingerbread release this includes network and
+             * disk operations but will likely expand in future releases.
+             */
+            public Builder detectAll() {
+                return enable(DETECT_DISK_WRITE | DETECT_DISK_READ | DETECT_NETWORK);
+            }
+
+            /**
+             * Disable the detection of everything.
+             */
+            public Builder permitAll() {
+                return disable(DETECT_DISK_WRITE | DETECT_DISK_READ | DETECT_NETWORK);
+            }
+
+            /**
+             * Enable detection of network operations.
+             */
+            public Builder detectNetwork() {
+                return enable(DETECT_NETWORK);
+            }
+
+            /**
+             * Disable detection of network operations.
+             */
+            public Builder permitNetwork() {
+                return disable(DETECT_NETWORK);
+            }
+
+            /**
+             * Enable detection of disk reads.
+             */
+            public Builder detectDiskReads() {
+                return enable(DETECT_DISK_READ);
+            }
+
+            /**
+             * Disable detection of disk reads.
+             */
+            public Builder permitDiskReads() {
+                return disable(DETECT_DISK_READ);
+            }
+
+            /**
+             * Enable detection of disk writes.
+             */
+            public Builder detectDiskWrites() {
+                return enable(DETECT_DISK_WRITE);
+            }
+
+            /**
+             * Disable detection of disk writes.
+             */
+            public Builder permitDiskWrites() {
+                return disable(DETECT_DISK_WRITE);
+            }
+
+            /**
+             * Show an annoying dialog to the developer on detected
+             * violations, rate-limited to be only a little annoying.
+             */
+            public Builder penaltyDialog() {
+                return enable(PENALTY_DIALOG);
+            }
+
+            /**
+             * Crash the whole process on violation.  This penalty runs at
+             * the end of all enabled penalties so you'll still get
+             * see logging or other violations before the process dies.
+             */
+            public Builder penaltyDeath() {
+                return enable(PENALTY_DEATH);
+            }
+
+            /**
+             * Log detected violations to the system log.
+             */
+            public Builder penaltyLog() {
+                return enable(PENALTY_LOG);
+            }
+
+            /**
+             * Enable detected violations log a stacktrace and timing data
+             * to the {@link android.os.DropBoxManager DropBox} on policy
+             * violation.  Intended mostly for platform integrators doing
+             * beta user field data collection.
+             */
+            public Builder penaltyDropBox() {
+                return enable(PENALTY_DROPBOX);
+            }
+
+            private Builder enable(int bit) {
+                mMask |= bit;
+                return this;
+            }
+
+            private Builder disable(int bit) {
+                mMask &= ~bit;
+                return this;
+            }
+
+            /**
+             * Construct the ThreadPolicy instance.
+             *
+             * <p>Note: if no penalties are enabled before calling
+             * <code>build</code>, {@link #penaltyLog} is implicitly
+             * set.
+             */
+            public ThreadPolicy build() {
+                // If there are detection bits set but no violation bits
+                // set, enable simple logging.
+                if (mMask != 0 &&
+                    (mMask & (PENALTY_DEATH | PENALTY_LOG |
+                              PENALTY_DROPBOX | PENALTY_DIALOG)) == 0) {
+                    penaltyLog();
+                }
+                return new ThreadPolicy(mMask);
+            }
+        }
+    }
+
+    /**
+     * {@link StrictMode} policy applied to all threads in the virtual machine's process.
+     *
+     * <p>The policy is enabled by {@link #setVmPolicy}.
+     */
+    public static final class VmPolicy {
+        /**
+         * The default, lax policy which doesn't catch anything.
+         */
+        public static final VmPolicy LAX = new VmPolicy(0);
+
+        final int mask;
+
+        private VmPolicy(int mask) {
+            this.mask = mask;
+        }
+
+        @Override
+        public String toString() {
+            return "[StrictMode.VmPolicy; mask=" + mask + "]";
+        }
+
+        /**
+         * Creates {@link VmPolicy} instances.  Methods whose names start
+         * with {@code detect} specify what problems we should look
+         * for.  Methods whose names start with {@code penalty} specify what
+         * we should do when we detect a problem.
+         *
+         * <p>You can call as many {@code detect} and {@code penalty}
+         * methods as you like. Currently order is insignificant: all
+         * penalties apply to all detected problems.
+         *
+         * <p>For example, detect everything and log anything that's found:
+         * <pre>
+         * StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
+         *     .detectAll()
+         *     .penaltyLog()
+         *     .build();
+         * StrictMode.setVmPolicy(policy);
+         * </pre>
+         */
+        public static final class Builder {
+            private int mMask;
+
+            /**
+             * Detect everything that's potentially suspect.
+             *
+             * <p>As of the Gingerbread release this only includes
+             * SQLite cursor leaks but will likely expand in future
+             * releases.
+             */
+            public Builder detectAll() {
+                return enable(DETECT_VM_CURSOR_LEAKS);
+            }
+
+            /**
+             * Detect when an
+             * {@link android.database.sqlite.SQLiteCursor} or other
+             * SQLite object is finalized without having been closed.
+             *
+             * <p>You always want to explicitly close your SQLite
+             * cursors to avoid unnecessary database contention and
+             * temporary memory leaks.
+             */
+            public Builder detectLeakedSqlLiteObjects() {
+                return enable(DETECT_VM_CURSOR_LEAKS);
+            }
+
+            /**
+             * Crashes the whole process on violation.  This penalty runs at
+             * the end of all enabled penalties so yo you'll still get
+             * your logging or other violations before the process dies.
+             */
+            public Builder penaltyDeath() {
+                return enable(PENALTY_DEATH);
+            }
+
+            /**
+             * Log detected violations to the system log.
+             */
+            public Builder penaltyLog() {
+                return enable(PENALTY_LOG);
+            }
+
+            /**
+             * Enable detected violations log a stacktrace and timing data
+             * to the {@link android.os.DropBoxManager DropBox} on policy
+             * violation.  Intended mostly for platform integrators doing
+             * beta user field data collection.
+             */
+            public Builder penaltyDropBox() {
+                return enable(PENALTY_DROPBOX);
+            }
+
+            private Builder enable(int bit) {
+                mMask |= bit;
+                return this;
+            }
+
+            /**
+             * Construct the VmPolicy instance.
+             *
+             * <p>Note: if no penalties are enabled before calling
+             * <code>build</code>, {@link #penaltyLog} is implicitly
+             * set.
+             */
+            public VmPolicy build() {
+                // If there are detection bits set but no violation bits
+                // set, enable simple logging.
+                if (mMask != 0 &&
+                    (mMask & (PENALTY_DEATH | PENALTY_LOG |
+                              PENALTY_DROPBOX | PENALTY_DIALOG)) == 0) {
+                    penaltyLog();
+                }
+                return new VmPolicy(mMask);
+            }
+        }
+    }
 
     /**
      * Log of strict mode violation stack traces that have occurred
@@ -181,19 +497,21 @@
     };
 
     /**
-     * Sets the policy for what actions the current thread isn't
-     * expected to do, as well as the penalty if it does.
+     * Sets the policy for what actions on the current thread should
+     * be detected, as well as the penalty if such actions occur.
      *
-     * <p>Internally this sets a thread-local integer which is
+     * <p>Internally this sets a thread-local variable which is
      * propagated across cross-process IPC calls, meaning you can
      * catch violations when a system service or another process
      * accesses the disk or network on your behalf.
      *
-     * @param policyMask a bitmask of DISALLOW_* and PENALTY_* values,
-     *     e.g. {@link #DISALLOW_DISK_READ}, {@link #DISALLOW_DISK_WRITE},
-     *     {@link #DISALLOW_NETWORK}, {@link #PENALTY_LOG}.
+     * @param policy the policy to put into place
      */
-    public static void setThreadPolicy(final int policyMask) {
+    public static void setThreadPolicy(final ThreadPolicy policy) {
+        setThreadPolicyMask(policy.mask);
+    }
+
+    private static void setThreadPolicyMask(final int policyMask) {
         // In addition to the Java-level thread-local in Dalvik's
         // BlockGuard, we also need to keep a native thread-local in
         // Binder in order to propagate the value across Binder calls,
@@ -222,65 +540,76 @@
 
     private static class StrictModeNetworkViolation extends BlockGuard.BlockGuardPolicyException {
         public StrictModeNetworkViolation(int policyMask) {
-            super(policyMask, DISALLOW_NETWORK);
+            super(policyMask, DETECT_NETWORK);
         }
     }
 
     private static class StrictModeDiskReadViolation extends BlockGuard.BlockGuardPolicyException {
         public StrictModeDiskReadViolation(int policyMask) {
-            super(policyMask, DISALLOW_DISK_READ);
+            super(policyMask, DETECT_DISK_READ);
         }
     }
 
     private static class StrictModeDiskWriteViolation extends BlockGuard.BlockGuardPolicyException {
         public StrictModeDiskWriteViolation(int policyMask) {
-            super(policyMask, DISALLOW_DISK_WRITE);
+            super(policyMask, DETECT_DISK_WRITE);
         }
     }
 
     /**
      * Returns the bitmask of the current thread's policy.
      *
-     * @return the bitmask of all the DISALLOW_* and PENALTY_* bits currently enabled
+     * @return the bitmask of all the DETECT_* and PENALTY_* bits currently enabled
+     *
+     * @hide
      */
-    public static int getThreadPolicy() {
+    public static int getThreadPolicyMask() {
         return BlockGuard.getThreadPolicy().getPolicyMask();
     }
 
     /**
-     * A convenience wrapper around {@link #getThreadPolicy} and
-     * {@link #setThreadPolicy}.  Updates the current thread's policy
-     * mask to allow both reading &amp; writing to disk, returning the
-     * old policy so you can restore it at the end of a block.
-     *
-     * @return the old policy mask, to be passed to setThreadPolicy to
-     *         restore the policy.
+     * Returns the current thread's policy.
      */
-    public static int allowThreadDiskWrites() {
-        int oldPolicy = getThreadPolicy();
-        int newPolicy = oldPolicy & ~(DISALLOW_DISK_WRITE | DISALLOW_DISK_READ);
-        if (newPolicy != oldPolicy) {
-            setThreadPolicy(newPolicy);
-        }
-        return oldPolicy;
+    public static ThreadPolicy getThreadPolicy() {
+        return new ThreadPolicy(getThreadPolicyMask());
     }
 
     /**
-     * A convenience wrapper around {@link #getThreadPolicy} and
-     * {@link #setThreadPolicy}.  Updates the current thread's policy
-     * mask to allow reading from disk, returning the old
-     * policy so you can restore it at the end of a block.
+     * A convenience wrapper that takes the current
+     * {@link ThreadPolicy} from {@link #getThreadPolicy}, modifies it
+     * to permit both disk reads &amp; writes, and sets the new policy
+     * with {@link #setThreadPolicy}, returning the old policy so you
+     * can restore it at the end of a block.
      *
-     * @return the old policy mask, to be passed to setThreadPolicy to
+     * @return the old policy, to be passed to {@link #setThreadPolicy} to
+     *         restore the policy at the end of a block
+     */
+    public static ThreadPolicy allowThreadDiskWrites() {
+        int oldPolicyMask = getThreadPolicyMask();
+        int newPolicyMask = oldPolicyMask & ~(DETECT_DISK_WRITE | DETECT_DISK_READ);
+        if (newPolicyMask != oldPolicyMask) {
+            setThreadPolicyMask(newPolicyMask);
+        }
+        return new ThreadPolicy(oldPolicyMask);
+    }
+
+    /**
+     * A convenience wrapper that takes the current
+     * {@link ThreadPolicy} from {@link #getThreadPolicy}, modifies it
+     * to permit disk reads, and sets the new policy
+     * with {@link #setThreadPolicy}, returning the old policy so you
+     * can restore it at the end of a block.
+     *
+     * @return the old policy, to be passed to setThreadPolicy to
      *         restore the policy.
      */
-    public static int allowThreadDiskReads() {
-        int oldPolicy = getThreadPolicy();
-        int newPolicy = oldPolicy & ~(DISALLOW_DISK_READ);
-        if (newPolicy != oldPolicy) {
-            setThreadPolicy(newPolicy);
+    public static ThreadPolicy allowThreadDiskReads() {
+        int oldPolicyMask = getThreadPolicyMask();
+        int newPolicyMask = oldPolicyMask & ~(DETECT_DISK_READ);
+        if (newPolicyMask != oldPolicyMask) {
+            setThreadPolicyMask(newPolicyMask);
         }
-        return oldPolicy;
+        return new ThreadPolicy(oldPolicyMask);
     }
 
     /**
@@ -294,11 +623,14 @@
         if ("user".equals(Build.TYPE)) {
             return false;
         }
-        StrictMode.setThreadPolicy(
-            StrictMode.DISALLOW_DISK_WRITE |
-            StrictMode.DISALLOW_DISK_READ |
-            StrictMode.DISALLOW_NETWORK |
+        StrictMode.setThreadPolicyMask(
+            StrictMode.DETECT_DISK_WRITE |
+            StrictMode.DETECT_DISK_READ |
+            StrictMode.DETECT_NETWORK |
             StrictMode.PENALTY_DROPBOX);
+        sVmPolicyMask = StrictMode.DETECT_VM_CURSOR_LEAKS |
+                StrictMode.PENALTY_DROPBOX |
+                StrictMode.PENALTY_LOG;
         return true;
     }
 
@@ -372,7 +704,7 @@
 
         // Part of BlockGuard.Policy interface:
         public void onWriteToDisk() {
-            if ((mPolicyMask & DISALLOW_DISK_WRITE) == 0) {
+            if ((mPolicyMask & DETECT_DISK_WRITE) == 0) {
                 return;
             }
             BlockGuard.BlockGuardPolicyException e = new StrictModeDiskWriteViolation(mPolicyMask);
@@ -382,7 +714,7 @@
 
         // Part of BlockGuard.Policy interface:
         public void onReadFromDisk() {
-            if ((mPolicyMask & DISALLOW_DISK_READ) == 0) {
+            if ((mPolicyMask & DETECT_DISK_READ) == 0) {
                 return;
             }
             BlockGuard.BlockGuardPolicyException e = new StrictModeDiskReadViolation(mPolicyMask);
@@ -392,7 +724,7 @@
 
         // Part of BlockGuard.Policy interface:
         public void onNetwork() {
-            if ((mPolicyMask & DISALLOW_NETWORK) == 0) {
+            if ((mPolicyMask & DETECT_NETWORK) == 0) {
                 return;
             }
             BlockGuard.BlockGuardPolicyException e = new StrictModeNetworkViolation(mPolicyMask);
@@ -547,13 +879,13 @@
             if (violationMaskSubset != 0) {
                 int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage);
                 violationMaskSubset |= violationBit;
-                final int savedPolicy = getThreadPolicy();
+                final int savedPolicyMask = getThreadPolicyMask();
                 try {
                     // First, remove any policy before we call into the Activity Manager,
                     // otherwise we'll infinite recurse as we try to log policy violations
                     // to disk, thus violating policy, thus requiring logging, etc...
                     // We restore the current policy below, in the finally block.
-                    setThreadPolicy(0);
+                    setThreadPolicyMask(0);
 
                     ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
                         RuntimeInit.getApplicationObject(),
@@ -563,7 +895,7 @@
                     Log.e(TAG, "RemoteException trying to handle StrictMode violation", e);
                 } finally {
                     // Restore the policy.
-                    setThreadPolicy(savedPolicy);
+                    setThreadPolicyMask(savedPolicyMask);
                 }
             }
 
@@ -592,6 +924,74 @@
     }
 
     /**
+     * Sets the policy for what actions in the VM process (on any
+     * thread) should be detected, as well as the penalty if such
+     * actions occur.
+     *
+     * @param policy the policy to put into place
+     */
+    public static void setVmPolicy(final VmPolicy policy) {
+        sVmPolicyMask = policy.mask;
+    }
+
+    /**
+     * Gets the current VM policy.
+     */
+    public static VmPolicy getVmPolicy() {
+        return new VmPolicy(sVmPolicyMask);
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean vmSqliteObjectLeaksEnabled() {
+        return (sVmPolicyMask & DETECT_VM_CURSOR_LEAKS) != 0;
+    }
+
+    /**
+     * @hide
+     */
+    public static void onSqliteObjectLeaked(String message, Throwable originStack) {
+        if ((sVmPolicyMask & PENALTY_LOG) != 0) {
+            Log.e(TAG, message, originStack);
+        }
+
+        if ((sVmPolicyMask & PENALTY_DROPBOX) != 0) {
+            final ViolationInfo info = new ViolationInfo(originStack, sVmPolicyMask);
+
+            // The violationMask, passed to ActivityManager, is a
+            // subset of the original StrictMode policy bitmask, with
+            // only the bit violated and penalty bits to be executed
+            // by the ActivityManagerService remaining set.
+            int violationMaskSubset = PENALTY_DROPBOX | DETECT_VM_CURSOR_LEAKS;
+            final int savedPolicyMask = getThreadPolicyMask();
+            try {
+                // First, remove any policy before we call into the Activity Manager,
+                // otherwise we'll infinite recurse as we try to log policy violations
+                // to disk, thus violating policy, thus requiring logging, etc...
+                // We restore the current policy below, in the finally block.
+                setThreadPolicyMask(0);
+
+                ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
+                    RuntimeInit.getApplicationObject(),
+                    violationMaskSubset,
+                    info);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException trying to handle StrictMode violation", e);
+            } finally {
+                // Restore the policy.
+                setThreadPolicyMask(savedPolicyMask);
+            }
+        }
+
+        if ((sVmPolicyMask & PENALTY_DEATH) != 0) {
+            System.err.println("StrictMode VmPolicy violation with POLICY_DEATH; shutting down.");
+            Process.killProcess(Process.myPid());
+            System.exit(10);
+        }
+    }
+
+    /**
      * Called from Parcel.writeNoException()
      */
     /* package */ static void writeGatheredViolationsToParcel(Parcel p) {
@@ -621,7 +1021,7 @@
         new LogStackTrace().printStackTrace(new PrintWriter(sw));
         String ourStack = sw.toString();
 
-        int policyMask = getThreadPolicy();
+        int policyMask = getThreadPolicyMask();
         boolean currentlyGathering = (policyMask & PENALTY_GATHER) != 0;
 
         int numViolations = p.readInt();
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4268618..8554ece 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -424,7 +424,6 @@
      * @param key secret used to encrypt the OBB; may be <code>null</code> if no
      *            encryption was used on the OBB.
      * @return whether the mount call was successfully queued or not
-     * @throws IllegalArgumentException when the OBB is already mounted
      */
     public boolean mountObb(String filename, String key, OnObbStateChangeListener listener) {
         try {
@@ -458,10 +457,8 @@
      * @param force whether to kill any programs using this in order to unmount
      *            it
      * @return whether the unmount call was successfully queued or not
-     * @throws IllegalArgumentException when OBB is not already mounted
      */
-    public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener)
-            throws IllegalArgumentException {
+    public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener) {
         try {
             mObbActionListener.addListener(listener);
             mMountService.unmountObb(filename, force, mObbActionListener);
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index d2620d7..4edc01f 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -39,7 +39,7 @@
 /**
  * TODO: Move this to
  * java/services/com/android/server/BluetoothEventLoop.java
- * and make the contructor package private again.
+ * and make the constructor package private again.
  *
  * @hide
  */
@@ -754,7 +754,7 @@
 
     private void onRestartRequired() {
         if (mBluetoothService.isEnabled()) {
-            Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
+            Log.e(TAG, "*** A serious error occurred (did bluetoothd crash?) - " +
                        "restarting Bluetooth ***");
             mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH);
         }
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index bd105a7..f8caa2c 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -500,7 +500,7 @@
                 // forked multiple times in a row, probably because there is
                 // some race in sdptool or bluez when operated in parallel.
                 // As a workaround, delay 500ms between each fork of sdptool.
-                // TODO: Don't fork sdptool in order to regsiter service
+                // TODO: Don't fork sdptool in order to register service
                 // records, use a DBUS call instead.
                 switch (msg.arg1) {
                 case 1:
@@ -713,7 +713,7 @@
     /** local cache of bonding state.
     /* we keep our own state to track the intermediate state BONDING, which
     /* bluez does not track.
-     * All addreses must be passed in upper case.
+     * All addresses must be passed in upper case.
      */
     public class BondState {
         private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
@@ -976,7 +976,7 @@
             }
         }
 
-        // This function adds a bluetooth address to the auto pairing blacklis
+        // This function adds a bluetooth address to the auto pairing blacklist
         // file. These addresses are added to DynamicAddressBlacklistSection
         private void updateAutoPairingData(String address) {
             BufferedWriter out = null;
@@ -1106,7 +1106,7 @@
      * a device discoverable; you need to call setMode() to make the device
      * explicitly discoverable.
      *
-     * @param timeout_s The discoverable timeout in seconds.
+     * @param timeout The discoverable timeout in seconds.
      */
     public synchronized boolean setDiscoverableTimeout(int timeout) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -2565,12 +2565,12 @@
     /*package*/ String getAddressFromObjectPath(String objectPath) {
         String adapterObjectPath = getPropertyInternal("ObjectPath");
         if (adapterObjectPath == null || objectPath == null) {
-            Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
+            Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
                     "  or deviceObjectPath:" + objectPath + " is null");
             return null;
         }
         if (!objectPath.startsWith(adapterObjectPath)) {
-            Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
+            Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
                     "  is not a prefix of deviceObjectPath:" + objectPath +
                     "bluetoothd crashed ?");
             return null;
diff --git a/core/java/android/util/CalendarUtils.java b/core/java/android/util/CalendarUtils.java
index 3d340d9..1b2a894 100644
--- a/core/java/android/util/CalendarUtils.java
+++ b/core/java/android/util/CalendarUtils.java
@@ -146,6 +146,9 @@
          * This formats a date/time range using Calendar's time zone and the
          * local conventions for the region of the device.
          *
+         * If the {@link DateUtils#FORMAT_UTC} flag is used it will pass in
+         * the UTC time zone instead.
+         *
          * @param context the context is required only if the time is shown
          * @param startMillis the start time in UTC milliseconds
          * @param endMillis the end time in UTC milliseconds
@@ -156,10 +159,16 @@
         public String formatDateRange(Context context, long startMillis,
                 long endMillis, int flags) {
             String date;
+            String tz;
+            if ((flags & DateUtils.FORMAT_UTC) != 0) {
+                tz = Time.TIMEZONE_UTC;
+            } else {
+                tz = getTimeZone(context, null);
+            }
             synchronized (mSB) {
                 mSB.setLength(0);
                 date = DateUtils.formatDateRange(context, mF, startMillis, endMillis, flags,
-                        getTimeZone(context, null)).toString();
+                        tz).toString();
             }
             return date;
         }
diff --git a/core/java/android/view/DragEvent.aidl b/core/java/android/view/DragEvent.aidl
new file mode 100644
index 0000000..f08943f
--- /dev/null
+++ b/core/java/android/view/DragEvent.aidl
@@ -0,0 +1,19 @@
+/*
+** 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.view;
+
+parcelable DragEvent;
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
new file mode 100644
index 0000000..47c6d3c
--- /dev/null
+++ b/core/java/android/view/DragEvent.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.view;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** !!! TODO: real docs */
+public class DragEvent implements Parcelable {
+    private static final boolean TRACK_RECYCLED_LOCATION = false;
+
+    int mAction;
+    float mX, mY;
+    ClipDescription mClipDescription;
+    ClipData mClipData;
+
+    private DragEvent mNext;
+    private RuntimeException mRecycledLocation;
+    private boolean mRecycled;
+
+    private static final int MAX_RECYCLED = 10;
+    private static final Object gRecyclerLock = new Object();
+    private static int gRecyclerUsed = 0;
+    private static DragEvent gRecyclerTop = null;
+
+    /**
+     * action constants for DragEvent dispatch
+     */
+    public static final int ACTION_DRAG_STARTED = 1;
+    public static final int ACTION_DRAG_LOCATION = 2;
+    public static final int ACTION_DROP = 3;
+    public static final int ACTION_DRAG_ENDED = 4;
+    public static final int ACTION_DRAG_ENTERED = 5;
+    public static final int ACTION_DRAG_EXITED = 6;
+
+    /* hide the constructor behind package scope */
+    DragEvent() {
+    }
+
+    public static DragEvent obtain() {
+        return DragEvent.obtain(0, 0f, 0f, null, null);
+    }
+
+    public static DragEvent obtain(int action, float x, float y,
+            ClipDescription description, ClipData data) {
+        final DragEvent ev;
+        synchronized (gRecyclerLock) {
+            if (gRecyclerTop == null) {
+                return new DragEvent();
+            }
+            ev = gRecyclerTop;
+            gRecyclerTop = ev.mNext;
+            gRecyclerUsed -= 1;
+        }
+        ev.mRecycledLocation = null;
+        ev.mRecycled = false;
+        ev.mNext = null;
+
+        ev.mAction = action;
+        ev.mX = x;
+        ev.mY = y;
+        ev.mClipDescription = description;
+        ev.mClipData = data;
+
+        return ev;
+    }
+
+    public int getAction() {
+        return mAction;
+    }
+
+    public float getX() {
+        return mX;
+    }
+
+    public float getY() {
+        return mY;
+    }
+
+    public ClipData getClipData() {
+        return mClipData;
+    }
+
+    public ClipDescription getClipDescription() {
+        return mClipDescription;
+    }
+
+    /**
+     * Recycle the DragEvent, to be re-used by a later caller.  After calling
+     * this function you must never touch the event again.
+     */
+    public final void recycle() {
+        // Ensure recycle is only called once!
+        if (TRACK_RECYCLED_LOCATION) {
+            if (mRecycledLocation != null) {
+                throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation);
+            }
+            mRecycledLocation = new RuntimeException("Last recycled here");
+        } else {
+            if (mRecycled) {
+                throw new RuntimeException(toString() + " recycled twice!");
+            }
+            mRecycled = true;
+        }
+
+        mClipData = null;
+        mClipDescription = null;
+
+        synchronized (gRecyclerLock) {
+            if (gRecyclerUsed < MAX_RECYCLED) {
+                gRecyclerUsed++;
+                mNext = gRecyclerTop;
+                gRecyclerTop = this;
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "DragEvent{" + Integer.toHexString(System.identityHashCode(this))
+        + " action=" + mAction + " @ (" + mX + ", " + mY + ") desc=" + mClipDescription
+        + " data=" + mClipData
+        + "}";
+    }
+
+    /* Parcelable interface */
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mAction);
+        dest.writeFloat(mX);
+        dest.writeFloat(mY);
+        if (mClipData == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            mClipData.writeToParcel(dest, flags);
+        }
+        if (mClipDescription == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            mClipDescription.writeToParcel(dest, flags);
+        }
+    }
+
+    public static final Parcelable.Creator<DragEvent> CREATOR =
+        new Parcelable.Creator<DragEvent>() {
+        public DragEvent createFromParcel(Parcel in) {
+            DragEvent event = DragEvent.obtain();
+            event.mAction = in.readInt();
+            event.mX = in.readFloat();
+            event.mY = in.readFloat();
+            if (in.readInt() != 0) {
+                event.mClipData = ClipData.CREATOR.createFromParcel(in);
+            }
+            if (in.readInt() != 0) {
+                event.mClipDescription = ClipDescription.CREATOR.createFromParcel(in);
+            }
+            return event;
+        }
+
+        public DragEvent[] newArray(int size) {
+            return new DragEvent[size];
+        }
+    };
+}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 921018a..6b9cda0 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -17,10 +17,13 @@
 
 package android.view;
 
+import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.view.DragEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
@@ -64,4 +67,9 @@
     
     void dispatchWallpaperCommand(String action, int x, int y,
             int z, in Bundle extras, boolean sync);
+
+    /**
+     * Drag/drop events
+     */
+     void dispatchDragEvent(in DragEvent event);
 }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7f10b76..79ea5b6 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -17,6 +17,7 @@
 
 package android.view;
 
+import android.content.ClipData;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -116,6 +117,30 @@
     boolean performHapticFeedback(IWindow window, int effectId, boolean always);
     
     /**
+     * Allocate the drag's thumbnail surface.  Also assigns a token that identifies
+     * the drag to the OS and passes that as the return value.  A return value of
+     * null indicates failure.
+     */
+    IBinder prepareDrag(IWindow window, boolean localOnly,
+            int thumbnailWidth, int thumbnailHeight, out Surface outSurface);
+
+    /**
+     * Initiate the drag operation itself
+     */
+    boolean performDrag(IWindow window, IBinder dragToken, float touchX, float touchY,
+            float thumbCenterX, float thumbCenterY, in ClipData data);
+
+    /**
+     * Tell the OS that we've just dragged into a View that is willing to accept the drop
+     */
+    void dragRecipientEntered(IWindow window);
+
+    /**
+     * Tell the OS that we've just dragged *off* of a View that was willing to accept the drop
+     */
+    void dragRecipientExited(IWindow window);
+
+    /**
      * For windows with the wallpaper behind them, and the wallpaper is
      * larger than the screen, set the offset within the screen.
      * For multi screen launcher type applications, xstep and ystep indicate
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f2d134b..4c01f4f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.content.ClipData;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -614,6 +615,7 @@
  */
 public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
     private static final boolean DBG = false;
+    static final boolean DEBUG_DRAG = true;
 
     /**
      * The logging tag used by this class with android.util.Log.
@@ -1681,7 +1683,7 @@
      * The transform matrix for the View. This transform is calculated internally
      * based on the rotation, scaleX, and scaleY properties. The identity matrix
      * is used by default. Do *not* use this variable directly; instead call
-     * getMatrix(), which will automatically recalculate the matrix if necessary
+     * getInverseMatrix(), which will automatically recalculate the matrix if necessary
      * to get the correct matrix based on the latest rotation and scale properties.
      */
     private Matrix mInverseMatrix;
@@ -1704,7 +1706,8 @@
      * A variable that tracks whether we need to recalculate the
      * transform matrix, based on whether the rotation or scaleX/Y properties
      * have changed since the matrix was last calculated. This variable
-     * is only valid after a call to getMatrix().
+     * is only valid after a call to updateMatrix() or to a function that
+     * calls it such as getMatrix(), hasIdentityMatrix() and getInverseMatrix().
      */
     private boolean mMatrixIsIdentity = true;
 
@@ -2030,6 +2033,14 @@
     private int mTouchSlop;
 
     /**
+     * Cache drag/drop state
+     *
+     */
+    boolean mCanAcceptDrop;
+    private int mThumbnailWidth;
+    private int mThumbnailHeight;
+
+    /**
      * Simple constructor to use when creating a view from code.
      *
      * @param context The Context the view is running in, through which it can
@@ -5108,7 +5119,7 @@
      * @return The current transform matrix for the view
      */
     public Matrix getMatrix() {
-        hasIdentityMatrix();
+        updateMatrix();
         return mMatrix;
     }
 
@@ -5123,11 +5134,20 @@
     }
 
     /**
-     * Recomputes the transform matrix if necessary.
+     * Returns true if the transform matrix is the identity matrix.
+     * Recomputes the matrix if necessary.
      * 
      * @return True if the transform matrix is the identity matrix, false otherwise.
      */
-    boolean hasIdentityMatrix() {
+    final boolean hasIdentityMatrix() {
+        updateMatrix();
+        return mMatrixIsIdentity;
+    }
+
+    /**
+     * Recomputes the transform matrix if necessary.
+     */
+    private final void updateMatrix() {
         if (mMatrixDirty) {
             // transform-related properties have changed since the last time someone
             // asked for the matrix; recalculate it with the current values
@@ -5166,7 +5186,6 @@
             mMatrixIsIdentity = mMatrix.isIdentity();
             mInverseMatrixDirty = true;
         }
-        return mMatrixIsIdentity;
     }
 
     /**
@@ -5176,7 +5195,8 @@
      *
      * @return The inverse of the current matrix of this view.
      */
-    Matrix getInverseMatrix() {
+    final Matrix getInverseMatrix() {
+        updateMatrix();
         if (mInverseMatrixDirty) {
             if (mInverseMatrix == null) {
                 mInverseMatrix = new Matrix();
@@ -5209,11 +5229,11 @@
     public void setRotation(float rotation) {
         if (mRotation != rotation) {
             // Double-invalidation is necessary to capture view's old and new areas
-            invalidate();
+            invalidate(false);
             mRotation = rotation;
             mMatrixDirty = true;
             mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
-            invalidate();
+            invalidate(false);
         }
     }
 
@@ -5240,11 +5260,11 @@
     public void setRotationY(float rotationY) {
         if (mRotationY != rotationY) {
             // Double-invalidation is necessary to capture view's old and new areas
-            invalidate();
+            invalidate(false);
             mRotationY = rotationY;
             mMatrixDirty = true;
             mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
-            invalidate();
+            invalidate(false);
         }
     }
 
@@ -5271,11 +5291,11 @@
     public void setRotationX(float rotationX) {
         if (mRotationX != rotationX) {
             // Double-invalidation is necessary to capture view's old and new areas
-            invalidate();
+            invalidate(false);
             mRotationX = rotationX;
             mMatrixDirty = true;
             mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
-            invalidate();
+            invalidate(false);
         }
     }
 
@@ -5304,11 +5324,11 @@
     public void setScaleX(float scaleX) {
         if (mScaleX != scaleX) {
             // Double-invalidation is necessary to capture view's old and new areas
-            invalidate();
+            invalidate(false);
             mScaleX = scaleX;
             mMatrixDirty = true;
             mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
-            invalidate();
+            invalidate(false);
         }
     }
 
@@ -5337,11 +5357,11 @@
     public void setScaleY(float scaleY) {
         if (mScaleY != scaleY) {
             // Double-invalidation is necessary to capture view's old and new areas
-            invalidate();
+            invalidate(false);
             mScaleY = scaleY;
             mMatrixDirty = true;
             mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
-            invalidate();
+            invalidate(false);
         }
     }
 
@@ -5376,11 +5396,11 @@
         mPrivateFlags |= PIVOT_EXPLICITLY_SET;
         if (mPivotX != pivotX) {
             // Double-invalidation is necessary to capture view's old and new areas
-            invalidate();
+            invalidate(false);
             mPivotX = pivotX;
             mMatrixDirty = true;
             mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
-            invalidate();
+            invalidate(false);
         }
     }
 
@@ -5414,11 +5434,11 @@
         mPrivateFlags |= PIVOT_EXPLICITLY_SET;
         if (mPivotY != pivotY) {
             // Double-invalidation is necessary to capture view's old and new areas
-            invalidate();
+            invalidate(false);
             mPivotY = pivotY;
             mMatrixDirty = true;
             mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
-            invalidate();
+            invalidate(false);
         }
     }
 
@@ -5441,8 +5461,12 @@
      */
     public void setAlpha(float alpha) {
         mAlpha = alpha;
-        onSetAlpha((int) (alpha * 255));
-        invalidate();
+        if (onSetAlpha((int) (alpha * 255))) {
+            // subclass is handling alpha - don't optimize rendering cache invalidation
+            invalidate();
+        } else {
+            invalidate(false);
+        }
     }
 
     /**
@@ -5464,7 +5488,8 @@
      */
     public final void setTop(int top) {
         if (top != mTop) {
-            if (hasIdentityMatrix()) {
+            updateMatrix();
+            if (mMatrixIsIdentity) {
                 final ViewParent p = mParent;
                 if (p != null && mAttachInfo != null) {
                     final Rect r = mAttachInfo.mTmpInvalRect;
@@ -5485,8 +5510,13 @@
                 invalidate();
             }
 
+            int width = mRight - mLeft;
+            int oldHeight = mBottom - mTop;
+
             mTop = top;
 
+            onSizeChanged(width, mBottom - mTop, width, oldHeight);
+
             if (!mMatrixIsIdentity) {
                 mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
                 invalidate();
@@ -5513,7 +5543,8 @@
      */
     public final void setBottom(int bottom) {
         if (bottom != mBottom) {
-            if (hasIdentityMatrix()) {
+            updateMatrix();
+            if (mMatrixIsIdentity) {
                 final ViewParent p = mParent;
                 if (p != null && mAttachInfo != null) {
                     final Rect r = mAttachInfo.mTmpInvalRect;
@@ -5531,8 +5562,13 @@
                 invalidate();
             }
 
+            int width = mRight - mLeft;
+            int oldHeight = mBottom - mTop;
+
             mBottom = bottom;
 
+            onSizeChanged(width, mBottom - mTop, width, oldHeight);
+
             if (!mMatrixIsIdentity) {
                 mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
                 invalidate();
@@ -5559,8 +5595,8 @@
      */
     public final void setLeft(int left) {
         if (left != mLeft) {
-            System.out.println("view " + this + " left = " + left);
-            if (hasIdentityMatrix()) {
+            updateMatrix();
+            if (mMatrixIsIdentity) {
                 final ViewParent p = mParent;
                 if (p != null && mAttachInfo != null) {
                     final Rect r = mAttachInfo.mTmpInvalRect;
@@ -5581,12 +5617,18 @@
                 invalidate();
             }
 
+            int oldWidth = mRight - mLeft;
+            int height = mBottom - mTop;
+
             mLeft = left;
 
+            onSizeChanged(mRight - mLeft, height, oldWidth, height);
+
             if (!mMatrixIsIdentity) {
                 mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
                 invalidate();
             }
+
         }
     }
 
@@ -5609,7 +5651,8 @@
      */
     public final void setRight(int right) {
         if (right != mRight) {
-            if (hasIdentityMatrix()) {
+            updateMatrix();
+            if (mMatrixIsIdentity) {
                 final ViewParent p = mParent;
                 if (p != null && mAttachInfo != null) {
                     final Rect r = mAttachInfo.mTmpInvalRect;
@@ -5627,8 +5670,13 @@
                 invalidate();
             }
 
+            int oldWidth = mRight - mLeft;
+            int height = mBottom - mTop;
+
             mRight = right;
 
+            onSizeChanged(mRight - mLeft, height, oldWidth, height);
+
             if (!mMatrixIsIdentity) {
                 mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
                 invalidate();
@@ -5703,11 +5751,11 @@
     public void setTranslationX(float translationX) {
         if (mTranslationX != translationX) {
             // Double-invalidation is necessary to capture view's old and new areas
-            invalidate();
+            invalidate(false);
             mTranslationX = translationX;
             mMatrixDirty = true;
             mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
-            invalidate();
+            invalidate(false);
         }
     }
 
@@ -5734,11 +5782,11 @@
     public void setTranslationY(float translationY) {
         if (mTranslationY != translationY) {
             // Double-invalidation is necessary to capture view's old and new areas
-            invalidate();
+            invalidate(false);
             mTranslationY = translationY;
             mMatrixDirty = true;
             mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
-            invalidate();
+            invalidate(false);
         }
     }
 
@@ -5748,13 +5796,13 @@
      * @param outRect The hit rectangle of the view.
      */
     public void getHitRect(Rect outRect) {
-        if (hasIdentityMatrix() || mAttachInfo == null) {
+        updateMatrix();
+        if (mMatrixIsIdentity || mAttachInfo == null) {
             outRect.set(mLeft, mTop, mRight, mBottom);
         } else {
-            Matrix m = getMatrix();
             final RectF tmpRect = mAttachInfo.mTmpTransformRect;
             tmpRect.set(-mPivotX, -mPivotY, getWidth() - mPivotX, getHeight() - mPivotY);
-            m.mapRect(tmpRect);
+            mMatrix.mapRect(tmpRect);
             outRect.set((int) tmpRect.left + mLeft, (int) tmpRect.top + mTop,
                     (int) tmpRect.right + mLeft, (int) tmpRect.bottom + mTop);
         }
@@ -5840,7 +5888,8 @@
      */
     public void offsetTopAndBottom(int offset) {
         if (offset != 0) {
-            if (hasIdentityMatrix()) {
+            updateMatrix();
+            if (mMatrixIsIdentity) {
                 final ViewParent p = mParent;
                 if (p != null && mAttachInfo != null) {
                     final Rect r = mAttachInfo.mTmpInvalRect;
@@ -5860,7 +5909,7 @@
                     p.invalidateChild(this, r);
                 }
             } else {
-                invalidate();
+                invalidate(false);
             }
 
             mTop += offset;
@@ -5868,7 +5917,7 @@
 
             if (!mMatrixIsIdentity) {
                 mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
-                invalidate();
+                invalidate(false);
             }
         }
     }
@@ -5880,7 +5929,8 @@
      */
     public void offsetLeftAndRight(int offset) {
         if (offset != 0) {
-            if (hasIdentityMatrix()) {
+            updateMatrix();
+            if (mMatrixIsIdentity) {
                 final ViewParent p = mParent;
                 if (p != null && mAttachInfo != null) {
                     final Rect r = mAttachInfo.mTmpInvalRect;
@@ -5897,7 +5947,7 @@
                     p.invalidateChild(this, r);
                 }
             } else {
-                invalidate();
+                invalidate(false);
             }
 
             mLeft += offset;
@@ -5905,7 +5955,7 @@
 
             if (!mMatrixIsIdentity) {
                 mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
-                invalidate();
+                invalidate(false);
             }
         }
     }
@@ -6203,12 +6253,30 @@
      * UI thread. To call from a non-UI thread, call {@link #postInvalidate()}.
      */
     public void invalidate() {
+        invalidate(true);
+    }
+
+    /**
+     * This is where the invalidate() work actually happens. A full invalidate()
+     * causes the drawing cache to be invalidated, but this function can be called with
+     * invalidateCache set to false to skip that invalidation step for cases that do not
+     * need it (for example, a component that remains at the same dimensions with the same
+     * content).
+     *
+     * @param invalidateCache Whether the drawing cache for this view should be invalidated as
+     * well. This is usually true for a full invalidate, but may be set to false if the
+     * View's contents or dimensions have not changed.
+     */
+    private void invalidate(boolean invalidateCache) {
         if (ViewDebug.TRACE_HIERARCHY) {
             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
         }
 
         if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) {
-            mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
+            mPrivateFlags &= ~DRAWN;
+            if (invalidateCache) {
+                mPrivateFlags &= ~DRAWING_CACHE_VALID;
+            }
             final ViewParent p = mParent;
             final AttachInfo ai = mAttachInfo;
             if (p != null && ai != null) {
@@ -9762,6 +9830,140 @@
     }
 
     /**
+     * Drag and drop.  App calls startDrag(), then callbacks to onMeasureDragThumbnail()
+     * and onDrawDragThumbnail() happen, then the drag operation is handed over to the
+     * OS.
+     * !!! TODO: real docs
+     * @hide
+     */
+    public final boolean startDrag(ClipData data, float touchX, float touchY,
+            float thumbnailTouchX, float thumbnailTouchY, boolean myWindowOnly) {
+        if (DEBUG_DRAG) {
+            Log.d(VIEW_LOG_TAG, "startDrag: touch=(" + touchX + "," + touchY
+                    + ") thumb=(" + thumbnailTouchX + "," + thumbnailTouchY
+                    + ") data=" + data + " local=" + myWindowOnly);
+        }
+        boolean okay = false;
+
+        measureThumbnail();     // throws if the view fails to specify dimensions
+
+        Surface surface = new Surface();
+        try {
+            IBinder token = mAttachInfo.mSession.prepareDrag(mAttachInfo.mWindow,
+                    myWindowOnly, mThumbnailWidth, mThumbnailHeight, surface);
+            if (DEBUG_DRAG) Log.d(VIEW_LOG_TAG, "prepareDrag returned token=" + token
+                    + " surface=" + surface);
+            if (token != null) {
+                Canvas canvas = surface.lockCanvas(null);
+                try {
+                    onDrawDragThumbnail(canvas);
+                } finally {
+                    surface.unlockCanvasAndPost(canvas);
+                }
+
+                okay = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, token,
+                        touchX, touchY, thumbnailTouchX, thumbnailTouchY, data);
+                if (DEBUG_DRAG) Log.d(VIEW_LOG_TAG, "performDrag returned " + okay);
+            }
+        } catch (Exception e) {
+            Log.e(VIEW_LOG_TAG, "Unable to initiate drag", e);
+            surface.destroy();
+        }
+
+        return okay;
+    }
+
+    private void measureThumbnail() {
+        mPrivateFlags &= ~MEASURED_DIMENSION_SET;
+
+        onMeasureDragThumbnail();
+
+        // flag not set, setDragThumbnailDimension() was not invoked, we raise
+        // an exception to warn the developer
+        if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
+            throw new IllegalStateException("onMeasureDragThumbnail() did not set the"
+                    + " measured dimension by calling setDragThumbnailDimension()");
+        }
+
+        if (DEBUG_DRAG) {
+            Log.d(VIEW_LOG_TAG, "Drag thumb measured: w=" + mThumbnailWidth
+                    + " h=" + mThumbnailHeight);
+        }
+    }
+
+    /**
+     * The View must call this method from onMeasureDragThumbnail() in order to
+     * specify the dimensions of the drag thumbnail image.
+     *
+     * @param width The desired thumbnail width.
+     * @param height The desired thumbnail height.
+     */
+    protected final void setDragThumbnailDimension(int width, int height) {
+        mPrivateFlags |= MEASURED_DIMENSION_SET;
+        mThumbnailWidth = width;
+        mThumbnailHeight = height;
+    }
+
+    /**
+     * The default implementation specifies a drag thumbnail that matches the
+     * View's current size and appearance.
+     */
+    protected void onMeasureDragThumbnail() {
+        setDragThumbnailDimension(getWidth(), getHeight());
+    }
+
+    /**
+     * The default implementation just draws the current View appearance as the thumbnail
+     * @param canvas
+     */
+    protected void onDrawDragThumbnail(Canvas canvas) {
+        draw(canvas);
+    }
+
+    /**
+     * Drag-and-drop event dispatch.  The event.getAction() verb is one of the DragEvent
+     * constants DRAG_STARTED_EVENT, DRAG_EVENT, DROP_EVENT, and DRAG_ENDED_EVENT.
+     *
+     * For DRAG_STARTED_EVENT, event.getClipDescription() describes the content
+     * being dragged.  onDragEvent() should return 'true' if the view can handle
+     * a drop of that content.  A view that returns 'false' here will receive no
+     * further calls to onDragEvent() about the drag/drop operation.
+     *
+     * For DRAG_ENTERED, event.getClipDescription() describes the content being
+     * dragged.  This will be the same content description passed in the
+     * DRAG_STARTED_EVENT invocation.
+     *
+     * For DRAG_EXITED, event.getClipDescription() describes the content being
+     * dragged.  This will be the same content description passed in the
+     * DRAG_STARTED_EVENT invocation.  The view should return to its approriate
+     * drag-acceptance visual state.
+     *
+     * For DRAG_LOCATION_EVENT, event.getX() and event.getY() give the location in View
+     * coordinates of the current drag point.  The view must return 'true' if it
+     * can accept a drop of the current drag content, false otherwise.
+     *
+     * For DROP_EVENT, event.getX() and event.getY() give the location of the drop
+     * within the view; also, event.getClipData() returns the full data payload
+     * being dropped.  The view should return 'true' if it consumed the dropped
+     * content, 'false' if it did not.
+     *
+     * For DRAG_ENDED_EVENT, the 'event' argument may be null.  The view should return
+     * to its normal visual state.
+     */
+    protected boolean onDragEvent(DragEvent event) {
+        return false;
+    }
+
+    /**
+     * Views typically don't need to override dispatchDragEvent(); it just calls
+     * onDragEvent(what, event) and passes the result up appropriately.
+     *
+     */
+    public boolean dispatchDragEvent(DragEvent event) {
+        return onDragEvent(event);
+    }
+
+    /**
      * This needs to be a better API (NOT ON VIEW) before it is exposed.  If
      * it is ever exposed at all.
      * @hide
@@ -9829,30 +10031,6 @@
                 ViewConfiguration.getLongPressTimeout() - delayOffset);
     }
 
-    private static int[] stateSetUnion(final int[] stateSet1, final int[] stateSet2) {
-        final int stateSet1Length = stateSet1.length;
-        final int stateSet2Length = stateSet2.length;
-        final int[] newSet = new int[stateSet1Length + stateSet2Length];
-        int k = 0;
-        int i = 0;
-        int j = 0;
-        // This is a merge of the two input state sets and assumes that the
-        // input sets are sorted by the order imposed by ViewDrawableStates.
-        for (int viewState : R.styleable.ViewDrawableStates) {
-            if (i < stateSet1Length && stateSet1[i] == viewState) {
-                newSet[k++] = viewState;
-                i++;
-            } else if (j < stateSet2Length && stateSet2[j] == viewState) {
-                newSet[k++] = viewState;
-                j++;
-            }
-            if (k > 1) {
-                assert(newSet[k - 1] > newSet[k - 2]);
-            }
-        }
-        return newSet;
-    }
-
     /**
      * Inflate a view from an XML resource.  This convenience method wraps the {@link
      * LayoutInflater} class, which provides a full range of options for view inflation.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1e86f74..8a3db38 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -19,6 +19,8 @@
 import android.animation.LayoutTransition;
 import com.android.internal.R;
 
+import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
@@ -26,6 +28,7 @@
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
@@ -33,6 +36,7 @@
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Animation;
@@ -68,6 +72,7 @@
 public abstract class ViewGroup extends View implements ViewParent, ViewManager {
 
     private static final boolean DBG = false;
+    private static final String TAG = "ViewGroup";
 
     /**
      * Views which have been hidden or removed which need to be animated on
@@ -105,6 +110,15 @@
      */
     private Transformation mInvalidationTransformation;
 
+    // View currently under an ongoing drag
+    private View mCurrentDragView;
+
+    // Does this group have a child that can accept the current drag payload?
+    private boolean mChildAcceptsDrag;
+
+    // Used during drag dispatch
+    private final PointF mLocalPoint = new PointF();
+
     // Layout animation
     private LayoutAnimationController mLayoutAnimationController;
     private Animation.AnimationListener mAnimationListener;
@@ -815,6 +829,136 @@
 
     /**
      * {@inheritDoc}
+     *
+     * !!! TODO: write real docs
+     */
+    @Override
+    public boolean dispatchDragEvent(DragEvent event) {
+        boolean retval = false;
+        final float tx = event.mX;
+        final float ty = event.mY;
+
+        // !!! BUGCHECK: If we have a ViewGroup, we must necessarily have a ViewRoot,
+        // so we don't need to check getRootView() for null here?
+        ViewRoot root = (ViewRoot)(getRootView().getParent());
+
+        // Dispatch down the view hierarchy
+        switch (event.mAction) {
+        case DragEvent.ACTION_DRAG_STARTED: {
+            // clear state to recalculate which views we drag over
+            root.setDragFocus(event, null);
+
+            // Now dispatch down to our children, caching the responses
+            mChildAcceptsDrag = false;
+            final int count = mChildrenCount;
+            final View[] children = mChildren;
+            for (int i = 0; i < count; i++) {
+                final boolean handled = children[i].dispatchDragEvent(event);
+                children[i].mCanAcceptDrop = handled;
+                if (handled) {
+                    mChildAcceptsDrag = true;
+                }
+            }
+
+            // Return HANDLED if one of our children can accept the drag
+            if (mChildAcceptsDrag) {
+                retval = true;
+            }
+        } break;
+
+        case DragEvent.ACTION_DRAG_ENDED: {
+            // Notify all of our children that the drag is over
+            final int count = mChildrenCount;
+            final View[] children = mChildren;
+            for (int i = 0; i < count; i++) {
+                children[i].dispatchDragEvent(event);
+            }
+            // We consider drag-ended to have been handled if one of our children
+            // had offered to handle the drag.
+            if (mChildAcceptsDrag) {
+                retval = true;
+            }
+        } break;
+
+        case DragEvent.ACTION_DRAG_LOCATION: {
+            // Find the [possibly new] drag target
+            final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
+
+            // If we've changed apparent drag target, tell the view root which view
+            // we're over now.  This will in turn send out DRAG_ENTERED / DRAG_EXITED
+            // notifications as appropriate.
+            if (mCurrentDragView != target) {
+                root.setDragFocus(event, target);
+                mCurrentDragView = target;
+            }
+            
+            // Dispatch the actual drag location notice, localized into its coordinates
+            if (target != null) {
+                event.mX = mLocalPoint.x;
+                event.mY = mLocalPoint.y;
+
+                retval = target.dispatchDragEvent(event);
+
+                event.mX = tx;
+                event.mY = ty;
+            }
+        } break;
+
+        case DragEvent.ACTION_DROP: {
+            if (View.DEBUG_DRAG) Slog.d(TAG, "Drop event: " + event);
+            View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
+            if (target != null) {
+                event.mX = mLocalPoint.x;
+                event.mY = mLocalPoint.y;
+                retval = target.dispatchDragEvent(event);
+                event.mX = tx;
+                event.mY = ty;
+            }
+        } break;
+        }
+
+        // If none of our children could handle the event, try here
+        if (!retval) {
+            retval = onDragEvent(event);
+        }
+        return retval;
+    }
+
+    // Find the frontmost child view that lies under the given point, and calculate
+    // the position within its own local coordinate system.
+    View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
+        final float scrolledX = x + mScrollX;
+        final float scrolledY = y + mScrollY;
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = count - 1; i >= 0; i--) {
+            final View child = children[i];
+            if (child.mCanAcceptDrop == false) {
+                continue;
+            }
+
+            float localX = scrolledX - child.mLeft;
+            float localY = scrolledY - child.mTop;
+            if (!child.hasIdentityMatrix() && mAttachInfo != null) {
+                // non-identity matrix: transform the point into the view's coordinates
+                final float[] localXY = mAttachInfo.mTmpTransformLocation;
+                localXY[0] = localX;
+                localXY[1] = localY;
+                child.getInverseMatrix().mapPoints(localXY);
+                localX = localXY[0];
+                localY = localXY[1];
+            }
+            if (localX >= 0 && localY >= 0 && localX < (child.mRight - child.mLeft) &&
+                    localY < (child.mBottom - child.mTop)) {
+                outLocalPoint.set(localX, localY);
+                return child;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
      */
     @Override
     public boolean dispatchKeyEventPreIme(KeyEvent event) {
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 77ba6fe..c63f7d6 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -24,6 +24,7 @@
 import android.graphics.Canvas;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.*;
@@ -45,6 +46,8 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.app.ActivityManagerNative;
@@ -198,6 +201,11 @@
 
     final ViewConfiguration mViewConfiguration;
 
+    /* Drag/drop */
+    ClipDescription mDragDescription;
+    View mCurrentDragView;
+    final PointF mDragPoint = new PointF();
+
     /**
      * see {@link #playSoundEffect(int)}
      */
@@ -1670,6 +1678,7 @@
     public final static int FINISH_INPUT_CONNECTION = 1012;
     public final static int CHECK_FOCUS = 1013;
     public final static int CLOSE_SYSTEM_DIALOGS = 1014;
+    public final static int DISPATCH_DRAG_EVENT = 1015;
 
     @Override
     public void handleMessage(Message msg) {
@@ -1845,6 +1854,9 @@
                 mView.onCloseSystemDialogs((String)msg.obj);
             }
         } break;
+        case DISPATCH_DRAG_EVENT: {
+            handleDragEvent((DragEvent)msg.obj);
+        } break;
         }
     }
     
@@ -2434,6 +2446,87 @@
         }
     }
 
+    /* drag/drop */
+    private void handleDragEvent(DragEvent event) {
+        // From the root, only drag start/end/location are dispatched.  entered/exited
+        // are determined and dispatched by the viewgroup hierarchy, who then report
+        // that back here for ultimate reporting back to the framework.
+        if (mView != null && mAdded) {
+            final int what = event.mAction;
+
+            if (what == DragEvent.ACTION_DRAG_EXITED) {
+                // A direct EXITED event means that the window manager knows we've just crossed
+                // a window boundary, so the current drag target within this one must have
+                // just been exited.  Send it the usual notifications and then we're done
+                // for now.
+                setDragFocus(event, null);
+            } else {
+                // Cache the drag description when the operation starts, then fill it in
+                // on subsequent calls as a convenience
+                if (what == DragEvent.ACTION_DRAG_STARTED) {
+                    mDragDescription = event.mClipDescription;
+                } else {
+                    event.mClipDescription = mDragDescription;
+                }
+
+                // For events with a [screen] location, translate into window coordinates
+                if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) {
+                    mDragPoint.set(event.mX, event.mY);
+                    if (mTranslator != null) {
+                        mTranslator.translatePointInScreenToAppWindow(mDragPoint);
+                    }
+
+                    if (mCurScrollY != 0) {
+                        mDragPoint.offset(0, mCurScrollY);
+                    }
+
+                    event.mX = mDragPoint.x;
+                    event.mY = mDragPoint.y;
+                }
+
+                // Remember who the current drag target is pre-dispatch
+                final View prevDragView = mCurrentDragView;
+
+                // Now dispatch the drag/drop event
+                mView.dispatchDragEvent(event);
+
+                // If we changed apparent drag target, tell the OS about it
+                if (prevDragView != mCurrentDragView) {
+                    try {
+                        if (prevDragView != null) {
+                            sWindowSession.dragRecipientExited(mWindow);
+                        }
+                        if (mCurrentDragView != null) {
+                            sWindowSession.dragRecipientEntered(mWindow);
+                        }
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Unable to note drag target change");
+                    }
+                    mCurrentDragView = prevDragView;
+                }
+            }
+        }
+        event.recycle();
+    }
+
+    public void setDragFocus(DragEvent event, View newDragTarget) {
+        final int action = event.mAction;
+        // If we've dragged off of a view, send it the EXITED message
+        if (mCurrentDragView != newDragTarget) {
+            if (mCurrentDragView != null) {
+                event.mAction = DragEvent.ACTION_DRAG_EXITED;
+                mCurrentDragView.dispatchDragEvent(event);
+            }
+        }
+        // If we've dragged over a new view, send it the ENTERED message
+        if (newDragTarget != null) {
+            event.mAction = DragEvent.ACTION_DRAG_ENTERED;
+            newDragTarget.dispatchDragEvent(event);
+        }
+        mCurrentDragView = newDragTarget;
+        event.mAction = action;  // restore the event's original state
+    }
+
     private AudioManager getAudioManager() {
         if (mView == null) {
             throw new IllegalStateException("getAudioManager called when there is no mView");
@@ -2725,7 +2818,12 @@
         msg.obj = reason;
         sendMessage(msg);
     }
-    
+
+    public void dispatchDragEvent(DragEvent event) {
+        Message msg = obtainMessage(DISPATCH_DRAG_EVENT, event);
+        sendMessage(msg);
+    }
+
     /**
      * The window is getting focus so if there is anything focused/selected
      * send an {@link AccessibilityEvent} to announce that.
@@ -2936,6 +3034,14 @@
                 }
             }
         }
+
+        /* Drag/drop */
+        public void dispatchDragEvent(DragEvent event) {
+            final ViewRoot viewRoot = mViewRoot.get();
+            if (viewRoot != null) {
+                viewRoot.dispatchDragEvent(event);
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index dc3b44d..eddd04e 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -347,6 +347,13 @@
         public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;
         
         /**
+         * Window type: the drag-and-drop pseudowindow.  There is only one
+         * drag layer (at most), and it is placed on top of all other windows.
+         * @hide
+         */
+        public static final int TYPE_DRAG               = FIRST_SYSTEM_WINDOW+15;
+
+        /**
          * End of types of system windows.
          */
         public static final int LAST_SYSTEM_WINDOW      = 2999;
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 76701a9..954b3e7 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -73,6 +73,7 @@
     public final static int FLAG_ALT_GR = 0x00000020;
     public final static int FLAG_MENU = 0x00000040;
     public final static int FLAG_LAUNCHER = 0x00000080;
+    public final static int FLAG_VIRTUAL = 0x00000100;
 
     public final static int FLAG_INJECTED = 0x01000000;
 
@@ -261,24 +262,6 @@
         boolean isDisplayedLw();
 
         /**
-         * Returns true if the window is both full screen and opaque.  Must be
-         * called with the window manager lock held.
-         * 
-         * @param width The width of the screen
-         * @param height The height of the screen 
-         * @param shownFrame If true, this is based on the actual shown frame of 
-         *                   the window (taking into account animations); if
-         *                   false, this is based on the currently requested
-         *                   frame, which any current animation will be moving
-         *                   towards.
-         * @param onlyOpaque If true, this will only pass if the window is
-         * also opaque.
-         * @return Returns true if the window is both full screen and opaque
-         */
-        public boolean fillsScreenLw(int width, int height, boolean shownFrame,
-                boolean onlyOpaque);
-
-        /**
          * Returns true if this window has been shown on screen at some time in 
          * the past.  Must be called with the window manager lock held.
          * 
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 7944807..fedb873 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1291,6 +1291,7 @@
     private void clearHelpers() {
         clearTextEntry();
         clearActionModes();
+        dismissFullScreenMode();
     }
 
     /**
@@ -4911,6 +4912,13 @@
         return mFullScreenHolder != null;
     }
 
+    private void dismissFullScreenMode() {
+        if (inFullScreenMode()) {
+            mFullScreenHolder.dismiss();
+            mFullScreenHolder = null;
+        }
+    }
+
     void onPinchToZoomAnimationStart() {
         // cancel the single touch handling
         cancelTouch();
@@ -6878,9 +6886,9 @@
                     View view = (View) msg.obj;
                     int npp = msg.arg1;
 
-                    if (mFullScreenHolder != null) {
+                    if (inFullScreenMode()) {
                         Log.w(LOGTAG, "Should not have another full screen.");
-                        mFullScreenHolder.dismiss();
+                        dismissFullScreenMode();
                     }
                     mFullScreenHolder = new PluginFullScreenHolder(WebView.this, npp);
                     mFullScreenHolder.setContentView(view);
@@ -6891,10 +6899,7 @@
                     break;
                 }
                 case HIDE_FULLSCREEN:
-                    if (inFullScreenMode()) {
-                        mFullScreenHolder.dismiss();
-                        mFullScreenHolder = null;
-                    }
+                    dismissFullScreenMode();
                     break;
 
                 case DOM_FOCUS_CHANGED:
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index b4efb92..9983d54 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 
 import android.animation.ObjectAnimator;
 import android.content.Context;
@@ -40,6 +41,7 @@
  * @attr ref android.R.styleable#AdapterViewAnimator_inAnimation
  * @attr ref android.R.styleable#AdapterViewAnimator_outAnimation
  * @attr ref android.R.styleable#AdapterViewAnimator_animateFirstView
+ * @attr ref android.R.styleable#AdapterViewAnimator_loopViews
  */
 public abstract class AdapterViewAnimator extends AdapterView<Adapter>
         implements RemoteViewsAdapter.RemoteAdapterConnectionCallback {
@@ -69,15 +71,14 @@
     int mNumActiveViews = 1;
 
     /**
-     * Array of the children of the {@link AdapterViewAnimator}. This array
-     * is accessed in a circular fashion
+     * Map of the children of the {@link AdapterViewAnimator}.
      */
-    View[] mActiveViews;
+    private HashMap<Integer, ViewAndIndex> mViewsMap = new HashMap<Integer, ViewAndIndex>();
 
     /**
      * List of views pending removal from the {@link AdapterViewAnimator}
      */
-    ArrayList<View> mPreviousViews;
+    ArrayList<Integer> mPreviousViews;
 
     /**
      * The index, relative to the adapter, of the beginning of the window of views
@@ -124,7 +125,7 @@
      * Specifies if the animator should wrap from 0 to the end and vice versa
      * or have hard boundaries at the beginning and end
      */
-    boolean mShouldLoop = true;
+    boolean mLoopViews = true;
 
     /**
      * The width and height of some child, used as a size reference in-case our
@@ -149,22 +150,25 @@
         super(context, attrs);
 
         TypedArray a = context.obtainStyledAttributes(attrs,
-                com.android.internal.R.styleable.ViewAnimator);
+                com.android.internal.R.styleable.AdapterViewAnimator);
         int resource = a.getResourceId(
-                com.android.internal.R.styleable.ViewAnimator_inAnimation, 0);
+                com.android.internal.R.styleable.AdapterViewAnimator_inAnimation, 0);
         if (resource > 0) {
             setInAnimation(context, resource);
         }
 
-        resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_outAnimation, 0);
+        resource = a.getResourceId(com.android.internal.R.styleable.AdapterViewAnimator_outAnimation, 0);
         if (resource > 0) {
             setOutAnimation(context, resource);
         }
 
         boolean flag = a.getBoolean(
-                com.android.internal.R.styleable.ViewAnimator_animateFirstView, true);
+                com.android.internal.R.styleable.AdapterViewAnimator_animateFirstView, true);
         setAnimateFirstView(flag);
 
+        mLoopViews = a.getBoolean(
+                com.android.internal.R.styleable.AdapterViewAnimator_loopViews, false);
+
         a.recycle();
 
         initViewAnimator();
@@ -175,11 +179,19 @@
      */
     private void initViewAnimator() {
         mMainQueue = new Handler(Looper.myLooper());
-        mActiveViews = new View[mNumActiveViews];
-        mPreviousViews = new ArrayList<View>();
+        mPreviousViews = new ArrayList<Integer>();
         mViewsToBringToFront = new ArrayList<View>();
     }
 
+    private class ViewAndIndex {
+        ViewAndIndex(View v, int i) {
+            view = v;
+            index = i;
+        }
+        View view;
+        int index;
+    }
+
     /**
      * This method is used by subclasses to configure the animator to display the
      * desired number of views, and specify the offset
@@ -193,18 +205,17 @@
      * @param shouldLoop If the animator is show view 0, and setPrevious() is called, do we
      *        we loop back to the end, or do we do nothing
      */
-     void configureViewAnimator(int numVisibleViews, int activeOffset, boolean shouldLoop) {
+     void configureViewAnimator(int numVisibleViews, int activeOffset) {
         if (activeOffset > numVisibleViews - 1) {
             // Throw an exception here.
         }
         mNumActiveViews = numVisibleViews;
         mActiveOffset = activeOffset;
-        mActiveViews = new View[mNumActiveViews];
         mPreviousViews.clear();
+        mViewsMap.clear();
         removeAllViewsInLayout();
         mCurrentWindowStart = 0;
         mCurrentWindowEnd = -1;
-        mShouldLoop = shouldLoop;
     }
 
     /**
@@ -238,9 +249,9 @@
         if (mAdapter != null) {
             mWhichChild = whichChild;
             if (whichChild >= mAdapter.getCount()) {
-                mWhichChild = mShouldLoop ? 0 : mAdapter.getCount() - 1;
+                mWhichChild = mLoopViews ? 0 : mAdapter.getCount() - 1;
             } else if (whichChild < 0) {
-                mWhichChild = mShouldLoop ? mAdapter.getCount() - 1 : 0;
+                mWhichChild = mLoopViews ? mAdapter.getCount() - 1 : 0;
             }
 
             boolean hasFocus = getFocusedChild() != null;
@@ -323,9 +334,10 @@
      * @return View at this index, null if the index is outside the bounds
      */
     View getViewAtRelativeIndex(int relativeIndex) {
-        if (relativeIndex >= 0 && relativeIndex <= mNumActiveViews - 1) {
-            int index = mCurrentWindowStartUnbounded + relativeIndex;
-            return mActiveViews[modulo(index, mNumActiveViews)];
+        if (relativeIndex >= 0 && relativeIndex <= mNumActiveViews - 1 && mAdapter != null) {
+            int adapterCount =  mAdapter.getCount();
+            int i = modulo(mCurrentWindowStartUnbounded + relativeIndex, adapterCount);
+            return mViewsMap.get(i).view;
         }
         return null;
     }
@@ -346,8 +358,8 @@
             // get the fresh child from the adapter
             View updatedChild = mAdapter.getView(i, null, this);
 
-            if (mActiveViews[index] != null) {
-                FrameLayout fl = (FrameLayout) mActiveViews[index];
+            if (mViewsMap.containsKey(index)) {
+                FrameLayout fl = (FrameLayout) mViewsMap.get(index).view;
                 // flush out the old child
                 fl.removeAllViewsInLayout();
                 // add the new child to the frame, if it exists
@@ -373,7 +385,8 @@
         if (mAdapter == null) return;
 
         for (int i = 0; i < mPreviousViews.size(); i++) {
-            View viewToRemove = mPreviousViews.get(i);
+            View viewToRemove = mViewsMap.get(mPreviousViews.get(i)).view;
+            mViewsMap.remove(mPreviousViews.get(i));
             viewToRemove.clearAnimation();
             if (viewToRemove instanceof ViewGroup) {
                 ViewGroup vg = (ViewGroup) viewToRemove;
@@ -386,64 +399,74 @@
             removeViewInLayout(viewToRemove);
         }
         mPreviousViews.clear();
+        int adapterCount = mAdapter.getCount();
         int newWindowStartUnbounded = childIndex - mActiveOffset;
         int newWindowEndUnbounded = newWindowStartUnbounded + mNumActiveViews - 1;
         int newWindowStart = Math.max(0, newWindowStartUnbounded);
-        int newWindowEnd = Math.min(mAdapter.getCount() - 1, newWindowEndUnbounded);
+        int newWindowEnd = Math.min(adapterCount - 1, newWindowEndUnbounded);
 
-        // This section clears out any items that are in our mActiveViews list
+        if (mLoopViews) {
+            newWindowStart = newWindowStartUnbounded;
+            newWindowEnd = newWindowEndUnbounded;
+        }
+        int rangeStart = modulo(newWindowStart, adapterCount);
+        int rangeEnd = modulo(newWindowEnd, adapterCount);
+
+        boolean wrap = false;
+        if (rangeStart > rangeEnd) {
+            wrap = true;
+        }
+
+        // This section clears out any items that are in our active views list
         // but are outside the effective bounds of our window (this is becomes an issue
         // at the extremities of the list, eg. where newWindowStartUnbounded < 0 or
         // newWindowEndUnbounded > mAdapter.getCount() - 1
-        for (int i = newWindowStartUnbounded; i < newWindowEndUnbounded; i++) {
-            if (i < newWindowStart || i > newWindowEnd) {
-                int index = modulo(i, mNumActiveViews);
-                if (mActiveViews[index] != null) {
-                    View previousView = mActiveViews[index];
-                    mPreviousViews.add(previousView);
-                    int previousViewRelativeIndex = modulo(index - mCurrentWindowStart,
-                            mNumActiveViews);
-                    animateViewForTransition(previousViewRelativeIndex, -1, previousView);
-                    mActiveViews[index] = null;
-                }
+        for (Integer index : mViewsMap.keySet()) {
+            boolean remove = false;
+            if (!wrap && (index < rangeStart || index > rangeEnd)) {
+                remove = true;
+            } else if (wrap && (index > rangeEnd && index < rangeStart)) {
+                remove = true;
+            }
+
+            if (remove) {
+                View previousView = mViewsMap.get(index).view;
+                int oldRelativeIndex = mViewsMap.get(index).index;
+
+                mPreviousViews.add(index);
+                animateViewForTransition(oldRelativeIndex, -1, previousView);
             }
         }
 
         // If the window has changed
-        if (! (newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd)) {
+        if (!(newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd)) {
             // Run through the indices in the new range
             for (int i = newWindowStart; i <= newWindowEnd; i++) {
 
-                int oldRelativeIndex = i - mCurrentWindowStartUnbounded;
+                int index = modulo(i, adapterCount);
+                int oldRelativeIndex;
+                if (mViewsMap.containsKey(index)) {
+                    oldRelativeIndex = mViewsMap.get(index).index;
+                } else {
+                    oldRelativeIndex = -1;
+                }
                 int newRelativeIndex = i - newWindowStartUnbounded;
-                int index = modulo(i, mNumActiveViews);
 
                 // If this item is in the current window, great, we just need to apply
                 // the transform for it's new relative position in the window, and animate
                 // between it's current and new relative positions
-                if (i >= mCurrentWindowStart && i <= mCurrentWindowEnd) {
-                    View view = mActiveViews[index];
+                boolean inOldRange = mViewsMap.containsKey(index) && !mPreviousViews.contains(index);
+
+                if (inOldRange) {
+                    View view = mViewsMap.get(index).view;
+                    mViewsMap.get(index).index = newRelativeIndex;
                     applyTransformForChildAtIndex(view, newRelativeIndex);
                     animateViewForTransition(oldRelativeIndex, newRelativeIndex, view);
 
-                // Otherwise this view is new, so first we have to displace the view that's
-                // taking the new view's place within our cache (a circular array)
+                // Otherwise this view is new to the window
                 } else {
-                    if (mActiveViews[index] != null) {
-                        View previousView = mActiveViews[index];
-                        mPreviousViews.add(previousView);
-                        int previousViewRelativeIndex = modulo(index - mCurrentWindowStart,
-                                mNumActiveViews);
-                        animateViewForTransition(previousViewRelativeIndex, -1, previousView);
-
-                        if (mCurrentWindowStart > newWindowStart) {
-                            mViewsToBringToFront.add(previousView);
-                        }
-                    }
-
-                    // We've cleared a spot for the new view. Get it from the adapter, add it
-                    // and apply any transform / animation
-                    View newView = mAdapter.getView(i, null, this);
+                    // Get the new view from the adapter, add it and apply any transform / animation
+                    View newView = mAdapter.getView(modulo(i, adapterCount), null, this);
 
                     // We wrap the new view in a FrameLayout so as to respect the contract
                     // with the adapter, that is, that we don't modify this view directly
@@ -453,12 +476,12 @@
                     if (newView != null) {
                        fl.addView(newView);
                     }
-                    mActiveViews[index] = fl;
+                    mViewsMap.put(index, new ViewAndIndex(fl, newRelativeIndex));
                     addChild(fl);
                     applyTransformForChildAtIndex(fl, newRelativeIndex);
                     animateViewForTransition(-1, newRelativeIndex, fl);
                 }
-                mActiveViews[index].bringToFront();
+                mViewsMap.get(index).view.bringToFront();
             }
 
             for (int i = 0; i < mViewsToBringToFront.size(); i++) {
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index 895683d..95ebdd3 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -68,6 +68,11 @@
                 com.android.internal.R.styleable.ViewFlipper_flipInterval, DEFAULT_INTERVAL);
         mAutoStart = a.getBoolean(
                 com.android.internal.R.styleable.ViewFlipper_autoStart, false);
+
+        // By default we want the flipper to loop
+        mLoopViews = a.getBoolean(
+                com.android.internal.R.styleable.AdapterViewAnimator_loopViews, true);
+
         a.recycle();
         initDefaultAnimations();
     }
@@ -194,8 +199,6 @@
        super.showPrevious();
    }
 
-   /**
-
     /**
      * Internal method to start or stop dispatching flip {@link Message} based
      * on {@link #mRunning} and {@link #mVisible} state.
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 40a5b29..839de7d 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -132,7 +132,7 @@
     }
 
     private void initStackView() {
-        configureViewAnimator(NUM_ACTIVE_VIEWS, NUM_ACTIVE_VIEWS - 2, false);
+        configureViewAnimator(NUM_ACTIVE_VIEWS, NUM_ACTIVE_VIEWS - 2);
         setStaticTransformationsEnabled(true);
         final ViewConfiguration configuration = ViewConfiguration.get(getContext());
         mTouchSlop = configuration.getScaledTouchSlop();
@@ -287,7 +287,6 @@
     // framework level support for drawing outside of a parent's bounds.
     private void disableParentalClipping() {
         if (mAncestorContainingAllChildren != null) {
-            Log.v(TAG, "Disabling parental clipping.");
             ViewGroup vg = this;
             while (vg.getParent() != null && vg.getParent() instanceof ViewGroup) {
                 if (vg == mAncestorContainingAllChildren) break;
@@ -363,7 +362,9 @@
 
             if (mAdapter == null) return;
 
-            if (mCurrentWindowStartUnbounded + activeIndex == 0) {
+            if (mLoopViews) {
+                mStackSlider.setMode(StackSlider.NORMAL_MODE);
+            } else if (mCurrentWindowStartUnbounded + activeIndex == 0) {
                 mStackSlider.setMode(StackSlider.BEGINNING_OF_STACK_MODE);
             } else if (mCurrentWindowStartUnbounded + activeIndex == mAdapter.getCount()) {
                 activeIndex--;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f5e806f..5a2cebe 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7594,28 +7594,28 @@
 
             if (canSelectAll()) {
                 menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
-                    setIcon(com.android.internal.R.drawable.ic_menu_chat_dashboard).
+                    setIcon(com.android.internal.R.drawable.ic_menu_select_all).
                     setAlphabeticShortcut('a');
                 atLeastOne = true;
             }
 
             if (canCut()) {
                 menu.add(0, ID_CUT, 0, com.android.internal.R.string.cut).
-                    setIcon(com.android.internal.R.drawable.ic_menu_compose).
+                    setIcon(com.android.internal.R.drawable.ic_menu_cut).
                     setAlphabeticShortcut('x');
                 atLeastOne = true;
             }
 
             if (canCopy()) {
                 menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy).
-                    setIcon(com.android.internal.R.drawable.ic_menu_attachment).
+                    setIcon(com.android.internal.R.drawable.ic_menu_copy).
                     setAlphabeticShortcut('c');
                 atLeastOne = true;
             }
 
             if (canPaste()) {
                 menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
-                        setIcon(com.android.internal.R.drawable.ic_menu_camera).
+                        setIcon(com.android.internal.R.drawable.ic_menu_paste).
                         setAlphabeticShortcut('v');
                 atLeastOne = true;
             }
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 6e11cff..2a8cd94 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -180,7 +180,7 @@
                 Log.d(TAG, "Found gdbserver: " + entry.getName());
             }
 
-            final String installGdbServerPath = APK_LIB + GDBSERVER;
+            final String installGdbServerPath = GDBSERVER;
             nativeFiles.add(Pair.create(entry, installGdbServerPath));
 
             return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 4da74e6..d5213db 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -16,11 +16,14 @@
 
 package com.android.internal.view;
 
+import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.view.DragEvent;
 import android.view.IWindow;
 import android.view.IWindowSession;
 import android.view.KeyEvent;
@@ -66,7 +69,10 @@
             }
         }
     }
-    
+
+    public void dispatchDragEvent(DragEvent event) {
+    }
+
     public void dispatchWallpaperCommand(String action, int x, int y,
             int z, Bundle extras, boolean sync) {
         if (sync) {
diff --git a/core/res/res/drawable-hdpi/ic_menu_copy.png b/core/res/res/drawable-hdpi/ic_menu_copy.png
new file mode 100644
index 0000000..8f11153
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_copy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_cut.png b/core/res/res/drawable-hdpi/ic_menu_cut.png
new file mode 100644
index 0000000..6ad379e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_cut.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_paste.png b/core/res/res/drawable-hdpi/ic_menu_paste.png
new file mode 100644
index 0000000..5a3850f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_paste.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_select_all.png b/core/res/res/drawable-hdpi/ic_menu_select_all.png
new file mode 100644
index 0000000..dde6741
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_select_all.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_copy.png b/core/res/res/drawable-mdpi/ic_menu_copy.png
new file mode 100644
index 0000000..89d626f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_copy.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_cut.png b/core/res/res/drawable-mdpi/ic_menu_cut.png
new file mode 100644
index 0000000..1b4733e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_cut.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_paste.png b/core/res/res/drawable-mdpi/ic_menu_paste.png
new file mode 100755
index 0000000..cdf7ca3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_paste.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_select_all.png b/core/res/res/drawable-mdpi/ic_menu_select_all.png
new file mode 100644
index 0000000..37fd3cbd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_select_all.png
Binary files differ
diff --git a/core/res/res/menu/webview_copy.xml b/core/res/res/menu/webview_copy.xml
index 224f54f..adba563 100644
--- a/core/res/res/menu/webview_copy.xml
+++ b/core/res/res/menu/webview_copy.xml
@@ -16,7 +16,7 @@
 
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:id="@+id/copy"
-        android:icon="@drawable/ic_menu_attachment"
+        android:icon="@drawable/ic_menu_copy"
         android:showAsAction="always"
         />
     <item android:id="@+id/share"
@@ -24,7 +24,7 @@
         android:showAsAction="always"
         />
     <item android:id="@+id/select_all"
-        android:icon="@drawable/ic_menu_chat_dashboard"
+        android:icon="@drawable/ic_menu_select_all"
         android:showAsAction="always"
         />
     <item android:id="@+id/find"
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index b618756..830fb01 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -71,20 +71,6 @@
         <item>@drawable/indicator_code_lock_point_area_default</item>
         <item>@drawable/indicator_code_lock_point_area_green</item>
         <item>@drawable/indicator_code_lock_point_area_red</item>
-        <!-- SlidingTab drawables shared by InCallScreen and LockScreen -->
-        <item>@drawable/jog_tab_bar_left_end_confirm_gray</item>
-        <item>@drawable/jog_tab_bar_left_end_normal</item>
-        <item>@drawable/jog_tab_bar_left_end_pressed</item>
-        <item>@drawable/jog_tab_bar_right_end_confirm_gray</item>
-        <item>@drawable/jog_tab_bar_right_end_normal</item>
-        <item>@drawable/jog_tab_bar_right_end_pressed</item>
-        <item>@drawable/jog_tab_left_confirm_gray</item>
-        <item>@drawable/jog_tab_left_normal</item>
-        <item>@drawable/jog_tab_left_pressed</item>
-        <item>@drawable/jog_tab_right_confirm_gray</item>
-        <item>@drawable/jog_tab_right_normal</item>
-        <item>@drawable/jog_tab_right_pressed</item>
-        <item>@drawable/jog_tab_target_gray</item>
     </array>
 
     <!-- Do not translate. These are all of the color state list resources that should be
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 317b3f3..5df95de 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2384,8 +2384,11 @@
         <attr name="inAnimation" />
         <!-- Identifier for the animation to use when a view is hidden. -->
         <attr name="outAnimation" />
+        <!--Defines whether the animator loops to the first view once it
+        has reached the end of the list. -->
+        <attr name="loopViews" format="boolean" />
         <!-- Defines whether to animate the current View when the ViewAnimation
-             is first displayed. -->
+        is first displayed. -->
         <attr name="animateFirstView" />
     </declare-styleable>
     <declare-styleable name="AdapterViewFlipper">
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index adcbb10..8592820 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1360,6 +1360,7 @@
   <public type="attr" name="breadCrumbShortTitle" />
   <public type="attr" name="listDividerAlertDialog" />
   <public type="attr" name="textColorAlertDialogListItem" />
+  <public type="attr" name="loopViews" />
 
   <public type="anim" name="animator_fade_in" />
   <public type="anim" name="animator_fade_out" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
old mode 100644
new mode 100755
index 16d35b4..8b4f91f
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1249,31 +1249,29 @@
     <!-- Policy administration -->
 
     <!-- Title of policy access to limiting the user's password choices -->
-    <string name="policylab_limitPassword">Limit password</string>
+    <string name="policylab_limitPassword">Set password rules</string>
     <!-- Description of policy access to limiting the user's password choices -->
-    <string name="policydesc_limitPassword">Restrict the types of passwords you
-        are allowed to use.</string>
+    <string name="policydesc_limitPassword">Control the length and the characters 
+    allowed in screen-unlock passwords</string>
     <!-- Title of policy access to watch user login attempts -->
-    <string name="policylab_watchLogin">Watch login attempts</string>
+    <string name="policylab_watchLogin">Monitor screen-unlock attempts</string>
     <!-- Description of policy access to watch user login attempts -->
-    <string name="policydesc_watchLogin">Monitor failed attempts to login to
-        the device, to perform some action.</string>
+    <string name="policydesc_watchLogin">Monitor the number of incorrect passwords 
+    entered when unlocking the screen, and lock the phone or erase all the phone\'s 
+    data if too many incorrect passwords are entered</string>
     <!-- Title of policy access to reset user's password -->
-    <string name="policylab_resetPassword">Reset password</string>
+    <string name="policylab_resetPassword">Change the screen-unlock password</string>
     <!-- Description of policy access to reset user's password -->
-    <string name="policydesc_resetPassword">Force your password
-        to a new value, requiring the administrator give it to you
-        before you can log in.</string>
+    <string name="policydesc_resetPassword">Change the screen-unlock password</string>
     <!-- Title of policy access to force lock the device -->
-    <string name="policylab_forceLock">Force lock</string>
+    <string name="policylab_forceLock">Lock the screen</string>
     <!-- Description of policy access to limiting the user's password choices -->
-    <string name="policydesc_forceLock">Control when device locks,
-        requiring you re-enter its password.</string>
+    <string name="policydesc_forceLock">Control how and when the screen locks</string>
     <!-- Title of policy access to wipe the user's data -->
     <string name="policylab_wipeData">Erase all data</string>
     <!-- Description of policy access to wipe the user's data -->
-    <string name="policydesc_wipeData">Perform a factory reset, deleting
-        all of your data without any confirmation.</string>
+    <string name="policydesc_wipeData">Erase the phone\'s data without warning, 
+    by performing a factory data reset</string>
     <string name="policylab_setGlobalProxy">Set the device global proxy</string>
     <!-- Description of policy access to wipe the user's data -->
     <string name="policydesc_setGlobalProxy">Set the device global proxy
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index 92d4e15..3b4ccb0e 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -52,7 +52,7 @@
 <div class="dashboard-panel">
 
 <img alt="" height="250" width="460"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:12.0,17.5,0.1,41.7,28.7&chl=
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:9.7,16.4,0.1,40.4,33.4&chl=
 Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b,
 6fad0c" />
 
@@ -62,13 +62,13 @@
   <th>API Level</th>
   <th>Distribution</th>
 </tr>
-<tr><td>Android 1.5</td><td>3</td><td>12.0%</td></tr>
-<tr><td>Android 1.6</td><td>4</td><td>17.5%</td></tr>
-<tr><td>Android 2.1</td><td>7</td><td>41.7%</td></tr>
-<tr><td>Android 2.2</td><td>8</td><td>28.7%</td></tr>
+<tr><td>Android 1.5</td><td>3</td><td>9.7%</td></tr>
+<tr><td>Android 1.6</td><td>4</td><td>16.4%</td></tr>
+<tr><td>Android 2.1</td><td>7</td><td>40.4%</td></tr>
+<tr><td>Android 2.2</td><td>8</td><td>33.4%</td></tr>
 </table>
 
-<p><em>Data collected during two weeks ending on September 1, 2010</em></p>
+<p><em>Data collected during two weeks ending on October 1, 2010</em></p>
 <p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
 
 </div><!-- end dashboard-panel -->
@@ -96,19 +96,19 @@
 <div class="dashboard-panel">
 
 <img alt="" height="250" width="660" style="padding:5px;background:#fff"
-src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,y,r&chxr=0,0,12|1,0,100|2,0,100&
-chxl=0%3A%7C2010/03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/
-01%7C08/15%7C2010/09/01%7C1%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C2%3A%7C0%25%7C25%25%7C50%25
-%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.4,99.5,99.6,99.6,99.6,99.7,
-100.6,101.1,99.9,100.0,100.0,99.8,99.9|61.6,60.6,61.5,61.7,62.3,63.5,73.0,76.4,78.6,81.1,84.5,86.6,
-88.0|24.3,25.4,29.4,30.2,32.7,35.3,46.2,51.3,55.1,59.0,64.1,68.2,70.4|0.0,0.0,4.0,28.3,32.0,34.9,45.
-9,51.0,54.9,58.8,64.0,68.1,70.3|0.0,0.0,0.0,0.0,0.0,0.0,0.8,1.2,1.8,3.3,4.3,11.3,27.8&chm=tAndroid%
-201.5,7caa36,0,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid%201.6,638d23,1,0,15,,t::-5|b,b0db6e,1,2,0|
-tAndroid%202.0.1,496c13,2,0,15,,t::-5|b,9ddb3d,2,3,0|tAndroid%202.1,2f4708,3,3,15,,t::-5|b,89cf19,3,
-4,0|tAndroid%202.2,131d02,4,11,15,,t::-5|B,6fad0c,4,5,0&chg=7,25&chdl=Android%201.5|Android%201.6|
-Android%202.0.1|Android%202.1|Android%202.2&chco=add274,9ad145,84c323,6ba213,507d08" />
+src="http://chart.apis.google.com/chart?cht=lc&chs=660x250&chxt=x,y,r&chxr=0,0,12|1,0,100|2,0,100&
+chxl=0:|2010/04/01|04/15|05/01|05/15|06/01|06/15|07/01|07/15|08/01|08/15|09/01|09/15|2010/10/01|1:|0
+%25|25%25|50%25|75%25|100%25|2:|0%25|25%25|50%25|75%25|100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&
+chxtc=0,5&chd=t:99.6,99.6,99.6,99.7,100.6,101.1,99.9,100.0,100.0,99.8,99.9,100.0,100.0|61.5,61.7,62.
+3,63.5,73.0,76.4,78.6,81.1,84.5,86.6,88.0,89.3,90.3|29.4,30.2,32.7,35.3,46.2,51.3,55.1,59.0,64.1,68.
+2,70.4,72.2,73.9|4.0,28.3,32.0,34.9,45.9,51.0,54.9,58.8,64.0,68.1,70.3,72.1,73.8|0.0,0.0,0.0,0.0,0.8
+,1.2,1.8,3.3,4.3,11.3,27.8,32.1,33.4&chm=tAndroid+1.5,7caa36,0,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid+1
+.6,638d23,1,0,15,,t::-5|b,b0db6e,1,2,0|tAndroid+2.0.1,496c13,2,0,15,,t::-5|b,9ddb3d,2,3,0|tAndroid+2
+.1,2f4708,3,1,15,,t:-30:-40|b,89cf19,3,4,0|tAndroid+2.2,131d02,4,9,15,,t::-5|B,6fad0c,4,5,0&chg=7,25
+&chdl=Android+1.5|Android+1.6|Android+2.0.1|Android+2.1|Android+2.2&chco=add274,9ad145,84c323,6ba213
+,507d08" />
 
-<p><em>Last historical dataset collected during two weeks ending on September 1, 2010</em></p>
+<p><em>Last historical dataset collected during two weeks ending on October 1, 2010</em></p>
 
 
 </div><!-- end dashboard-panel -->
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index c3416a0..1324431 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -236,6 +236,7 @@
      * @param y The y-coordinate of the end of a line
      */
     public void lineTo(float x, float y) {
+        isSimplePath = false;
         native_lineTo(mNativePath, x, y);
     }
 
@@ -250,6 +251,7 @@
      *           this contour, to specify a line
      */
     public void rLineTo(float dx, float dy) {
+        isSimplePath = false;
         native_rLineTo(mNativePath, dx, dy);
     }
 
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 1510f87..c6990bf 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -42,8 +42,11 @@
 #define CBLK_FORCEREADY_ON      0x0004  // track is considered ready immediately by AudioFlinger
 #define CBLK_FORCEREADY_OFF     0x0000  // track is ready when buffer full
 #define CBLK_INVALID_MSK        0x0008
-#define CBLK_INVALID_ON         0x0008  // track buffer is invalidated by AudioFlinger: must be re-created
-#define CBLK_INVALID_OFF        0x0000
+#define CBLK_INVALID_ON         0x0008  // track buffer is invalidated by AudioFlinger:
+#define CBLK_INVALID_OFF        0x0000  // must be re-created
+#define CBLK_DISABLED_MSK       0x0010
+#define CBLK_DISABLED_ON        0x0010  // track disabled by AudioFlinger due to underrun:
+#define CBLK_DISABLED_OFF       0x0000  // must be re-started
 
 struct audio_track_cblk_t
 {
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index 76307b2..6533600 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -115,7 +115,8 @@
      */
     virtual status_t captureScreen(DisplayID dpy,
             sp<IMemoryHeap>* heap,
-            uint32_t* width, uint32_t* height, PixelFormat* format) = 0;
+            uint32_t* width, uint32_t* height, PixelFormat* format,
+            uint32_t reqWidth, uint32_t reqHeight) = 0;
 
     /* Signal surfaceflinger that there might be some work to do
      * This is an ASYNCHRONOUS call.
diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h
index 8773d71..a80832d 100644
--- a/include/surfaceflinger/SurfaceComposerClient.h
+++ b/include/surfaceflinger/SurfaceComposerClient.h
@@ -170,6 +170,36 @@
 };
 
 // ---------------------------------------------------------------------------
+
+class ScreenshotClient
+{
+    sp<IMemoryHeap> mHeap;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    PixelFormat mFormat;
+public:
+    ScreenshotClient();
+
+    // frees the previous screenshot and capture a new one
+    status_t update();
+    status_t update(uint32_t reqWidth, uint32_t reqHeight);
+
+    // release memory occupied by the screenshot
+    void release();
+
+    // pixels are valid until this object is freed or
+    // release() or update() is called
+    void const* getPixels() const;
+
+    uint32_t getWidth() const;
+    uint32_t getHeight() const;
+    PixelFormat getFormat() const;
+    uint32_t getStride() const;
+    // size of allocated memory in bytes
+    size_t getSize() const;
+};
+
+// ---------------------------------------------------------------------------
 }; // namespace android
 
 #endif // ANDROID_SF_SURFACE_COMPOSER_CLIENT_H
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 21baf32..ee40b85 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -73,7 +73,8 @@
  * policy decisions such as waking from device sleep.
  */
 enum {
-    /* These flags originate in RawEvents and are generally set in the key map. */
+    /* These flags originate in RawEvents and are generally set in the key map.
+     * See also labels for policy flags in KeycodeLabels.h. */
 
     POLICY_FLAG_WAKE = 0x00000001,
     POLICY_FLAG_WAKE_DROPPED = 0x00000002,
@@ -83,6 +84,7 @@
     POLICY_FLAG_ALT_GR = 0x00000020,
     POLICY_FLAG_MENU = 0x00000040,
     POLICY_FLAG_LAUNCHER = 0x00000080,
+    POLICY_FLAG_VIRTUAL = 0x00000100,
 
     POLICY_FLAG_RAW_MASK = 0x0000ffff,
 
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index e85735a..2209cb8 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -103,10 +103,6 @@
     virtual bool getDisplayInfo(int32_t displayId,
             int32_t* width, int32_t* height, int32_t* orientation) = 0;
 
-    /* Provides feedback for a virtual key down.
-     */
-    virtual void virtualKeyDownFeedback() = 0;
-
     /* Intercepts a key event.
      * The policy can use this method as an opportunity to perform power management functions
      * and early event preprocessing such as updating policy flags.
@@ -308,9 +304,6 @@
             GetStateFunc getStateFunc);
     bool markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags);
-
-    // dump state
-    void dumpDeviceInfo(String8& dump);
 };
 
 
@@ -340,6 +333,7 @@
 
     inline bool isIgnored() { return mMappers.isEmpty(); }
 
+    void dump(String8& dump);
     void addMapper(InputMapper* mapper);
     void configure();
     void reset();
@@ -393,6 +387,7 @@
 
     virtual uint32_t getSources() = 0;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void dump(String8& dump);
     virtual void configure();
     virtual void reset();
     virtual void process(const RawEvent* rawEvent) = 0;
@@ -436,6 +431,7 @@
 
     virtual uint32_t getSources();
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void dump(String8& dump);
     virtual void reset();
     virtual void process(const RawEvent* rawEvent);
 
@@ -484,6 +480,7 @@
 
     virtual uint32_t getSources();
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void dump(String8& dump);
     virtual void reset();
     virtual void process(const RawEvent* rawEvent);
 
@@ -540,6 +537,7 @@
 
     virtual uint32_t getSources();
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
+    virtual void dump(String8& dump);
     virtual void configure();
     virtual void reset();
 
@@ -761,15 +759,16 @@
     } mLocked;
 
     virtual void configureParameters();
-    virtual void logParameters();
+    virtual void dumpParameters(String8& dump);
     virtual void configureRawAxes();
-    virtual void logRawAxes();
+    virtual void dumpRawAxes(String8& dump);
     virtual bool configureSurfaceLocked();
-    virtual void logMotionRangesLocked();
+    virtual void dumpSurfaceLocked(String8& dump);
     virtual void configureVirtualKeysLocked();
+    virtual void dumpVirtualKeysLocked(String8& dump);
     virtual void parseCalibration();
     virtual void resolveCalibration();
-    virtual void logCalibration();
+    virtual void dumpCalibration(String8& dump);
 
     enum TouchResult {
         // Dispatch the touch normally.
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index c8d6ffc..f71d9cd 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -142,6 +142,7 @@
     { NULL, 0 }
 };
 
+// See also policy flags in Input.h.
 static const KeycodeLabel FLAGS[] = {
     { "WAKE", 0x00000001 },
     { "WAKE_DROPPED", 0x00000002 },
@@ -151,6 +152,7 @@
     { "ALT_GR", 0x00000020 },
     { "MENU", 0x00000040 },
     { "LAUNCHER", 0x00000080 },
+    { "VIRTUAL", 0x00000100 },
     { NULL, 0 }
 };
 
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index ee90702..ce85d46 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -22,6 +22,62 @@
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define PATH_HEAP_SIZE 64
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers
+///////////////////////////////////////////////////////////////////////////////
+
+PathHeap::PathHeap(): mHeap(PATH_HEAP_SIZE * sizeof(SkPath)) {
+}
+
+PathHeap::PathHeap(SkFlattenableReadBuffer& buffer): mHeap(PATH_HEAP_SIZE * sizeof(SkPath)) {
+    int count = buffer.readS32();
+
+    mPaths.setCount(count);
+    SkPath** ptr = mPaths.begin();
+    SkPath* p = (SkPath*) mHeap.allocThrow(count * sizeof(SkPath));
+
+    for (int i = 0; i < count; i++) {
+        new (p) SkPath;
+        p->unflatten(buffer);
+        *ptr++ = p;
+        p++;
+    }
+}
+
+PathHeap::~PathHeap() {
+    SkPath** iter = mPaths.begin();
+    SkPath** stop = mPaths.end();
+    while (iter < stop) {
+        (*iter)->~SkPath();
+        iter++;
+    }
+}
+
+int PathHeap::append(const SkPath& path) {
+    SkPath* p = (SkPath*) mHeap.allocThrow(sizeof(SkPath));
+    new (p) SkPath(path);
+    *mPaths.append() = p;
+    return mPaths.count();
+}
+
+void PathHeap::flatten(SkFlattenableWriteBuffer& buffer) const {
+    int count = mPaths.count();
+
+    buffer.write32(count);
+    SkPath** iter = mPaths.begin();
+    SkPath** stop = mPaths.end();
+    while (iter < stop) {
+        (*iter)->flatten(buffer);
+        iter++;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Display list
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 735f0e7..5d02bd7 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -45,39 +45,11 @@
 
 class PathHeap: public SkRefCnt {
 public:
-    PathHeap(): mHeap(64 * sizeof(SkPath)) {
-    };
+    PathHeap();
+    PathHeap(SkFlattenableReadBuffer& buffer);
+    ~PathHeap();
 
-    PathHeap(SkFlattenableReadBuffer& buffer): mHeap(64 * sizeof(SkPath)) {
-        int count = buffer.readS32();
-
-        mPaths.setCount(count);
-        SkPath** ptr = mPaths.begin();
-        SkPath* p = (SkPath*) mHeap.allocThrow(count * sizeof(SkPath));
-
-        for (int i = 0; i < count; i++) {
-            new (p) SkPath;
-            p->unflatten(buffer);
-            *ptr++ = p;
-            p++;
-        }
-    }
-
-    ~PathHeap() {
-        SkPath** iter = mPaths.begin();
-        SkPath** stop = mPaths.end();
-        while (iter < stop) {
-            (*iter)->~SkPath();
-            iter++;
-        }
-    }
-
-    int append(const SkPath& path) {
-        SkPath* p = (SkPath*) mHeap.allocThrow(sizeof(SkPath));
-        new (p) SkPath(path);
-        *mPaths.append() = p;
-        return mPaths.count();
-    }
+    int append(const SkPath& path);
 
     int count() const { return mPaths.count(); }
 
@@ -85,17 +57,7 @@
         return *mPaths[index];
     }
 
-    void flatten(SkFlattenableWriteBuffer& buffer) const {
-        int count = mPaths.count();
-
-        buffer.write32(count);
-        SkPath** iter = mPaths.begin();
-        SkPath** stop = mPaths.end();
-        while (iter < stop) {
-            (*iter)->flatten(buffer);
-            iter++;
-        }
-    }
+    void flatten(SkFlattenableWriteBuffer& buffer) const;
 
 private:
     SkChunkAlloc mHeap;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index b66696d..4e4a277 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -435,25 +435,25 @@
     glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
             GL_ALPHA, GL_UNSIGNED_BYTE, 0);
 
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
     // Split up our cache texture into lines of certain widths
     int nextLine = 0;
-    mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
+    mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
     nextLine += mCacheLines.top()->mMaxHeight;
-    mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
+    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
     nextLine += mCacheLines.top()->mMaxHeight;
-    mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
+    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
     nextLine += mCacheLines.top()->mMaxHeight;
-    mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
+    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
     nextLine += mCacheLines.top()->mMaxHeight;
-    mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
+    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
     nextLine += mCacheLines.top()->mMaxHeight;
-    mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0));
+    mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
     nextLine += mCacheLines.top()->mMaxHeight;
     mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
 }
@@ -631,6 +631,7 @@
         precacheLatin(paint);
     }
 }
+
 FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
         uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
     checkInit();
@@ -713,7 +714,7 @@
 
     float normalizeFactor = 0.0f;
     for(int32_t r = -radius; r <= radius; r ++) {
-        float floatR = (float)r;
+        float floatR = (float) r;
         weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
         normalizeFactor += weights[r + radius];
     }
@@ -742,7 +743,7 @@
             if ((x > radius) && (x < (width - radius))) {
                 const uint8_t *i = input + (x - radius);
                 for(int r = -radius; r <= radius; r ++) {
-                    currentPixel = (float)(*i);
+                    currentPixel = (float) (*i);
                     blurredPixel += currentPixel * gPtr[0];
                     gPtr++;
                     i++;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index de5c019..4fb8f8d 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -183,14 +183,14 @@
         }
 
         bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
-            if (glyph.fHeight > mMaxHeight) {
+            if (glyph.fHeight + 2 > mMaxHeight) {
                 return false;
             }
 
-            if (mCurrentCol + glyph.fWidth < mMaxWidth) {
-                *retOriginX = mCurrentCol;
-                *retOriginY = mCurrentRow;
-                mCurrentCol += glyph.fWidth;
+            if (mCurrentCol + glyph.fWidth + 2 < mMaxWidth) {
+                *retOriginX = mCurrentCol + 1;
+                *retOriginY = mCurrentRow + 1;
+                mCurrentCol += glyph.fWidth + 2;
                 mDirty = true;
                 return true;
             }
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 60523db..a0cc5d6 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -44,7 +44,7 @@
     uint32_t id;
 
     bool operator<(const LayerSize& rhs) const {
-        if (id != 0 && rhs.id != 0) {
+        if (id != 0 && rhs.id != 0 && id != rhs.id) {
             return id < rhs.id;
         }
         if (width == rhs.width) {
@@ -54,7 +54,7 @@
     }
 
     bool operator==(const LayerSize& rhs) const {
-        return width == rhs.width && height == rhs.height;
+        return id == rhs.id && width == rhs.width && height == rhs.height;
     }
 }; // struct LayerSize
 
@@ -83,7 +83,7 @@
      */
     bool blend;
     /**
-     * Indicates that this layer has never been used before.
+     * Indicates whether this layer has been used already.
      */
     bool empty;
 }; // struct Layer
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 2770868..8c70cf9 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -114,6 +114,8 @@
         glGenTextures(1, &layer->texture);
         glBindTexture(GL_TEXTURE_2D, layer->texture);
 
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 23de3a5..0810fb8 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -366,6 +366,8 @@
         return false;
     }
 
+    glActiveTexture(GL_TEXTURE0);
+
     LayerSize size(bounds.getWidth(), bounds.getHeight());
     Layer* layer = mCaches.layerCache.get(size);
     if (!layer) {
@@ -383,17 +385,22 @@
     // Copy the framebuffer into the layer
     glBindTexture(GL_TEXTURE_2D, layer->texture);
 
-    if (layer->empty) {
-        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom,
-                bounds.getWidth(), bounds.getHeight(), 0);
-        layer->empty = false;
-    } else {
-        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, mHeight - bounds.bottom,
-                bounds.getWidth(), bounds.getHeight());
-    }
+    // TODO: Workaround for b/3054204
+    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom,
+            bounds.getWidth(), bounds.getHeight(), 0);
 
-    if (flags & SkCanvas::kClipToLayer_SaveFlag) {
-        if (mSnapshot->clipTransformed(bounds)) setScissorFromClip();
+    // TODO: Waiting for b/3054204 to be fixed
+//    if (layer->empty) {
+//        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom,
+//                bounds.getWidth(), bounds.getHeight(), 0);
+//        layer->empty = false;
+//    } else {
+//        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, mHeight - bounds.bottom,
+//                bounds.getWidth(), bounds.getHeight());
+//    }
+
+    if (flags & SkCanvas::kClipToLayer_SaveFlag && mSnapshot->clipTransformed(bounds)) {
+        setScissorFromClip();
     }
 
     // Enqueue the buffer coordinates to clear the corresponding region later
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 70e06a1..377727b 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -138,8 +138,8 @@
         const SkPath *path, const SkPaint* paint) {
     const SkRect& bounds = path->getBounds();
 
-    const float pathWidth = bounds.width();
-    const float pathHeight = bounds.height();
+    const float pathWidth = fmax(bounds.width(), 1.0f);
+    const float pathHeight = fmax(bounds.height(), 1.0f);
 
     if (pathWidth > mMaxTextureSize || pathHeight > mMaxTextureSize) {
         LOGW("Path too large to be rendered into a texture");
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
index 33945a5..f5fecba 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
@@ -65,7 +65,6 @@
 
 
 static void copyInput() {
-    RS_DEBUG_MARKER;
     rs_allocation ain = rsGetAllocation(InPixel);
     uint32_t dimx = rsAllocationGetDimX(ain);
     uint32_t dimy = rsAllocationGetDimY(ain);
@@ -74,7 +73,6 @@
             ScratchPixel1[x + y * dimx] = convert_float4(InPixel[x + y * dimx]);
         }
     }
-    RS_DEBUG_MARKER;
 }
 
 void filter() {
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index cfd6479..6940033 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -241,6 +241,10 @@
     return true;
 }
 
+void Context::setupProgramStore() {
+    mFragmentStore->setupGL2(this, &mStateFragmentStore);
+}
+
 static bool getProp(const char *str)
 {
     char buf[PROPERTY_VALUE_MAX];
@@ -282,14 +286,14 @@
      rsc->props.mLogShadersUniforms = getProp("debug.rs.shader.uniforms");
      rsc->props.mLogVisual = getProp("debug.rs.visual");
 
-     ScriptTLSStruct *tlsStruct = new ScriptTLSStruct;
-     if (!tlsStruct) {
+     rsc->mTlsStruct = new ScriptTLSStruct;
+     if (!rsc->mTlsStruct) {
          LOGE("Error allocating tls storage");
          return NULL;
      }
-     tlsStruct->mContext = rsc;
-     tlsStruct->mScript = NULL;
-     int status = pthread_setspecific(rsc->gThreadTLSKey, tlsStruct);
+     rsc->mTlsStruct->mContext = rsc;
+     rsc->mTlsStruct->mScript = NULL;
+     int status = pthread_setspecific(rsc->gThreadTLSKey, rsc->mTlsStruct);
      if (status) {
          LOGE("pthread_setspecific %i", status);
      }
@@ -361,6 +365,7 @@
          rsc->deinitEGL();
          pthread_mutex_unlock(&gInitMutex);
      }
+     delete rsc->mTlsStruct;
 
      LOGV("%p, RS Thread exited", rsc);
      return NULL;
@@ -387,6 +392,11 @@
 #endif
 
      setpriority(PRIO_PROCESS, rsc->mWorkers.mNativeThreadId[idx], rsc->mThreadPriority);
+     int status = pthread_setspecific(rsc->gThreadTLSKey, rsc->mTlsStruct);
+     if (status) {
+         LOGE("pthread_setspecific %i", status);
+     }
+
      while(rsc->mRunning) {
          rsc->mWorkers.mLaunchSignals[idx].wait();
          if (rsc->mWorkers.mLaunchCallback) {
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 2e84930..dabe196 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -49,6 +49,24 @@
 
 namespace renderscript {
 
+#if 0
+#define CHECK_OBJ(o) { \
+    GET_TLS(); \
+    if(!ObjectBase::isValid(rsc, (const ObjectBase *)o)) {  \
+        LOGE("Bad object %p at %s, %i", o, __FILE__, __LINE__);  \
+    } \
+}
+#define CHECK_OBJ_OR_NULL(o) { \
+    GET_TLS(); \
+    if(o && !ObjectBase::isValid(rsc, (const ObjectBase *)o)) {  \
+        LOGE("Bad object %p at %s, %i", o, __FILE__, __LINE__);  \
+    } \
+}
+#else
+#define CHECK_OBJ(o)
+#define CHECK_OBJ_OR_NULL(o)
+#endif
+
 class Context
 {
 public:
@@ -64,6 +82,7 @@
         Context * mContext;
         Script * mScript;
     };
+    ScriptTLSStruct *mTlsStruct;
 
     typedef void (*WorkerCallback_t)(void *usr, uint32_t idx);
 
@@ -99,6 +118,7 @@
     Font * getFont() {return mFont.get();}
 
     bool setupCheck();
+    void setupProgramStore();
     bool checkDriver() const {return mEGL.mSurface != 0;}
 
     void pause();
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index 12dedac..c516ea9 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -23,6 +23,7 @@
 
 #include "rsFont.h"
 #include "rsProgramFragment.h"
+#include <cutils/properties.h>
 #include FT_BITMAP_H
 
 #include <GLES/gl.h>
@@ -268,6 +269,44 @@
     mRSC = NULL;
     mLibrary = NULL;
     setFontColor(0.1f, 0.1f, 0.1f, 1.0f);
+
+    // Get the renderer properties
+    char property[PROPERTY_VALUE_MAX];
+
+    // Get the gamma
+    float gamma = DEFAULT_TEXT_GAMMA;
+    if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) {
+        LOGD("  Setting text gamma to %s", property);
+        gamma = atof(property);
+    } else {
+        LOGD("  Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA);
+    }
+
+    // Get the black gamma threshold
+    int blackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
+    if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) {
+        LOGD("  Setting text black gamma threshold to %s", property);
+        blackThreshold = atoi(property);
+    } else {
+        LOGD("  Using default text black gamma threshold of %d",
+                DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD);
+    }
+    mBlackThreshold = (float)(blackThreshold) / 255.0f;
+
+    // Get the white gamma threshold
+    int whiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
+    if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) {
+        LOGD("  Setting text white gamma threshold to %s", property);
+        whiteThreshold = atoi(property);
+    } else {
+        LOGD("  Using default white black gamma threshold of %d",
+                DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD);
+    }
+    mWhiteThreshold = (float)(whiteThreshold) / 255.0f;
+
+    // Compute the gamma tables
+    mBlackGamma = gamma;
+    mWhiteGamma = 1.0f / gamma;
 }
 
 FontState::~FontState()
@@ -391,12 +430,15 @@
     shaderString.append("void main() {\n");
     shaderString.append("  lowp vec4 col = UNI_Color;\n");
     shaderString.append("  col.a = texture2D(UNI_Tex0, varTex0.xy).a;\n");
+    shaderString.append("  col.a = pow(col.a, UNI_Gamma);\n");
     shaderString.append("  gl_FragColor = col;\n");
     shaderString.append("}\n");
 
     const Element *colorElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
+    const Element *gammaElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1);
     mRSC->mStateElement.elementBuilderBegin();
     mRSC->mStateElement.elementBuilderAdd(colorElem, "Color", 1);
+    mRSC->mStateElement.elementBuilderAdd(gammaElem, "Gamma", 1);
     const Element *constInput = mRSC->mStateElement.elementBuilderCreate(mRSC);
 
     Type *inputType = new Type(mRSC);
@@ -558,9 +600,9 @@
     ObjectBaseRef<const ProgramStore> tmpPS(mRSC->getFragmentStore());
     mRSC->setFragmentStore(mFontProgramStore.get());
 
-    if(mFontColorDirty) {
-        mFontShaderFConstant->data(mRSC, &mFontColor, 4*sizeof(float));
-        mFontColorDirty = false;
+    if(mConstantsDirty) {
+        mFontShaderFConstant->data(mRSC, &mConstants, sizeof(mConstants));
+        mConstantsDirty = false;
     }
 
     if (!mRSC->setupCheck()) {
@@ -725,18 +767,26 @@
 }
 
 void FontState::setFontColor(float r, float g, float b, float a) {
-    mFontColor[0] = r;
-    mFontColor[1] = g;
-    mFontColor[2] = b;
-    mFontColor[3] = a;
-    mFontColorDirty = true;
+    mConstants.mFontColor[0] = r;
+    mConstants.mFontColor[1] = g;
+    mConstants.mFontColor[2] = b;
+    mConstants.mFontColor[3] = a;
+
+    mConstants.mGamma = 1.0f;
+    const int luminance = (r * 2.0f + g * 5.0f + b) / 8.0f;
+    if (luminance <= mBlackThreshold) {
+        mConstants.mGamma = mBlackGamma;
+    } else if (luminance >= mWhiteThreshold) {
+        mConstants.mGamma = mWhiteGamma;
+    }
+    mConstantsDirty = true;
 }
 
 void FontState::getFontColor(float *r, float *g, float *b, float *a) const {
-    *r = mFontColor[0];
-    *g = mFontColor[1];
-    *b = mFontColor[2];
-    *a = mFontColor[3];
+    *r = mConstants.mFontColor[0];
+    *g = mConstants.mFontColor[1];
+    *b = mConstants.mFontColor[2];
+    *a = mConstants.mFontColor[3];
 }
 
 void FontState::deinit(Context *rsc)
diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h
index 027ed1d..16009ef 100644
--- a/libs/rs/rsFont.h
+++ b/libs/rs/rsFont.h
@@ -31,6 +31,15 @@
 
 namespace renderscript {
 
+// Gamma (>= 1.0, <= 10.0)
+#define PROPERTY_TEXT_GAMMA "ro.text_gamma"
+#define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "ro.text_gamma.black_threshold"
+#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "ro.text_gamma.white_threshold"
+
+#define DEFAULT_TEXT_GAMMA 1.4f
+#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64
+#define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192
+
 class FontState;
 
 class Font : public ObjectBase
@@ -162,8 +171,17 @@
 
     Context *mRSC;
 
-    float mFontColor[4];
-    bool mFontColorDirty;
+    struct {
+        float mFontColor[4];
+        float mGamma;
+    } mConstants;
+    bool mConstantsDirty;
+
+    float mBlackGamma;
+    float mWhiteGamma;
+
+    float mBlackThreshold;
+    float mWhiteThreshold;
 
     // Free type library, we only need one copy
     FT_Library mLibrary;
diff --git a/libs/rs/rsObjectBase.cpp b/libs/rs/rsObjectBase.cpp
index 713d61e..f69cb15 100644
--- a/libs/rs/rsObjectBase.cpp
+++ b/libs/rs/rsObjectBase.cpp
@@ -195,3 +195,15 @@
     }
 }
 
+bool ObjectBase::isValid(const Context *rsc, const ObjectBase *obj)
+{
+    const ObjectBase * o = rsc->mObjHead;
+    while (o) {
+        if (o == obj) {
+            return true;
+        }
+        o = o->mNext;
+    }
+    return false;
+}
+
diff --git a/libs/rs/rsObjectBase.h b/libs/rs/rsObjectBase.h
index ad95b81..59fb4a6 100644
--- a/libs/rs/rsObjectBase.h
+++ b/libs/rs/rsObjectBase.h
@@ -56,6 +56,8 @@
     virtual void serialize(OStream *stream) const = 0;
     virtual RsA3DClassID getClassId() const = 0;
 
+    static bool isValid(const Context *rsc, const ObjectBase *obj);
+
 protected:
     const char *mAllocFile;
     uint32_t mAllocLine;
diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp
index 2531a9b..10e00e6 100644
--- a/libs/rs/rsProgram.cpp
+++ b/libs/rs/rsProgram.cpp
@@ -115,6 +115,14 @@
 
 Program::~Program()
 {
+    if(mRSC->props.mLogShaders) {
+        LOGV("Program::~Program with shader id %u", mShaderID);
+    }
+
+    if(mShaderID) {
+        glDeleteShader(mShaderID);
+    }
+
     for (uint32_t ct=0; ct < MAX_UNIFORMS; ct++) {
         bindAllocation(NULL, NULL, ct);
     }
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index 275a1df..c94f294 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -49,6 +49,9 @@
 
 ProgramFragment::~ProgramFragment()
 {
+    if(mShaderID) {
+        mRSC->mShaderCache.cleanupFragment(mShaderID);
+    }
 }
 
 void ProgramFragment::setConstantColor(Context *rsc, float r, float g, float b, float a)
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index bd12989..d3dbfb2 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -45,15 +45,9 @@
 
 ProgramVertex::~ProgramVertex()
 {
-}
-
-static void logMatrix(const char *txt, const float *f)
-{
-    LOGV("Matrix %s, %p", txt, f);
-    LOGV("%6.4f, %6.4f, %6.4f, %6.4f", f[0], f[4], f[8], f[12]);
-    LOGV("%6.4f, %6.4f, %6.4f, %6.4f", f[1], f[5], f[9], f[13]);
-    LOGV("%6.4f, %6.4f, %6.4f, %6.4f", f[2], f[6], f[10], f[14]);
-    LOGV("%6.4f, %6.4f, %6.4f, %6.4f", f[3], f[7], f[11], f[15]);
+    if(mShaderID) {
+        mRSC->mShaderCache.cleanupVertex(mShaderID);
+    }
 }
 
 void ProgramVertex::loadShader(Context *rsc) {
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 22fd421..e0de867 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -203,45 +203,46 @@
 
 static uint32_t SC_allocGetDimX(RsAllocation va)
 {
-    GET_TLS();
     const Allocation *a = static_cast<const Allocation *>(va);
-    //LOGE("SC_allocGetDimX a=%p", a);
-    //LOGE(" type=%p", a->getType());
+    CHECK_OBJ(a);
+    //LOGE("SC_allocGetDimX a=%p  type=%p", a, a->getType());
     return a->getType()->getDimX();
 }
 
 static uint32_t SC_allocGetDimY(RsAllocation va)
 {
-    GET_TLS();
     const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
     return a->getType()->getDimY();
 }
 
 static uint32_t SC_allocGetDimZ(RsAllocation va)
 {
-    GET_TLS();
     const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
     return a->getType()->getDimZ();
 }
 
 static uint32_t SC_allocGetDimLOD(RsAllocation va)
 {
-    GET_TLS();
     const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
     return a->getType()->getDimLOD();
 }
 
 static uint32_t SC_allocGetDimFaces(RsAllocation va)
 {
-    GET_TLS();
     const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
     return a->getType()->getDimFaces();
 }
 
 static const void * SC_getElementAtX(RsAllocation va, uint32_t x)
 {
     const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
     const Type *t = a->getType();
+    CHECK_OBJ(t);
     const uint8_t *p = (const uint8_t *)a->getPtr();
     return &p[t->getElementSizeBytes() * x];
 }
@@ -249,7 +250,9 @@
 static const void * SC_getElementAtXY(RsAllocation va, uint32_t x, uint32_t y)
 {
     const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
     const Type *t = a->getType();
+    CHECK_OBJ(t);
     const uint8_t *p = (const uint8_t *)a->getPtr();
     return &p[t->getElementSizeBytes() * (x + y*t->getDimX())];
 }
@@ -257,7 +260,9 @@
 static const void * SC_getElementAtXYZ(RsAllocation va, uint32_t x, uint32_t y, uint32_t z)
 {
     const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
     const Type *t = a->getType();
+    CHECK_OBJ(t);
     const uint8_t *p = (const uint8_t *)a->getPtr();
     return &p[t->getElementSizeBytes() * (x + y*t->getDimX())];
 }
@@ -265,9 +270,11 @@
 static void SC_setObject(void **vdst, void * vsrc) {
     //LOGE("SC_setObject  %p,%p  %p", vdst, *vdst, vsrc);
     if (vsrc) {
+        CHECK_OBJ(vsrc);
         static_cast<ObjectBase *>(vsrc)->incSysRef();
     }
     if (vdst[0]) {
+        CHECK_OBJ(vdst[0]);
         static_cast<ObjectBase *>(vdst[0])->decSysRef();
     }
     *vdst = vsrc;
@@ -276,6 +283,7 @@
 static void SC_clearObject(void **vdst) {
     //LOGE("SC_clearObject  %p,%p", vdst, *vdst);
     if (vdst[0]) {
+        CHECK_OBJ(vdst[0]);
         static_cast<ObjectBase *>(vdst[0])->decSysRef();
     }
     *vdst = NULL;
diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp
index fd4c379..88db761 100644
--- a/libs/rs/rsScriptC_LibGL.cpp
+++ b/libs/rs/rsScriptC_LibGL.cpp
@@ -44,6 +44,8 @@
 
 static void SC_bindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va)
 {
+    CHECK_OBJ_OR_NULL(va);
+    CHECK_OBJ(vpf);
     GET_TLS();
     rsi_ProgramBindTexture(rsc,
                            static_cast<ProgramFragment *>(vpf),
@@ -54,6 +56,8 @@
 
 static void SC_bindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs)
 {
+    CHECK_OBJ_OR_NULL(vs);
+    CHECK_OBJ(vpf);
     GET_TLS();
     rsi_ProgramBindSampler(rsc,
                            static_cast<ProgramFragment *>(vpf),
@@ -64,24 +68,28 @@
 
 static void SC_bindProgramStore(RsProgramStore pfs)
 {
+    CHECK_OBJ_OR_NULL(pfs);
     GET_TLS();
     rsi_ContextBindProgramStore(rsc, pfs);
 }
 
 static void SC_bindProgramFragment(RsProgramFragment pf)
 {
+    CHECK_OBJ_OR_NULL(pf);
     GET_TLS();
     rsi_ContextBindProgramFragment(rsc, pf);
 }
 
 static void SC_bindProgramVertex(RsProgramVertex pv)
 {
+    CHECK_OBJ_OR_NULL(pv);
     GET_TLS();
     rsi_ContextBindProgramVertex(rsc, pv);
 }
 
 static void SC_bindProgramRaster(RsProgramRaster pv)
 {
+    CHECK_OBJ_OR_NULL(pv);
     GET_TLS();
     rsi_ContextBindProgramRaster(rsc, pv);
 }
@@ -112,6 +120,7 @@
 static void SC_pfConstantColor(RsProgramFragment vpf, float r, float g, float b, float a)
 {
     GET_TLS();
+    CHECK_OBJ(vpf);
     ProgramFragment *pf = static_cast<ProgramFragment *>(vpf);
     pf->setConstantColor(rsc, r, g, b, a);
 }
@@ -228,6 +237,7 @@
 
 static void SC_drawMesh(RsMesh vsm)
 {
+    CHECK_OBJ(vsm);
     GET_TLS();
     Mesh *sm = static_cast<Mesh *>(vsm);
     if (!rsc->setupCheck()) {
@@ -238,6 +248,7 @@
 
 static void SC_drawMeshPrimitive(RsMesh vsm, uint32_t primIndex)
 {
+    CHECK_OBJ(vsm);
     GET_TLS();
     Mesh *sm = static_cast<Mesh *>(vsm);
     if (!rsc->setupCheck()) {
@@ -248,6 +259,7 @@
 
 static void SC_drawMeshPrimitiveRange(RsMesh vsm, uint32_t primIndex, uint32_t start, uint32_t len)
 {
+    CHECK_OBJ(vsm);
     GET_TLS();
     Mesh *sm = static_cast<Mesh *>(vsm);
     if (!rsc->setupCheck()) {
@@ -259,6 +271,7 @@
 static void SC_meshComputeBoundingBox(RsMesh vsm, float *minX, float *minY, float *minZ,
                                                      float *maxX, float *maxY, float *maxZ)
 {
+    CHECK_OBJ(vsm);
     GET_TLS();
     Mesh *sm = static_cast<Mesh *>(vsm);
     sm->computeBBox();
@@ -285,17 +298,20 @@
 
 static void SC_uploadToTexture2(RsAllocation va, uint32_t baseMipLevel)
 {
+    CHECK_OBJ(va);
     GET_TLS();
     rsi_AllocationUploadToTexture(rsc, va, false, baseMipLevel);
 }
 static void SC_uploadToTexture(RsAllocation va)
 {
+    CHECK_OBJ(va);
     GET_TLS();
     rsi_AllocationUploadToTexture(rsc, va, false, 0);
 }
 
 static void SC_uploadToBufferObject(RsAllocation va)
 {
+    CHECK_OBJ(va);
     GET_TLS();
     rsi_AllocationUploadToBufferObject(rsc, va);
 }
@@ -303,9 +319,7 @@
 static void SC_ClearColor(float r, float g, float b, float a)
 {
     GET_TLS();
-    if (!rsc->setupCheck()) {
-        return;
-    }
+    rsc->setupProgramStore();
 
     glClearColor(r, g, b, a);
     glClear(GL_COLOR_BUFFER_BIT);
@@ -314,9 +328,7 @@
 static void SC_ClearDepth(float v)
 {
     GET_TLS();
-    if (!rsc->setupCheck()) {
-        return;
-    }
+    rsc->setupProgramStore();
 
     glClearDepthf(v);
     glClear(GL_DEPTH_BUFFER_BIT);
@@ -336,6 +348,7 @@
 
 static void SC_DrawTextAlloc(RsAllocation va, int x, int y)
 {
+    CHECK_OBJ(va);
     GET_TLS();
     Allocation *alloc = static_cast<Allocation *>(va);
     rsc->mStateFont.renderText(alloc, x, y);
@@ -349,6 +362,7 @@
 
 static void SC_BindFont(RsFont font)
 {
+    CHECK_OBJ(font);
     GET_TLS();
     rsi_ContextBindFont(rsc, font);
 }
diff --git a/libs/rs/rsShaderCache.cpp b/libs/rs/rsShaderCache.cpp
index 28e3b1d..45f6207 100644
--- a/libs/rs/rsShaderCache.cpp
+++ b/libs/rs/rsShaderCache.cpp
@@ -29,20 +29,15 @@
 
 ShaderCache::ShaderCache()
 {
-    mEntryCount = 0;
-    mEntryAllocationCount = 16;
-    mEntries = (entry_t *)calloc(mEntryAllocationCount, sizeof(entry_t));
+    mEntries.setCapacity(16);
 }
 
 ShaderCache::~ShaderCache()
 {
-    for (uint32_t ct=0; ct < mEntryCount; ct++) {
-        glDeleteProgram(mEntries[ct].program);
+    for (uint32_t ct=0; ct < mEntries.size(); ct++) {
+        glDeleteProgram(mEntries[ct]->program);
+        free(mEntries[ct]);
     }
-
-    mEntryCount = 0;
-    mEntryAllocationCount = 0;
-    free(mEntries);
 }
 
 bool ShaderCache::lookup(Context *rsc, ProgramVertex *vtx, ProgramFragment *frag)
@@ -59,44 +54,30 @@
         return false;
     }
     //LOGV("ShaderCache lookup  vtx %i, frag %i", vtx->getShaderID(), frag->getShaderID());
+    uint32_t entryCount = mEntries.size();
+    for(uint32_t ct = 0; ct < entryCount; ct ++) {
+        if ((mEntries[ct]->vtx == vtx->getShaderID()) &&
+            (mEntries[ct]->frag == frag->getShaderID())) {
 
-    for (uint32_t ct=0; ct < mEntryCount; ct++) {
-        if ((mEntries[ct].vtx == vtx->getShaderID()) &&
-            (mEntries[ct].frag == frag->getShaderID())) {
-
-            //LOGV("SC using program %i", mEntries[ct].program);
-            glUseProgram(mEntries[ct].program);
-            mCurrent = &mEntries[ct];
+            //LOGV("SC using program %i", mEntries[ct]->program);
+            glUseProgram(mEntries[ct]->program);
+            mCurrent = mEntries[ct];
             //LOGV("ShaderCache hit, using %i", ct);
             rsc->checkError("ShaderCache::lookup (hit)");
             return true;
         }
     }
-    // Not in cache, add it.
 
-    if (mEntryAllocationCount == mEntryCount) {
-        // Out of space, make some.
-        mEntryAllocationCount *= 2;
-        entry_t *e = (entry_t *)calloc(mEntryAllocationCount, sizeof(entry_t));
-        if (!e) {
-            LOGE("Out of memory for ShaderCache::lookup");
-            return false;
-        }
-        memcpy(e, mEntries, sizeof(entry_t) * mEntryCount);
-        free(mEntries);
-        mEntries = e;
-    }
-
-    //LOGV("ShaderCache miss, using %i", mEntryCount);
+    //LOGV("ShaderCache miss");
     //LOGE("e0 %x", glGetError());
-
-    entry_t *e = &mEntries[mEntryCount];
+    entry_t *e = (entry_t *)malloc(sizeof(entry_t));
+    mEntries.push(e);
     mCurrent = e;
     e->vtx = vtx->getShaderID();
     e->frag = frag->getShaderID();
     e->program = glCreateProgram();
     e->vtxAttrCount = vtx->getAttribCount();
-    if (mEntries[mEntryCount].program) {
+    if (e->program) {
         GLuint pgm = e->program;
         glAttachShader(pgm, vtx->getShaderID());
         //LOGE("e1 %x", glGetError());
@@ -155,7 +136,6 @@
     e->mIsValid = true;
     //LOGV("SC made program %i", e->program);
     glUseProgram(e->program);
-    mEntryCount++;
     rsc->checkError("ShaderCache::lookup (miss)");
     return true;
 }
@@ -171,10 +151,32 @@
 
 void ShaderCache::cleanupVertex(uint32_t id)
 {
+    int32_t numEntries = (int32_t)mEntries.size();
+    for(int32_t ct = 0; ct < numEntries; ct ++) {
+        if (mEntries[ct]->vtx == id) {
+            glDeleteProgram(mEntries[ct]->program);
+
+            free(mEntries[ct]);
+            mEntries.removeAt(ct);
+            numEntries = (int32_t)mEntries.size();
+            ct --;
+        }
+    }
 }
 
 void ShaderCache::cleanupFragment(uint32_t id)
 {
+    int32_t numEntries = (int32_t)mEntries.size();
+    for(int32_t ct = 0; ct < numEntries; ct ++) {
+        if (mEntries[ct]->frag == id) {
+            glDeleteProgram(mEntries[ct]->program);
+
+            free(mEntries[ct]);
+            mEntries.removeAt(ct);
+            numEntries = (int32_t)mEntries.size();
+            ct --;
+        }
+    }
 }
 
 void ShaderCache::cleanupAll()
diff --git a/libs/rs/rsShaderCache.h b/libs/rs/rsShaderCache.h
index 312c251..35ff95b 100644
--- a/libs/rs/rsShaderCache.h
+++ b/libs/rs/rsShaderCache.h
@@ -58,11 +58,12 @@
         int32_t mFragUniformSlots[Program::MAX_UNIFORMS];
         bool mIsValid;
     } entry_t;
-    entry_t *mEntries;
+    //entry_t *mEntries;
+    Vector<entry_t*> mEntries;
     entry_t *mCurrent;
 
-    uint32_t mEntryCount;
-    uint32_t mEntryAllocationCount;
+    /*uint32_t mEntryCount;
+    uint32_t mEntryAllocationCount;*/
 
 };
 
diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp
index 040060e..d676f5e 100644
--- a/libs/surfaceflinger_client/ISurfaceComposer.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp
@@ -126,11 +126,14 @@
 
     virtual status_t captureScreen(DisplayID dpy,
             sp<IMemoryHeap>* heap,
-            uint32_t* width, uint32_t* height, PixelFormat* format)
+            uint32_t* width, uint32_t* height, PixelFormat* format,
+            uint32_t reqWidth, uint32_t reqHeight)
     {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         data.writeInt32(dpy);
+        data.writeInt32(reqWidth);
+        data.writeInt32(reqHeight);
         remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
         *heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
         *width = reply.readInt32();
@@ -208,10 +211,13 @@
         case CAPTURE_SCREEN: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             DisplayID dpy = data.readInt32();
+            uint32_t reqWidth = data.readInt32();
+            uint32_t reqHeight = data.readInt32();
             sp<IMemoryHeap> heap;
             uint32_t w, h;
             PixelFormat f;
-            status_t res = captureScreen(dpy, &heap, &w, &h, &f);
+            status_t res = captureScreen(dpy, &heap, &w, &h, &f,
+                    reqWidth, reqHeight);
             reply->writeStrongBinder(heap->asBinder());
             reply->writeInt32(w);
             reply->writeInt32(h);
diff --git a/libs/surfaceflinger_client/SurfaceComposerClient.cpp b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
index 4096ac6..f270461 100644
--- a/libs/surfaceflinger_client/SurfaceComposerClient.cpp
+++ b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
@@ -545,5 +545,55 @@
 }
 
 // ----------------------------------------------------------------------------
+
+ScreenshotClient::ScreenshotClient()
+    : mWidth(0), mHeight(0), mFormat(PIXEL_FORMAT_NONE) {
+}
+
+status_t ScreenshotClient::update() {
+    sp<ISurfaceComposer> s(ComposerService::getComposerService());
+    if (s == NULL) return NO_INIT;
+    mHeap = 0;
+    return s->captureScreen(0, &mHeap,
+            &mWidth, &mHeight, &mFormat, 0, 0);
+}
+
+status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight) {
+    sp<ISurfaceComposer> s(ComposerService::getComposerService());
+    if (s == NULL) return NO_INIT;
+    mHeap = 0;
+    return s->captureScreen(0, &mHeap,
+            &mWidth, &mHeight, &mFormat, reqWidth, reqHeight);
+}
+
+void ScreenshotClient::release() {
+    mHeap = 0;
+}
+
+void const* ScreenshotClient::getPixels() const {
+    return mHeap->getBase();
+}
+
+uint32_t ScreenshotClient::getWidth() const {
+    return mWidth;
+}
+
+uint32_t ScreenshotClient::getHeight() const {
+    return mHeight;
+}
+
+PixelFormat ScreenshotClient::getFormat() const {
+    return mFormat;
+}
+
+uint32_t ScreenshotClient::getStride() const {
+    return mWidth;
+}
+
+size_t ScreenshotClient::getSize() const {
+    return mHeap->getSize();
+}
+
+// ----------------------------------------------------------------------------
 }; // namespace android
 
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index f2b029a..825febc 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -33,6 +33,9 @@
 #include <math.h>
 
 #define INDENT "  "
+#define INDENT2 "    "
+#define INDENT3 "      "
+#define INDENT4 "        "
 
 namespace android {
 
@@ -63,6 +66,10 @@
     return sqrtf(x * x + y * y);
 }
 
+static inline const char* toString(bool value) {
+    return value ? "true" : "false";
+}
+
 
 int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
     int32_t mask;
@@ -236,16 +243,14 @@
     String8 name = mEventHub->getDeviceName(deviceId);
     uint32_t classes = mEventHub->getDeviceClasses(deviceId);
 
-    // Write a log message about the added device as a heading for subsequent log messages.
-    LOGI("Device added: id=0x%x, name=%s", deviceId, name.string());
-
     InputDevice* device = createDevice(deviceId, name, classes);
     device->configure();
 
     if (device->isIgnored()) {
-        LOGI(INDENT "Sources: none (device is ignored)");
+        LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", deviceId, name.string());
     } else {
-        LOGI(INDENT "Sources: 0x%08x", device->getSources());
+        LOGI("Device added: id=0x%x, name=%s, sources=%08x", deviceId, name.string(),
+                device->getSources());
     }
 
     bool added = false;
@@ -287,7 +292,6 @@
         return;
     }
 
-    // Write a log message about the removed device as a heading for subsequent log messages.
     if (device->isIgnored()) {
         LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)",
                 device->getId(), device->getName().string());
@@ -571,59 +575,15 @@
 }
 
 void InputReader::dump(String8& dump) {
-    dumpDeviceInfo(dump);
-}
+    { // acquire device registry reader lock
+        RWLock::AutoRLock _rl(mDeviceRegistryLock);
 
-static void dumpMotionRange(String8& dump,
-        const char* name, const InputDeviceInfo::MotionRange* range) {
-    if (range) {
-        dump.appendFormat("      %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n",
-                name, range->min, range->max, range->flat, range->fuzz);
-    }
-}
-
-#define DUMP_MOTION_RANGE(range) \
-    dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range));
-
-void InputReader::dumpDeviceInfo(String8& dump) {
-    Vector<int32_t> deviceIds;
-    getInputDeviceIds(deviceIds);
-
-    InputDeviceInfo deviceInfo;
-    for (size_t i = 0; i < deviceIds.size(); i++) {
-        int32_t deviceId = deviceIds[i];
-
-        status_t result = getInputDeviceInfo(deviceId, & deviceInfo);
-        if (result == NAME_NOT_FOUND) {
-            continue;
-        } else if (result != OK) {
-            dump.appendFormat("  ** Unexpected error %d getting information about input devices.\n",
-                    result);
-            continue;
+        for (size_t i = 0; i < mDevices.size(); i++) {
+            mDevices.valueAt(i)->dump(dump);
         }
-
-        dump.appendFormat("  Device %d: '%s'\n",
-                deviceInfo.getId(), deviceInfo.getName().string());
-        dump.appendFormat("    sources = 0x%08x\n",
-                deviceInfo.getSources());
-        dump.appendFormat("    keyboardType = %d\n",
-                deviceInfo.getKeyboardType());
-
-        dump.append("    motion ranges:\n");
-        DUMP_MOTION_RANGE(X);
-        DUMP_MOTION_RANGE(Y);
-        DUMP_MOTION_RANGE(PRESSURE);
-        DUMP_MOTION_RANGE(SIZE);
-        DUMP_MOTION_RANGE(TOUCH_MAJOR);
-        DUMP_MOTION_RANGE(TOUCH_MINOR);
-        DUMP_MOTION_RANGE(TOOL_MAJOR);
-        DUMP_MOTION_RANGE(TOOL_MINOR);
-        DUMP_MOTION_RANGE(ORIENTATION);
-    }
+    } // release device registy reader lock
 }
 
-#undef DUMP_MOTION_RANGE
-
 
 // --- InputReaderThread ---
 
@@ -654,6 +614,43 @@
     mMappers.clear();
 }
 
+static void dumpMotionRange(String8& dump, const InputDeviceInfo& deviceInfo,
+        int32_t rangeType, const char* name) {
+    const InputDeviceInfo::MotionRange* range = deviceInfo.getMotionRange(rangeType);
+    if (range) {
+        dump.appendFormat(INDENT3 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n",
+                name, range->min, range->max, range->flat, range->fuzz);
+    }
+}
+
+void InputDevice::dump(String8& dump) {
+    InputDeviceInfo deviceInfo;
+    getDeviceInfo(& deviceInfo);
+
+    dump.appendFormat(INDENT "Device 0x%x: %s\n", deviceInfo.getId(),
+            deviceInfo.getName().string());
+    dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
+    dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
+    if (!deviceInfo.getMotionRanges().isEmpty()) {
+        dump.append(INDENT2 "Motion Ranges:\n");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_X, "X");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_Y, "Y");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_PRESSURE, "Pressure");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_SIZE, "Size");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MAJOR, "TouchMajor");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MINOR, "TouchMinor");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MAJOR, "ToolMajor");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MINOR, "ToolMinor");
+        dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_ORIENTATION, "Orientation");
+    }
+
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        mapper->dump(dump);
+    }
+}
+
 void InputDevice::addMapper(InputMapper* mapper) {
     mMappers.add(mapper);
 }
@@ -763,6 +760,9 @@
     info->addSource(getSources());
 }
 
+void InputMapper::dump(String8& dump) {
+}
+
 void InputMapper::configure() {
 }
 
@@ -856,6 +856,19 @@
     info->setKeyboardType(mKeyboardType);
 }
 
+void KeyboardInputMapper::dump(String8& dump) {
+    { // acquire lock
+        AutoMutex _l(mLock);
+        dump.append(INDENT2 "Keyboard Input Mapper:\n");
+        dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+        dump.appendFormat(INDENT3 "Sources: 0x%x\n", mSources);
+        dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType);
+        dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mLocked.keyDowns.size());
+        dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mLocked.metaState);
+        dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
+    } // release lock
+}
+
 void KeyboardInputMapper::reset() {
     for (;;) {
         int32_t keyCode, scanCode;
@@ -980,7 +993,10 @@
     int32_t keyEventAction = down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP;
     int32_t keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM;
     if (policyFlags & POLICY_FLAG_WOKE_HERE) {
-        keyEventFlags = keyEventFlags | AKEY_EVENT_FLAG_WOKE_HERE;
+        keyEventFlags |= AKEY_EVENT_FLAG_WOKE_HERE;
+    }
+    if (policyFlags & POLICY_FLAG_VIRTUAL) {
+        keyEventFlags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
     }
 
     getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
@@ -1044,6 +1060,18 @@
     info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale);
 }
 
+void TrackballInputMapper::dump(String8& dump) {
+    { // acquire lock
+        AutoMutex _l(mLock);
+        dump.append(INDENT2 "Trackball Input Mapper:\n");
+        dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+        dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
+        dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
+        dump.appendFormat(INDENT3 "Down: %s\n", toString(mLocked.down));
+        dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
+    } // release lock
+}
+
 void TrackballInputMapper::initializeLocked() {
     mAccumulator.clear();
 
@@ -1275,6 +1303,21 @@
     } // release lock
 }
 
+void TouchInputMapper::dump(String8& dump) {
+    { // acquire lock
+        AutoMutex _l(mLock);
+        dump.append(INDENT2 "Touch Input Mapper:\n");
+        dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+        dumpParameters(dump);
+        dumpVirtualKeysLocked(dump);
+        dumpRawAxes(dump);
+        dumpCalibration(dump);
+        dumpSurfaceLocked(dump);
+        dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mLocked.xPrecision);
+        dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mLocked.yPrecision);
+    } // release lock
+}
+
 void TouchInputMapper::initializeLocked() {
     mCurrentTouch.clear();
     mLastTouch.clear();
@@ -1301,16 +1344,13 @@
 
     // Configure basic parameters.
     configureParameters();
-    logParameters();
 
     // Configure absolute axis information.
     configureRawAxes();
-    logRawAxes();
 
     // Prepare input device calibration.
     parseCalibration();
     resolveCalibration();
-    logCalibration();
 
     { // acquire lock
         AutoMutex _l(mLock);
@@ -1326,16 +1366,13 @@
     mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
 }
 
-void TouchInputMapper::logParameters() {
-    if (mParameters.useBadTouchFilter) {
-        LOGI(INDENT "Bad touch filter enabled.");
-    }
-    if (mParameters.useAveragingTouchFilter) {
-        LOGI(INDENT "Averaging touch filter enabled.");
-    }
-    if (mParameters.useJumpyTouchFilter) {
-        LOGI(INDENT "Jumpy touch filter enabled.");
-    }
+void TouchInputMapper::dumpParameters(String8& dump) {
+    dump.appendFormat(INDENT3 "UseBadTouchFilter: %s\n",
+            toString(mParameters.useBadTouchFilter));
+    dump.appendFormat(INDENT3 "UseAveragingTouchFilter: %s\n",
+            toString(mParameters.useAveragingTouchFilter));
+    dump.appendFormat(INDENT3 "UseJumpyTouchFilter: %s\n",
+            toString(mParameters.useJumpyTouchFilter));
 }
 
 void TouchInputMapper::configureRawAxes() {
@@ -1349,24 +1386,25 @@
     mRawAxes.orientation.clear();
 }
 
-static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) {
+static void dumpAxisInfo(String8& dump, RawAbsoluteAxisInfo axis, const char* name) {
     if (axis.valid) {
-        LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d",
+        dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n",
                 name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
     } else {
-        LOGI(INDENT "Raw %s axis: unknown range", name);
+        dump.appendFormat(INDENT4 "%s: unknown range\n", name);
     }
 }
 
-void TouchInputMapper::logRawAxes() {
-    logAxisInfo(mRawAxes.x, "x");
-    logAxisInfo(mRawAxes.y, "y");
-    logAxisInfo(mRawAxes.pressure, "pressure");
-    logAxisInfo(mRawAxes.touchMajor, "touchMajor");
-    logAxisInfo(mRawAxes.touchMinor, "touchMinor");
-    logAxisInfo(mRawAxes.toolMajor, "toolMajor");
-    logAxisInfo(mRawAxes.toolMinor, "toolMinor");
-    logAxisInfo(mRawAxes.orientation, "orientation");
+void TouchInputMapper::dumpRawAxes(String8& dump) {
+    dump.append(INDENT3 "Raw Axes:\n");
+    dumpAxisInfo(dump, mRawAxes.x, "X");
+    dumpAxisInfo(dump, mRawAxes.y, "Y");
+    dumpAxisInfo(dump, mRawAxes.pressure, "Pressure");
+    dumpAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor");
+    dumpAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor");
+    dumpAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor");
+    dumpAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor");
+    dumpAxisInfo(dump, mRawAxes.orientation, "Orientation");
 }
 
 bool TouchInputMapper::configureSurfaceLocked() {
@@ -1391,10 +1429,8 @@
 
     bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
     if (sizeChanged) {
-        LOGI("Device reconfigured (display size changed): id=0x%x, name=%s",
-                getDeviceId(), getDeviceName().string());
-        LOGI(INDENT "Width: %dpx", width);
-        LOGI(INDENT "Height: %dpx", height);
+        LOGI("Device reconfigured: id=0x%x, name=%s, display size is now %dx%d",
+                getDeviceId(), getDeviceName().string(), width, height);
 
         mLocked.surfaceWidth = width;
         mLocked.surfaceHeight = height;
@@ -1562,39 +1598,13 @@
         mLocked.orientedRanges.y.fuzz = orientedYScale;
     }
 
-    if (sizeChanged) {
-        logMotionRangesLocked();
-    }
-
     return true;
 }
 
-static void logMotionRangeInfo(InputDeviceInfo::MotionRange* range, const char* name) {
-    if (range) {
-        LOGI(INDENT "Output %s range: min=%f, max=%f, flat=%f, fuzz=%f",
-                name, range->min, range->max, range->flat, range->fuzz);
-    } else {
-        LOGI(INDENT "Output %s range: unsupported", name);
-    }
-}
-
-void TouchInputMapper::logMotionRangesLocked() {
-    logMotionRangeInfo(& mLocked.orientedRanges.x, "x");
-    logMotionRangeInfo(& mLocked.orientedRanges.y, "y");
-    logMotionRangeInfo(mLocked.orientedRanges.havePressure
-            ? & mLocked.orientedRanges.pressure : NULL, "pressure");
-    logMotionRangeInfo(mLocked.orientedRanges.haveSize
-            ? & mLocked.orientedRanges.size : NULL, "size");
-    logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
-            ? & mLocked.orientedRanges.touchMajor : NULL, "touchMajor");
-    logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea
-            ? & mLocked.orientedRanges.touchMinor : NULL, "touchMinor");
-    logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
-            ? & mLocked.orientedRanges.toolMajor : NULL, "toolMajor");
-    logMotionRangeInfo(mLocked.orientedRanges.haveToolArea
-            ? & mLocked.orientedRanges.toolMinor : NULL, "toolMinor");
-    logMotionRangeInfo(mLocked.orientedRanges.haveOrientation
-            ? & mLocked.orientedRanges.orientation : NULL, "orientation");
+void TouchInputMapper::dumpSurfaceLocked(String8& dump) {
+    dump.appendFormat(INDENT3 "SurfaceWidth: %dpx\n", mLocked.surfaceWidth);
+    dump.appendFormat(INDENT3 "SurfaceHeight: %dpx\n", mLocked.surfaceHeight);
+    dump.appendFormat(INDENT3 "SurfaceOrientation: %d\n", mLocked.surfaceOrientation);
 }
 
 void TouchInputMapper::configureVirtualKeysLocked() {
@@ -1651,9 +1661,21 @@
         virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
                 * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
 
-        LOGI(INDENT "VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d",
-                virtualKey.scanCode, virtualKey.keyCode,
-                virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom);
+    }
+}
+
+void TouchInputMapper::dumpVirtualKeysLocked(String8& dump) {
+    if (!mLocked.virtualKeys.isEmpty()) {
+        dump.append(INDENT3 "Virtual Keys:\n");
+
+        for (size_t i = 0; i < mLocked.virtualKeys.size(); i++) {
+            const VirtualKey& virtualKey = mLocked.virtualKeys.itemAt(i);
+            dump.appendFormat(INDENT4 "%d: scanCode=%d, keyCode=%d, "
+                    "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n",
+                    i, virtualKey.scanCode, virtualKey.keyCode,
+                    virtualKey.hitLeft, virtualKey.hitRight,
+                    virtualKey.hitTop, virtualKey.hitBottom);
+        }
     }
 }
 
@@ -1861,19 +1883,19 @@
     }
 }
 
-void TouchInputMapper::logCalibration() {
-    LOGI(INDENT "Calibration:");
+void TouchInputMapper::dumpCalibration(String8& dump) {
+    dump.append(INDENT3 "Calibration:\n");
 
     // Touch Area
     switch (mCalibration.touchAreaCalibration) {
     case Calibration::TOUCH_AREA_CALIBRATION_NONE:
-        LOGI(INDENT INDENT "touch.touchArea.calibration: none");
+        dump.append(INDENT4 "touch.touchArea.calibration: none\n");
         break;
     case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC:
-        LOGI(INDENT INDENT "touch.touchArea.calibration: geometric");
+        dump.append(INDENT4 "touch.touchArea.calibration: geometric\n");
         break;
     case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE:
-        LOGI(INDENT INDENT "touch.touchArea.calibration: pressure");
+        dump.append(INDENT4 "touch.touchArea.calibration: pressure\n");
         break;
     default:
         assert(false);
@@ -1882,40 +1904,43 @@
     // Tool Area
     switch (mCalibration.toolAreaCalibration) {
     case Calibration::TOOL_AREA_CALIBRATION_NONE:
-        LOGI(INDENT INDENT "touch.toolArea.calibration: none");
+        dump.append(INDENT4 "touch.toolArea.calibration: none\n");
         break;
     case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC:
-        LOGI(INDENT INDENT "touch.toolArea.calibration: geometric");
+        dump.append(INDENT4 "touch.toolArea.calibration: geometric\n");
         break;
     case Calibration::TOOL_AREA_CALIBRATION_LINEAR:
-        LOGI(INDENT INDENT "touch.toolArea.calibration: linear");
+        dump.append(INDENT4 "touch.toolArea.calibration: linear\n");
         break;
     default:
         assert(false);
     }
 
     if (mCalibration.haveToolAreaLinearScale) {
-        LOGI(INDENT INDENT "touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale);
+        dump.appendFormat(INDENT4 "touch.toolArea.linearScale: %0.3f\n",
+                mCalibration.toolAreaLinearScale);
     }
 
     if (mCalibration.haveToolAreaLinearBias) {
-        LOGI(INDENT INDENT "touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias);
+        dump.appendFormat(INDENT4 "touch.toolArea.linearBias: %0.3f\n",
+                mCalibration.toolAreaLinearBias);
     }
 
     if (mCalibration.haveToolAreaIsSummed) {
-        LOGI(INDENT INDENT "touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed);
+        dump.appendFormat(INDENT4 "touch.toolArea.isSummed: %d\n",
+                mCalibration.toolAreaIsSummed);
     }
 
     // Pressure
     switch (mCalibration.pressureCalibration) {
     case Calibration::PRESSURE_CALIBRATION_NONE:
-        LOGI(INDENT INDENT "touch.pressure.calibration: none");
+        dump.append(INDENT4 "touch.pressure.calibration: none\n");
         break;
     case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
-        LOGI(INDENT INDENT "touch.pressure.calibration: physical");
+        dump.append(INDENT4 "touch.pressure.calibration: physical\n");
         break;
     case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
-        LOGI(INDENT INDENT "touch.pressure.calibration: amplitude");
+        dump.append(INDENT4 "touch.pressure.calibration: amplitude\n");
         break;
     default:
         assert(false);
@@ -1923,10 +1948,10 @@
 
     switch (mCalibration.pressureSource) {
     case Calibration::PRESSURE_SOURCE_PRESSURE:
-        LOGI(INDENT INDENT "touch.pressure.source: pressure");
+        dump.append(INDENT4 "touch.pressure.source: pressure\n");
         break;
     case Calibration::PRESSURE_SOURCE_TOUCH:
-        LOGI(INDENT INDENT "touch.pressure.source: touch");
+        dump.append(INDENT4 "touch.pressure.source: touch\n");
         break;
     case Calibration::PRESSURE_SOURCE_DEFAULT:
         break;
@@ -1935,16 +1960,17 @@
     }
 
     if (mCalibration.havePressureScale) {
-        LOGI(INDENT INDENT "touch.pressure.scale: %f", mCalibration.pressureScale);
+        dump.appendFormat(INDENT4 "touch.pressure.scale: %0.3f\n",
+                mCalibration.pressureScale);
     }
 
     // Size
     switch (mCalibration.sizeCalibration) {
     case Calibration::SIZE_CALIBRATION_NONE:
-        LOGI(INDENT INDENT "touch.size.calibration: none");
+        dump.append(INDENT4 "touch.size.calibration: none\n");
         break;
     case Calibration::SIZE_CALIBRATION_NORMALIZED:
-        LOGI(INDENT INDENT "touch.size.calibration: normalized");
+        dump.append(INDENT4 "touch.size.calibration: normalized\n");
         break;
     default:
         assert(false);
@@ -1953,10 +1979,10 @@
     // Orientation
     switch (mCalibration.orientationCalibration) {
     case Calibration::ORIENTATION_CALIBRATION_NONE:
-        LOGI(INDENT INDENT "touch.orientation.calibration: none");
+        dump.append(INDENT4 "touch.orientation.calibration: none\n");
         break;
     case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
-        LOGI(INDENT INDENT "touch.orientation.calibration: interpolated");
+        dump.append(INDENT4 "touch.orientation.calibration: interpolated\n");
         break;
     default:
         assert(false);
@@ -2139,10 +2165,7 @@
         int32_t keyCode, int32_t scanCode, nsecs_t downTime) {
     int32_t metaState = mContext->getGlobalMetaState();
 
-    if (keyEventAction == AKEY_EVENT_ACTION_DOWN) {
-        getPolicy()->virtualKeyDownFeedback();
-    }
-
+    policyFlags |= POLICY_FLAG_VIRTUAL;
     int32_t policyActions = getPolicy()->interceptKey(when, getDeviceId(),
             keyEventAction == AKEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags);
 
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 890786e..587c8ff 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -316,6 +316,7 @@
         mNewPosition = mCblk->server + mUpdatePeriod;
         mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
         mCblk->waitTimeMs = 0;
+        mCblk->flags &= ~CBLK_DISABLED_ON;
         if (t != 0) {
            t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);
         } else {
@@ -842,6 +843,13 @@
         cblk->lock.unlock();
     }
 
+    // restart track if it was disabled by audioflinger due to previous underrun
+    if (cblk->flags & CBLK_DISABLED_MSK) {
+        cblk->flags &= ~CBLK_DISABLED_ON;
+        LOGW("obtainBuffer() track %p disabled, restarting", this);
+        mAudioTrack->start();
+    }
+
     cblk->waitTimeMs = 0;
 
     if (framesReq > framesAvail) {
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 97c9003..8e50d39 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -276,20 +276,6 @@
 
 status_t AwesomePlayer::setDataSource(
         int fd, int64_t offset, int64_t length) {
-#if 0
-    // return setDataSource("httplive://qthttp.apple.com.edgesuite.net/1009qpeijrfn/sl.m3u8");
-    return setDataSource("httplive://qthttp.apple.com.edgesuite.net/1009qpeijrfn/0440.m3u8");
-    // return setDataSource("httplive://qthttp.apple.com.edgesuite.net/1009qpeijrfn/0640.m3u8");
-    // return setDataSource("httplive://qthttp.apple.com.edgesuite.net/1009qpeijrfn/1240_vod.m3u8");
-    // return setDataSource("httplive://iphoned5.akamai.com.edgesuite.net/mhbarron/nasatv/nasatv_96.m3u8");
-    // return setDataSource("httplive://iphoned5.akamai.com.edgesuite.net/mhbarron/nasatv/nasatv_1500.m3u8");
-    // return setDataSource("httplive://iphone.video.hsn.com/iPhone_high.m3u8");
-    // return setDataSource("httplive://iphoned5.akamai.com.edgesuite.net/mhbarron/iphonewebcast/webcast090209_all/webcast090209_all.m3u8");
-    // return setDataSource("httplive://qthttp.akamai.com.edgesuite.net/iphone_demo/Video_Content/usat/tt_062209_iphone/hi/prog_index.m3u8");
-    // return setDataSource("httplive://qthttp.akamai.com.edgesuite.net/iphone_demo/Video_Content/usat/tt_googmaps/hi/prog_index.m3u8");
-    // return setDataSource("httplive://qthttp.akamai.com.edgesuite.net/iphone_demo/Video_Content/mtv/ni_spo_25a_rt74137_clip_syn/hi/prog_index.m3u8");
-#endif
-
     Mutex::Autolock autoLock(mLock);
 
     reset_l();
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index e6c2f7e..546df47 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1118,7 +1118,7 @@
         startTimeUs = 0;
     }
 
-    mIsRealTimeRecording = true;
+    mIsRealTimeRecording = false;
     {
         int32_t isNotRealTime;
         if (params && params->findInt32(kKeyNotRealTime, &isNotRealTime)) {
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index 511ae12..a8f1104 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -21,7 +21,7 @@
 
 namespace android {
 
-static unsigned parseUE(ABitReader *br) {
+unsigned parseUE(ABitReader *br) {
     unsigned numZeroes = 0;
     while (br->getBits(1) == 0) {
         ++numZeroes;
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
index 3c0b736..868c514 100644
--- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -31,6 +31,7 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/hexdump.h>
 
 namespace android {
 
diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp
index 001afc4..9103927 100644
--- a/media/libstagefright/httplive/LiveSource.cpp
+++ b/media/libstagefright/httplive/LiveSource.cpp
@@ -93,7 +93,7 @@
         }
 
         if (mLastFetchTimeUs < 0) {
-            mPlaylistIndex = mPlaylist->size() / 2;
+            mPlaylistIndex = 0;
         } else {
             if (nextSequenceNumber < mFirstItemSequenceNumber
                     || nextSequenceNumber
diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h
index cc405b5..6602852 100644
--- a/media/libstagefright/include/avc_utils.h
+++ b/media/libstagefright/include/avc_utils.h
@@ -22,9 +22,13 @@
 
 namespace android {
 
+struct ABitReader;
+
 void FindAVCDimensions(
         const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height);
 
+unsigned parseUE(ABitReader *br);
+
 }  // namespace android
 
 #endif  // AVC_UTILS_H_
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 47cca80..bcaab9f 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -21,6 +21,7 @@
 #include "ATSParser.h"
 
 #include "AnotherPacketSource.h"
+#include "ESQueue.h"
 #include "include/avc_utils.h"
 
 #include <media/stagefright/foundation/ABitReader.h>
@@ -79,6 +80,8 @@
     sp<AnotherPacketSource> mSource;
     bool mPayloadStarted;
 
+    ElementaryStreamQueue mQueue;
+
     void flush();
     void parsePES(ABitReader *br);
 
@@ -232,7 +235,9 @@
     : mElementaryPID(elementaryPID),
       mStreamType(streamType),
       mBuffer(new ABuffer(128 * 1024)),
-      mPayloadStarted(false) {
+      mPayloadStarted(false),
+      mQueue(streamType == 0x1b
+              ? ElementaryStreamQueue::H264 : ElementaryStreamQueue::AAC) {
     mBuffer->setRange(0, 0);
 }
 
@@ -433,373 +438,31 @@
     mBuffer->setRange(0, 0);
 }
 
-static sp<ABuffer> FindNAL(
-        const uint8_t *data, size_t size, unsigned nalType,
-        size_t *stopOffset) {
-    bool foundStart = false;
-    size_t startOffset = 0;
-
-    size_t offset = 0;
-    for (;;) {
-        while (offset + 3 < size
-                && memcmp("\x00\x00\x00\x01", &data[offset], 4)) {
-            ++offset;
-        }
-
-        if (foundStart) {
-            size_t nalSize;
-            if (offset + 3 >= size) {
-                nalSize = size - startOffset;
-            } else {
-                nalSize = offset - startOffset;
-            }
-
-            sp<ABuffer> nal = new ABuffer(nalSize);
-            memcpy(nal->data(), &data[startOffset], nalSize);
-
-            if (stopOffset != NULL) {
-                *stopOffset = startOffset + nalSize;
-            }
-
-            return nal;
-        }
-
-        if (offset + 4 >= size) {
-            return NULL;
-        }
-
-        if ((data[offset + 4] & 0x1f) == nalType) {
-            foundStart = true;
-            startOffset = offset + 4;
-        }
-
-        offset += 4;
-    }
-}
-
-static sp<ABuffer> MakeAVCCodecSpecificData(
-        const sp<ABuffer> &buffer, int32_t *width, int32_t *height) {
-    const uint8_t *data = buffer->data();
-    size_t size = buffer->size();
-
-    sp<ABuffer> seqParamSet = FindNAL(data, size, 7, NULL);
-    if (seqParamSet == NULL) {
-        return NULL;
-    }
-
-    FindAVCDimensions(seqParamSet, width, height);
-
-    size_t stopOffset;
-    sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
-    CHECK(picParamSet != NULL);
-
-    buffer->setRange(stopOffset, size - stopOffset);
-    LOGV("buffer has %d bytes left.", buffer->size());
-
-    size_t csdSize =
-        1 + 3 + 1 + 1
-        + 2 * 1 + seqParamSet->size()
-        + 1 + 2 * 1 + picParamSet->size();
-
-    sp<ABuffer> csd = new ABuffer(csdSize);
-    uint8_t *out = csd->data();
-
-    *out++ = 0x01;  // configurationVersion
-    memcpy(out, seqParamSet->data() + 1, 3);  // profile/level...
-    out += 3;
-    *out++ = (0x3f << 2) | 1;  // lengthSize == 2 bytes
-    *out++ = 0xe0 | 1;
-
-    *out++ = seqParamSet->size() >> 8;
-    *out++ = seqParamSet->size() & 0xff;
-    memcpy(out, seqParamSet->data(), seqParamSet->size());
-    out += seqParamSet->size();
-
-    *out++ = 1;
-
-    *out++ = picParamSet->size() >> 8;
-    *out++ = picParamSet->size() & 0xff;
-    memcpy(out, picParamSet->data(), picParamSet->size());
-
-    return csd;
-}
-
-static bool getNextNALUnit(
-        const uint8_t **_data, size_t *_size,
-        const uint8_t **nalStart, size_t *nalSize) {
-    const uint8_t *data = *_data;
-    size_t size = *_size;
-
-    // hexdump(data, size);
-
-    *nalStart = NULL;
-    *nalSize = 0;
-
-    if (size == 0) {
-        return false;
-    }
-
-    size_t offset = 0;
-    for (;;) {
-        CHECK_LT(offset + 2, size);
-
-        if (!memcmp("\x00\x00\x01", &data[offset], 3)) {
-            break;
-        }
-
-        CHECK_EQ((unsigned)data[offset], 0x00u);
-        ++offset;
-    }
-
-    offset += 3;
-    size_t startOffset = offset;
-
-    while (offset + 2 < size
-            && memcmp("\x00\x00\x00", &data[offset], 3)
-            && memcmp("\x00\x00\x01", &data[offset], 3)) {
-        ++offset;
-    }
-
-    if (offset + 2 >= size) {
-        *nalStart = &data[startOffset];
-        *nalSize = size - startOffset;
-
-        *_data = NULL;
-        *_size = 0;
-
-        return true;
-    }
-
-    size_t endOffset = offset;
-
-    while (offset + 2 < size && memcmp("\x00\x00\x01", &data[offset], 3)) {
-        CHECK_EQ((unsigned)data[offset], 0x00u);
-        ++offset;
-    }
-
-    *nalStart = &data[startOffset];
-    *nalSize = endOffset - startOffset;
-
-    if (offset + 2 < size) {
-        *_data = &data[offset];
-        *_size = size - offset;
-    } else {
-        *_data = NULL;
-        *_size = 0;
-    }
-
-    return true;
-}
-
-sp<ABuffer> MakeCleanAVCData(const uint8_t *data, size_t size) {
-    // hexdump(data, size);
-
-    const uint8_t *tmpData = data;
-    size_t tmpSize = size;
-
-    size_t totalSize = 0;
-    const uint8_t *nalStart;
-    size_t nalSize;
-    while (getNextNALUnit(&tmpData, &tmpSize, &nalStart, &nalSize)) {
-        // hexdump(nalStart, nalSize);
-        totalSize += 4 + nalSize;
-    }
-
-    sp<ABuffer> buffer = new ABuffer(totalSize);
-    size_t offset = 0;
-    while (getNextNALUnit(&data, &size, &nalStart, &nalSize)) {
-        memcpy(buffer->data() + offset, "\x00\x00\x00\x01", 4);
-        memcpy(buffer->data() + offset + 4, nalStart, nalSize);
-
-        offset += 4 + nalSize;
-    }
-
-    return buffer;
-}
-
-static sp<ABuffer> FindMPEG2ADTSConfig(
-        const sp<ABuffer> &buffer, int32_t *sampleRate, int32_t *channelCount) {
-    ABitReader br(buffer->data(), buffer->size());
-
-    CHECK_EQ(br.getBits(12), 0xfffu);
-    CHECK_EQ(br.getBits(1), 0u);
-    CHECK_EQ(br.getBits(2), 0u);
-    br.getBits(1);  // protection_absent
-    unsigned profile = br.getBits(2);
-    LOGV("profile = %u", profile);
-    CHECK_NE(profile, 3u);
-    unsigned sampling_freq_index = br.getBits(4);
-    br.getBits(1);  // private_bit
-    unsigned channel_configuration = br.getBits(3);
-    CHECK_NE(channel_configuration, 0u);
-
-    LOGV("sampling_freq_index = %u", sampling_freq_index);
-    LOGV("channel_configuration = %u", channel_configuration);
-
-    CHECK_LE(sampling_freq_index, 11u);
-    static const int32_t kSamplingFreq[] = {
-        96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
-        16000, 12000, 11025, 8000
-    };
-    *sampleRate = kSamplingFreq[sampling_freq_index];
-
-    *channelCount = channel_configuration;
-
-    static const uint8_t kStaticESDS[] = {
-        0x03, 22,
-        0x00, 0x00,     // ES_ID
-        0x00,           // streamDependenceFlag, URL_Flag, OCRstreamFlag
-
-        0x04, 17,
-        0x40,                       // Audio ISO/IEC 14496-3
-        0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00,
-
-        0x05, 2,
-        // AudioSpecificInfo follows
-
-        // oooo offf fccc c000
-        // o - audioObjectType
-        // f - samplingFreqIndex
-        // c - channelConfig
-    };
-    sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
-    memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
-
-    csd->data()[sizeof(kStaticESDS)] =
-        ((profile + 1) << 3) | (sampling_freq_index >> 1);
-
-    csd->data()[sizeof(kStaticESDS) + 1] =
-        ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
-
-    // hexdump(csd->data(), csd->size());
-    return csd;
-}
-
 void ATSParser::Stream::onPayloadData(
         unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
         const uint8_t *data, size_t size) {
     LOGV("onPayloadData mStreamType=0x%02x", mStreamType);
 
-    sp<ABuffer> buffer;
-
-    if (mStreamType == 0x1b) {
-        buffer = MakeCleanAVCData(data, size);
-    } else {
-        // hexdump(data, size);
-
-        buffer = new ABuffer(size);
-        memcpy(buffer->data(), data, size);
-    }
-
-    if (mSource == NULL) {
-        sp<MetaData> meta = new MetaData;
-
-        if (mStreamType == 0x1b) {
-            meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
-
-            int32_t width, height;
-            sp<ABuffer> csd = MakeAVCCodecSpecificData(buffer, &width, &height);
-
-            if (csd == NULL) {
-                return;
-            }
-
-            meta->setData(kKeyAVCC, 0, csd->data(), csd->size());
-            meta->setInt32(kKeyWidth, width);
-            meta->setInt32(kKeyHeight, height);
-        } else {
-            CHECK_EQ(mStreamType, 0x0fu);
-
-            meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
-
-            int32_t sampleRate, channelCount;
-            sp<ABuffer> csd =
-                FindMPEG2ADTSConfig(buffer, &sampleRate, &channelCount);
-
-            LOGV("sampleRate = %d", sampleRate);
-            LOGV("channelCount = %d", channelCount);
-
-            meta->setInt32(kKeySampleRate, sampleRate);
-            meta->setInt32(kKeyChannelCount, channelCount);
-
-            meta->setData(kKeyESDS, 0, csd->data(), csd->size());
-        }
-
-        LOGV("created source!");
-        mSource = new AnotherPacketSource(meta);
-
-        // fall through
-    }
-
     CHECK(PTS_DTS_flags == 2 || PTS_DTS_flags == 3);
-    buffer->meta()->setInt64("time", (PTS * 100) / 9);
+    int64_t timeUs = (PTS * 100) / 9;
 
-    if (mStreamType == 0x0f) {
-        extractAACFrames(buffer);
-    }
+    status_t err = mQueue.appendData(data, size, timeUs);
+    CHECK_EQ(err, (status_t)OK);
 
-    mSource->queueAccessUnit(buffer);
-}
+    sp<ABuffer> accessUnit;
+    while ((accessUnit = mQueue.dequeueAccessUnit()) != NULL) {
+        if (mSource == NULL) {
+            sp<MetaData> meta = mQueue.getFormat();
 
-// Disassemble one or more ADTS frames into their constituent parts and
-// leave only the concatenated raw_data_blocks in the buffer.
-void ATSParser::Stream::extractAACFrames(const sp<ABuffer> &buffer) {
-    size_t dstOffset = 0;
-
-    size_t offset = 0;
-    while (offset < buffer->size()) {
-        CHECK_LE(offset + 7, buffer->size());
-
-        ABitReader bits(buffer->data() + offset, buffer->size() - offset);
-
-        // adts_fixed_header
-
-        CHECK_EQ(bits.getBits(12), 0xfffu);
-        bits.skipBits(3);  // ID, layer
-        bool protection_absent = bits.getBits(1) != 0;
-
-        // profile_ObjectType, sampling_frequency_index, private_bits,
-        // channel_configuration, original_copy, home
-        bits.skipBits(12);
-
-        // adts_variable_header
-
-        // copyright_identification_bit, copyright_identification_start
-        bits.skipBits(2);
-
-        unsigned aac_frame_length = bits.getBits(13);
-
-        bits.skipBits(11);  // adts_buffer_fullness
-
-        unsigned number_of_raw_data_blocks_in_frame = bits.getBits(2);
-
-        if (number_of_raw_data_blocks_in_frame == 0) {
-            size_t scan = offset + aac_frame_length;
-
-            offset += 7;
-            if (!protection_absent) {
-                offset += 2;
+            if (meta != NULL) {
+                LOGV("created source!");
+                mSource = new AnotherPacketSource(meta);
+                mSource->queueAccessUnit(accessUnit);
             }
-
-            CHECK_LE(scan, buffer->size());
-
-            LOGV("found aac raw data block at [0x%08x ; 0x%08x)", offset, scan);
-
-            memmove(&buffer->data()[dstOffset], &buffer->data()[offset],
-                    scan - offset);
-
-            dstOffset += scan - offset;
-            offset = scan;
         } else {
-            // To be implemented.
-            TRESPASS();
+            mSource->queueAccessUnit(accessUnit);
         }
     }
-    CHECK_EQ(offset, buffer->size());
-
-    buffer->setRange(buffer->offset(), dstOffset);
 }
 
 sp<MediaSource> ATSParser::Stream::getSource(SourceType type) {
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index 3544b4c..4dfc0f7 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -5,6 +5,7 @@
 LOCAL_SRC_FILES:=                 \
         AnotherPacketSource.cpp   \
         ATSParser.cpp             \
+        ESQueue.cpp               \
         MPEG2TSExtractor.cpp      \
 
 LOCAL_C_INCLUDES:= \
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
new file mode 100644
index 0000000..d87040b
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -0,0 +1,504 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "ESQueue"
+#include <media/stagefright/foundation/ADebug.h>
+
+#include "ESQueue.h"
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+
+#include "include/avc_utils.h"
+
+namespace android {
+
+ElementaryStreamQueue::ElementaryStreamQueue(Mode mode)
+    : mMode(mode) {
+}
+
+sp<MetaData> ElementaryStreamQueue::getFormat() {
+    return mFormat;
+}
+
+static status_t getNextNALUnit(
+        const uint8_t **_data, size_t *_size,
+        const uint8_t **nalStart, size_t *nalSize,
+        bool startCodeFollows = false) {
+    const uint8_t *data = *_data;
+    size_t size = *_size;
+
+    *nalStart = NULL;
+    *nalSize = 0;
+
+    if (size == 0) {
+        return -EAGAIN;
+    }
+
+    // Skip any number of leading 0x00.
+
+    size_t offset = 0;
+    while (offset < size && data[offset] == 0x00) {
+        ++offset;
+    }
+
+    if (offset == size) {
+        return -EAGAIN;
+    }
+
+    // A valid startcode consists of at least two 0x00 bytes followed by 0x01.
+
+    if (offset < 2 || data[offset] != 0x01) {
+        return ERROR_MALFORMED;
+    }
+
+    ++offset;
+
+    size_t startOffset = offset;
+
+    for (;;) {
+        while (offset < size && data[offset] != 0x01) {
+            ++offset;
+        }
+
+        if (offset == size) {
+            if (startCodeFollows) {
+                offset = size + 2;
+                break;
+            }
+
+            return -EAGAIN;
+        }
+
+        if (data[offset - 1] == 0x00 && data[offset - 2] == 0x00) {
+            break;
+        }
+
+        ++offset;
+    }
+
+    size_t endOffset = offset - 2;
+    while (data[endOffset - 1] == 0x00) {
+        --endOffset;
+    }
+
+    *nalStart = &data[startOffset];
+    *nalSize = endOffset - startOffset;
+
+    if (offset + 2 < size) {
+        *_data = &data[offset - 2];
+        *_size = size - offset + 2;
+    } else {
+        *_data = NULL;
+        *_size = 0;
+    }
+
+    return OK;
+}
+
+status_t ElementaryStreamQueue::appendData(
+        const void *data, size_t size, int64_t timeUs) {
+    if (mBuffer == NULL || mBuffer->size() == 0) {
+        switch (mMode) {
+            case H264:
+            {
+                if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
+                    return ERROR_MALFORMED;
+                }
+                break;
+            }
+
+            case AAC:
+            {
+                uint8_t *ptr = (uint8_t *)data;
+
+                if (size < 2 || ptr[0] != 0xff || (ptr[1] >> 4) != 0x0f) {
+                    return ERROR_MALFORMED;
+                }
+                break;
+            }
+
+            default:
+                TRESPASS();
+                break;
+        }
+    }
+
+    size_t neededSize = (mBuffer == NULL ? 0 : mBuffer->size()) + size;
+    if (mBuffer == NULL || neededSize > mBuffer->capacity()) {
+        neededSize = (neededSize + 65535) & ~65535;
+
+        LOGI("resizing buffer to size %d", neededSize);
+
+        sp<ABuffer> buffer = new ABuffer(neededSize);
+        if (mBuffer != NULL) {
+            memcpy(buffer->data(), mBuffer->data(), mBuffer->size());
+            buffer->setRange(0, mBuffer->size());
+        } else {
+            buffer->setRange(0, 0);
+        }
+
+        mBuffer = buffer;
+    }
+
+    memcpy(mBuffer->data() + mBuffer->size(), data, size);
+    mBuffer->setRange(0, mBuffer->size() + size);
+
+    mTimestamps.push_back(timeUs);
+
+    return OK;
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnit() {
+    if (mMode == H264) {
+        return dequeueAccessUnitH264();
+    } else {
+        CHECK_EQ((unsigned)mMode, (unsigned)AAC);
+        return dequeueAccessUnitAAC();
+    }
+}
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() {
+    Vector<size_t> frameOffsets;
+    Vector<size_t> frameSizes;
+    size_t auSize = 0;
+
+    size_t offset = 0;
+    while (offset + 7 <= mBuffer->size()) {
+        ABitReader bits(mBuffer->data() + offset, mBuffer->size() - offset);
+
+        // adts_fixed_header
+
+        CHECK_EQ(bits.getBits(12), 0xfffu);
+        bits.skipBits(3);  // ID, layer
+        bool protection_absent = bits.getBits(1) != 0;
+
+        if (mFormat == NULL) {
+            unsigned profile = bits.getBits(2);
+            CHECK_NE(profile, 3u);
+            unsigned sampling_freq_index = bits.getBits(4);
+            bits.getBits(1);  // private_bit
+            unsigned channel_configuration = bits.getBits(3);
+            CHECK_NE(channel_configuration, 0u);
+            bits.skipBits(2);  // original_copy, home
+
+            mFormat = MakeAACCodecSpecificData(
+                    profile, sampling_freq_index, channel_configuration);
+        } else {
+            // profile_ObjectType, sampling_frequency_index, private_bits,
+            // channel_configuration, original_copy, home
+            bits.skipBits(12);
+        }
+
+        // adts_variable_header
+
+        // copyright_identification_bit, copyright_identification_start
+        bits.skipBits(2);
+
+        unsigned aac_frame_length = bits.getBits(13);
+
+        bits.skipBits(11);  // adts_buffer_fullness
+
+        unsigned number_of_raw_data_blocks_in_frame = bits.getBits(2);
+
+        if (number_of_raw_data_blocks_in_frame != 0) {
+            // To be implemented.
+            TRESPASS();
+        }
+
+        if (offset + aac_frame_length > mBuffer->size()) {
+            break;
+        }
+
+        size_t headerSize = protection_absent ? 7 : 9;
+
+        frameOffsets.push(offset + headerSize);
+        frameSizes.push(aac_frame_length - headerSize);
+        auSize += aac_frame_length - headerSize;
+
+        offset += aac_frame_length;
+    }
+
+    if (offset == 0) {
+        return NULL;
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(auSize);
+    size_t dstOffset = 0;
+    for (size_t i = 0; i < frameOffsets.size(); ++i) {
+        memcpy(accessUnit->data() + dstOffset,
+               mBuffer->data() + frameOffsets.itemAt(i),
+               frameSizes.itemAt(i));
+
+        dstOffset += frameSizes.itemAt(i);
+    }
+
+    memmove(mBuffer->data(), mBuffer->data() + offset,
+            mBuffer->size() - offset);
+    mBuffer->setRange(0, mBuffer->size() - offset);
+
+    CHECK_GT(mTimestamps.size(), 0u);
+    int64_t timeUs = *mTimestamps.begin();
+    mTimestamps.erase(mTimestamps.begin());
+
+    accessUnit->meta()->setInt64("time", timeUs);
+
+    return accessUnit;
+}
+
+// static
+sp<MetaData> ElementaryStreamQueue::MakeAACCodecSpecificData(
+        unsigned profile, unsigned sampling_freq_index,
+        unsigned channel_configuration) {
+    sp<MetaData> meta = new MetaData;
+    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+    CHECK_LE(sampling_freq_index, 11u);
+    static const int32_t kSamplingFreq[] = {
+        96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+        16000, 12000, 11025, 8000
+    };
+    meta->setInt32(kKeySampleRate, kSamplingFreq[sampling_freq_index]);
+    meta->setInt32(kKeyChannelCount, channel_configuration);
+
+    static const uint8_t kStaticESDS[] = {
+        0x03, 22,
+        0x00, 0x00,     // ES_ID
+        0x00,           // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+        0x04, 17,
+        0x40,                       // Audio ISO/IEC 14496-3
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+
+        0x05, 2,
+        // AudioSpecificInfo follows
+
+        // oooo offf fccc c000
+        // o - audioObjectType
+        // f - samplingFreqIndex
+        // c - channelConfig
+    };
+    sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
+    memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
+
+    csd->data()[sizeof(kStaticESDS)] =
+        ((profile + 1) << 3) | (sampling_freq_index >> 1);
+
+    csd->data()[sizeof(kStaticESDS) + 1] =
+        ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
+
+    meta->setData(kKeyESDS, 0, csd->data(), csd->size());
+
+    return meta;
+}
+
+struct NALPosition {
+    size_t nalOffset;
+    size_t nalSize;
+};
+
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() {
+    const uint8_t *data = mBuffer->data();
+    size_t size = mBuffer->size();
+
+    Vector<NALPosition> nals;
+
+    size_t totalSize = 0;
+
+    status_t err;
+    const uint8_t *nalStart;
+    size_t nalSize;
+    bool foundSlice = false;
+    while ((err = getNextNALUnit(&data, &size, &nalStart, &nalSize)) == OK) {
+        CHECK_GT(nalSize, 0u);
+
+        unsigned nalType = nalStart[0] & 0x1f;
+        bool flush = false;
+
+        if (nalType == 1 || nalType == 5) {
+            if (foundSlice) {
+                ABitReader br(nalStart + 1, nalSize);
+                unsigned first_mb_in_slice = parseUE(&br);
+
+                if (first_mb_in_slice == 0) {
+                    // This slice starts a new frame.
+
+                    flush = true;
+                }
+            }
+
+            foundSlice = true;
+        } else if ((nalType == 9 || nalType == 7) && foundSlice) {
+            // Access unit delimiter and SPS will be associated with the
+            // next frame.
+
+            flush = true;
+        }
+
+        if (flush) {
+            // The access unit will contain all nal units up to, but excluding
+            // the current one, separated by 0x00 0x00 0x00 0x01 startcodes.
+
+            size_t auSize = 4 * nals.size() + totalSize;
+            sp<ABuffer> accessUnit = new ABuffer(auSize);
+
+#if !LOG_NDEBUG
+            AString out;
+#endif
+
+            size_t dstOffset = 0;
+            for (size_t i = 0; i < nals.size(); ++i) {
+                const NALPosition &pos = nals.itemAt(i);
+
+                unsigned nalType = mBuffer->data()[pos.nalOffset] & 0x1f;
+
+#if !LOG_NDEBUG
+                char tmp[128];
+                sprintf(tmp, "0x%02x", nalType);
+                if (i > 0) {
+                    out.append(", ");
+                }
+                out.append(tmp);
+#endif
+
+                memcpy(accessUnit->data() + dstOffset, "\x00\x00\x00\x01", 4);
+
+                memcpy(accessUnit->data() + dstOffset + 4,
+                       mBuffer->data() + pos.nalOffset,
+                       pos.nalSize);
+
+                dstOffset += pos.nalSize + 4;
+            }
+
+            LOGV("accessUnit contains nal types %s", out.c_str());
+
+            const NALPosition &pos = nals.itemAt(nals.size() - 1);
+            size_t nextScan = pos.nalOffset + pos.nalSize;
+
+            memmove(mBuffer->data(),
+                    mBuffer->data() + nextScan,
+                    mBuffer->size() - nextScan);
+
+            mBuffer->setRange(0, mBuffer->size() - nextScan);
+
+            CHECK_GT(mTimestamps.size(), 0u);
+            int64_t timeUs = *mTimestamps.begin();
+            mTimestamps.erase(mTimestamps.begin());
+
+            accessUnit->meta()->setInt64("time", timeUs);
+
+            if (mFormat == NULL) {
+                mFormat = MakeAVCCodecSpecificData(accessUnit);
+            }
+
+            return accessUnit;
+        }
+
+        NALPosition pos;
+        pos.nalOffset = nalStart - mBuffer->data();
+        pos.nalSize = nalSize;
+
+        nals.push(pos);
+
+        totalSize += nalSize;
+    }
+    CHECK_EQ(err, (status_t)-EAGAIN);
+
+    return NULL;
+}
+
+static sp<ABuffer> FindNAL(
+        const uint8_t *data, size_t size, unsigned nalType,
+        size_t *stopOffset) {
+    const uint8_t *nalStart;
+    size_t nalSize;
+    while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
+        if ((nalStart[0] & 0x1f) == nalType) {
+            sp<ABuffer> buffer = new ABuffer(nalSize);
+            memcpy(buffer->data(), nalStart, nalSize);
+            return buffer;
+        }
+    }
+
+    return NULL;
+}
+
+sp<MetaData> ElementaryStreamQueue::MakeAVCCodecSpecificData(
+        const sp<ABuffer> &accessUnit) {
+    const uint8_t *data = accessUnit->data();
+    size_t size = accessUnit->size();
+
+    sp<ABuffer> seqParamSet = FindNAL(data, size, 7, NULL);
+    if (seqParamSet == NULL) {
+        return NULL;
+    }
+
+    int32_t width, height;
+    FindAVCDimensions(seqParamSet, &width, &height);
+
+    size_t stopOffset;
+    sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
+    CHECK(picParamSet != NULL);
+
+    size_t csdSize =
+        1 + 3 + 1 + 1
+        + 2 * 1 + seqParamSet->size()
+        + 1 + 2 * 1 + picParamSet->size();
+
+    sp<ABuffer> csd = new ABuffer(csdSize);
+    uint8_t *out = csd->data();
+
+    *out++ = 0x01;  // configurationVersion
+    memcpy(out, seqParamSet->data() + 1, 3);  // profile/level...
+    out += 3;
+    *out++ = (0x3f << 2) | 1;  // lengthSize == 2 bytes
+    *out++ = 0xe0 | 1;
+
+    *out++ = seqParamSet->size() >> 8;
+    *out++ = seqParamSet->size() & 0xff;
+    memcpy(out, seqParamSet->data(), seqParamSet->size());
+    out += seqParamSet->size();
+
+    *out++ = 1;
+
+    *out++ = picParamSet->size() >> 8;
+    *out++ = picParamSet->size() & 0xff;
+    memcpy(out, picParamSet->data(), picParamSet->size());
+
+#if 0
+    LOGI("AVC seq param set");
+    hexdump(seqParamSet->data(), seqParamSet->size());
+#endif
+
+    sp<MetaData> meta = new MetaData;
+    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+
+    meta->setData(kKeyAVCC, 0, csd->data(), csd->size());
+    meta->setInt32(kKeyWidth, width);
+    meta->setInt32(kKeyHeight, height);
+
+    return meta;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
new file mode 100644
index 0000000..d2e87f2
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -0,0 +1,66 @@
+/*
+ * 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 ES_QUEUE_H_
+
+#define ES_QUEUE_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/List.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+struct MetaData;
+
+struct ElementaryStreamQueue {
+    enum Mode {
+        H264,
+        AAC
+    };
+    ElementaryStreamQueue(Mode mode);
+
+    status_t appendData(const void *data, size_t size, int64_t timeUs);
+
+    sp<ABuffer> dequeueAccessUnit();
+
+    sp<MetaData> getFormat();
+
+private:
+    Mode mMode;
+
+    sp<ABuffer> mBuffer;
+    List<int64_t> mTimestamps;
+
+    sp<MetaData> mFormat;
+
+    sp<ABuffer> dequeueAccessUnitH264();
+    sp<ABuffer> dequeueAccessUnitAAC();
+
+    static sp<MetaData> MakeAACCodecSpecificData(
+            unsigned profile, unsigned sampling_freq_index,
+            unsigned channel_configuration);
+
+    static sp<MetaData> MakeAVCCodecSpecificData(
+            const sp<ABuffer> &accessUnit);
+
+    DISALLOW_EVIL_CONSTRUCTORS(ElementaryStreamQueue);
+};
+
+}  // namespace android
+
+#endif  // ES_QUEUE_H_
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 2417305..c5257bb 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -157,7 +157,7 @@
             }
         }
 
-        if (++numPacketsParsed > 1500) {
+        if (++numPacketsParsed > 2500) {
             break;
         }
     }
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index eb86277..ce10f5b 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -159,7 +159,7 @@
             try {
                 return ObbScanner.getObbInfo(filename);
             } catch (IOException e) {
-                Log.d(TAG, "Couldn't get OBB info", e);
+                Log.d(TAG, "Couldn't get OBB info for " + filename);
                 return null;
             }
         }
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 3980189..8de507e 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -80,7 +80,7 @@
     <bool name="def_accessibility_script_injection">false</bool>
 
     <!-- Default for Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS -->
-    <string name="def_accessibility_web_content_key_bindings">
+    <string name="def_accessibility_web_content_key_bindings" translatable="false">
             <!-- DPAD/Trackball UP maps to traverse previous on current axis and send an event. -->
             0x13=0x01000100;
             <!-- DPAD/Trackball DOWN maps to traverse next on current axis and send an event. -->
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index c047522..c70f5d4 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -89,6 +89,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_DRAG;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
@@ -152,8 +153,11 @@
     // responsible for power management when displayed.
     static final int KEYGUARD_LAYER = 14;
     static final int KEYGUARD_DIALOG_LAYER = 15;
+    // the drag layer: input for drag-and-drop is associated with this window,
+    // which sits above all other focusable windows
+    static final int DRAG_LAYER = 16;
     // things in here CAN NOT take focus, but are shown on top of everything else.
-    static final int SYSTEM_OVERLAY_LAYER = 16;
+    static final int SYSTEM_OVERLAY_LAYER = 17;
 
     static final int APPLICATION_MEDIA_SUBLAYER = -2;
     static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
@@ -839,6 +843,8 @@
             return TOAST_LAYER;
         case TYPE_WALLPAPER:
             return WALLPAPER_LAYER;
+        case TYPE_DRAG:
+            return DRAG_LAYER;
         }
         Log.e(TAG, "Unknown window type: " + type);
         return APPLICATION_LAYER;
@@ -1544,7 +1550,9 @@
             } 
             if (attrs.type >= FIRST_APPLICATION_WINDOW
                     && attrs.type <= LAST_APPLICATION_WINDOW
-                    && win.fillsScreenLw(mW, mH, false, false)) {
+                    && attrs.x == 0 && attrs.y == 0
+                    && attrs.width == WindowManager.LayoutParams.MATCH_PARENT
+                    && attrs.height == WindowManager.LayoutParams.MATCH_PARENT) {
                 if (DEBUG_LAYOUT) Log.v(TAG, "Fullscreen window: " + win);
                 mTopFullscreenOpaqueWindowState = win;
                 if ((attrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 97b8086..8527059 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1856,6 +1856,8 @@
                 if (--(track->mRetryCount) <= 0) {
                     LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this);
                     tracksToRemove->add(track);
+                    // indicate to client process that the track was disabled because of underrun
+                    cblk->flags |= CBLK_DISABLED_ON;
                 } else if (mixerStatus != MIXER_TRACKS_READY) {
                     mixerStatus = MIXER_TRACKS_ENABLED;
                 }
@@ -2790,7 +2792,7 @@
                     mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
                     memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
                     // Force underrun condition to avoid false underrun callback until first data is
-                    // written to buffer
+                    // written to buffer (other flags are cleared)
                     mCblk->flags = CBLK_UNDERRUN_ON;
                 } else {
                     mBuffer = sharedBuffer->pointer();
@@ -2813,7 +2815,7 @@
            mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
            memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
            // Force underrun condition to avoid false underrun callback until first data is
-           // written to buffer
+           // written to buffer (other flags are cleared)
            mCblk->flags = CBLK_UNDERRUN_ON;
            mBufferEnd = (uint8_t *)mBuffer + bufferSize;
        }
@@ -3794,6 +3796,8 @@
     AudioBufferProvider::Buffer buffer;
     sp<RecordTrack> activeTrack;
 
+    nsecs_t lastWarning = 0;
+
     // start recording
     while (!exitPending()) {
 
@@ -3935,8 +3939,13 @@
             }
             // client isn't retrieving buffers fast enough
             else {
-                if (!mActiveTrack->setOverflow())
-                    LOGW("RecordThread: buffer overflow");
+                if (!mActiveTrack->setOverflow()) {
+                    nsecs_t now = systemTime();
+                    if ((now - lastWarning) > kWarningThrottle) {
+                        LOGW("RecordThread: buffer overflow");
+                        lastWarning = now;
+                    }
+                }
                 // Release the processor for a while before asking for a new buffer.
                 // This will give the application more chance to read from the buffer and
                 // clear the overflow.
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 6095117..880befd 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -68,7 +68,7 @@
  */
 public class ConnectivityService extends IConnectivityManager.Stub {
 
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
     private static final String TAG = "ConnectivityService";
 
     // how long to wait before switching back to a radio's default network
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 0c3a0e6..28126b9 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -58,6 +58,7 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -605,6 +606,8 @@
             Slog.w(TAG, "failed parsing " + file + " " + e);
         } catch (XmlPullParserException e) {
             Slog.w(TAG, "failed parsing " + file + " " + e);
+        } catch (FileNotFoundException e) {
+            // Don't be noisy, this is normal if we haven't defined any policies.
         } catch (IOException e) {
             Slog.w(TAG, "failed parsing " + file + " " + e);
         } catch (IndexOutOfBoundsException e) {
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index fe306b3..a960097 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -342,6 +342,7 @@
         if (toChannel == null) {
             throw new IllegalArgumentException("toChannel must not be null.");
         }
+        Slog.d(TAG, "transferring touch focus");
         return nativeTransferTouchFocus(fromChannel, toChannel);
     }
     
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 675760f..de28375 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -951,10 +951,11 @@
     }
 
     public void updateStatusIcon(IBinder token, String packageName, int iconId) {
+        int uid = Binder.getCallingUid();
         long ident = Binder.clearCallingIdentity();
         try {
             if (token == null || mCurToken != token) {
-                Slog.w(TAG, "Ignoring setInputMethod of token: " + token);
+                Slog.w(TAG, "Ignoring setInputMethod of uid " + uid + " token: " + token);
                 return;
             }
 
@@ -1048,6 +1049,7 @@
 
     public boolean showSoftInput(IInputMethodClient client, int flags,
             ResultReceiver resultReceiver) {
+        int uid = Binder.getCallingUid();
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mMethodMap) {
@@ -1058,7 +1060,7 @@
                         // focus in the window manager, to allow this call to
                         // be made before input is started in it.
                         if (!mIWindowManager.inputMethodClientHasFocus(client)) {
-                            Slog.w(TAG, "Ignoring showSoftInput of: " + client);
+                            Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
                             return false;
                         }
                     } catch (RemoteException e) {
@@ -1112,6 +1114,7 @@
 
     public boolean hideSoftInput(IInputMethodClient client, int flags,
             ResultReceiver resultReceiver) {
+        int uid = Binder.getCallingUid();
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mMethodMap) {
@@ -1122,7 +1125,8 @@
                         // focus in the window manager, to allow this call to
                         // be made before input is started in it.
                         if (!mIWindowManager.inputMethodClientHasFocus(client)) {
-                            Slog.w(TAG, "Ignoring hideSoftInput of: " + client);
+                            if (DEBUG) Slog.w(TAG, "Ignoring hideSoftInput of uid "
+                                    + uid + ": " + client);
                             return false;
                         }
                     } catch (RemoteException e) {
@@ -1257,7 +1261,8 @@
         synchronized (mMethodMap) {
             if (mCurClient == null || client == null
                     || mCurClient.client.asBinder() != client.asBinder()) {
-                Slog.w(TAG, "Ignoring showInputMethodDialogFromClient of: " + client);
+                Slog.w(TAG, "Ignoring showInputMethodDialogFromClient of uid "
+                        + Binder.getCallingUid() + ": " + client);
             }
 
             mHandler.sendEmptyMessage(MSG_SHOW_IM_PICKER);
@@ -1290,7 +1295,8 @@
                             + android.Manifest.permission.WRITE_SECURE_SETTINGS);
                 }
             } else if (mCurToken != token) {
-                Slog.w(TAG, "Ignoring setInputMethod of token: " + token);
+                Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
+                        + " token: " + token);
                 return;
             }
 
@@ -1306,7 +1312,8 @@
     public void hideMySoftInput(IBinder token, int flags) {
         synchronized (mMethodMap) {
             if (token == null || mCurToken != token) {
-                Slog.w(TAG, "Ignoring hideInputMethod of token: " + token);
+                if (DEBUG) Slog.w(TAG, "Ignoring hideInputMethod of uid "
+                        + Binder.getCallingUid() + " token: " + token);
                 return;
             }
             long ident = Binder.clearCallingIdentity();
@@ -1321,7 +1328,8 @@
     public void showMySoftInput(IBinder token, int flags) {
         synchronized (mMethodMap) {
             if (token == null || mCurToken != token) {
-                Slog.w(TAG, "Ignoring hideInputMethod of token: " + token);
+                Slog.w(TAG, "Ignoring showMySoftInput of uid "
+                        + Binder.getCallingUid() + " token: " + token);
                 return;
             }
             long ident = Binder.clearCallingIdentity();
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index 8ab65e9..e47de13 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -28,6 +28,7 @@
 import java.util.Set;
 
 import android.util.Log;
+import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.LogPrinter;
 import android.util.Printer;
@@ -92,10 +93,12 @@
     }
 
     boolean dumpMap(PrintWriter out, String titlePrefix, String title,
-            String prefix, Map<String, ArrayList<F>> map, String packageName) {
+            String prefix, Map<String, ArrayList<F>> map, String packageName,
+            boolean printFilter) {
         String eprefix = prefix + "  ";
         String fprefix = prefix + "    ";
         boolean printedSomething = false;
+        Printer printer = null;
         for (Map.Entry<String, ArrayList<F>> e : map.entrySet()) {
             ArrayList<F> a = e.getValue();
             final int N = a.size();
@@ -115,37 +118,44 @@
                 }
                 printedSomething = true;
                 dumpFilter(out, fprefix, filter);
+                if (printFilter) {
+                    if (printer == null) {
+                        printer = new PrintWriterPrinter(out);
+                    }
+                    filter.dump(printer, fprefix + "  ");
+                }
             }
         }
         return printedSomething;
     }
 
-    public boolean dump(PrintWriter out, String title, String prefix, String packageName) {
+    public boolean dump(PrintWriter out, String title, String prefix, String packageName,
+            boolean printFilter) {
         String innerPrefix = prefix + "  ";
         String sepPrefix = "\n" + prefix;
         String curPrefix = title + "\n" + prefix;
         if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix,
-                mTypeToFilter, packageName)) {
+                mTypeToFilter, packageName, printFilter)) {
             curPrefix = sepPrefix;
         }
         if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix,
-                mBaseTypeToFilter, packageName)) {
+                mBaseTypeToFilter, packageName, printFilter)) {
             curPrefix = sepPrefix;
         }
         if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix,
-                mWildTypeToFilter, packageName)) {
+                mWildTypeToFilter, packageName, printFilter)) {
             curPrefix = sepPrefix;
         }
         if (dumpMap(out, curPrefix, "Schemes:", innerPrefix,
-                mSchemeToFilter, packageName)) {
+                mSchemeToFilter, packageName, printFilter)) {
             curPrefix = sepPrefix;
         }
         if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix,
-                mActionToFilter, packageName)) {
+                mActionToFilter, packageName, printFilter)) {
             curPrefix = sepPrefix;
         }
         if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix,
-                mTypedActionToFilter, packageName)) {
+                mTypedActionToFilter, packageName, printFilter)) {
             curPrefix = sepPrefix;
         }
         return curPrefix == sepPrefix;
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index ca8fc52..7870b06 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import com.android.internal.app.IMediaContainerService;
+import com.android.internal.util.HexDump;
 import com.android.server.am.ActivityManagerService;
 
 import android.content.BroadcastReceiver;
@@ -44,15 +45,22 @@
 import android.os.storage.IMountShutdownObserver;
 import android.os.storage.IObbActionListener;
 import android.os.storage.StorageResultCode;
+import android.security.MessageDigest;
 import android.util.Slog;
 
+import java.io.FileDescriptor;
 import java.io.IOException;
+import java.io.PrintWriter;
+import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 
 /**
  * MountService implements back-end services for platform storage
@@ -71,6 +79,8 @@
 
     private static final String VOLD_TAG = "VoldConnector";
 
+    protected static final int MAX_OBBS = 8;
+
     /*
      * Internal vold volume state constants
      */
@@ -151,15 +161,19 @@
      * Mounted OBB tracking information. Used to track the current state of all
      * OBBs.
      */
-    final private Map<IObbActionListener, List<ObbState>> mObbMounts = new HashMap<IObbActionListener, List<ObbState>>();
+    final private Map<Integer, Integer> mObbUidUsage = new HashMap<Integer, Integer>();
+    final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
     final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
 
     class ObbState implements IBinder.DeathRecipient {
-        public ObbState(String filename, IObbActionListener token, int callerUid) {
+        public ObbState(String filename, IObbActionListener token, int callerUid)
+                throws RemoteException {
             this.filename = filename;
             this.token = token;
             this.callerUid = callerUid;
             mounted = false;
+
+            getBinder().linkToDeath(this, 0);
         }
 
         // OBB source filename
@@ -174,14 +188,33 @@
         // Whether this is mounted currently.
         boolean mounted;
 
+        public IBinder getBinder() {
+            return token.asBinder();
+        }
+
         @Override
         public void binderDied() {
             ObbAction action = new UnmountObbAction(this, true);
             mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+        }
 
-            removeObbState(this);
+        public void cleanUp() {
+            getBinder().unlinkToDeath(this, 0);
+        }
 
-            token.asBinder().unlinkToDeath(this, 0);
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder("ObbState{");
+            sb.append("filename=");
+            sb.append(filename);
+            sb.append(",token=");
+            sb.append(token.toString());
+            sb.append(",callerUid=");
+            sb.append(callerUid);
+            sb.append(",mounted=");
+            sb.append(mounted);
+            sb.append('}');
+            return sb.toString();
         }
     }
 
@@ -481,6 +514,34 @@
                 mPms.updateExternalMediaStatus(true, false);
             }
         }
+
+        // Remove all OBB mappings and listeners from this path
+        synchronized (mObbMounts) {
+            final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
+
+            final Iterator<Entry<String, ObbState>> i = mObbPathToStateMap.entrySet().iterator();
+            while (i.hasNext()) {
+                final Entry<String, ObbState> obbEntry = i.next();
+
+                // If this entry's source file is in the volume path that got
+                // unmounted, remove it because it's no longer valid.
+                if (obbEntry.getKey().startsWith(path)) {
+                    obbStatesToRemove.add(obbEntry.getValue());
+                }
+            }
+
+            for (final ObbState obbState : obbStatesToRemove) {
+                removeObbState(obbState);
+
+                try {
+                    obbState.token.onObbResult(obbState.filename, Environment.MEDIA_UNMOUNTED);
+                } catch (RemoteException e) {
+                    Slog.i(TAG, "Couldn't send unmount notification for  OBB: "
+                            + obbState.filename);
+                }
+            }
+        }
+
         String oldState = mLegacyState;
         mLegacyState = state;
 
@@ -1507,11 +1568,18 @@
 
     public boolean isObbMounted(String filename) {
         synchronized (mObbMounts) {
-            return mObbPathToStateMap.containsKey(filename);
+            final ObbState obbState = mObbPathToStateMap.get(filename);
+            if (obbState != null) {
+                synchronized (obbState) {
+                    return obbState.mounted;
+                }
+            }
         }
+        return false;
     }
 
-    public void mountObb(String filename, String key, IObbActionListener token) {
+    public void mountObb(String filename, String key, IObbActionListener token)
+            throws RemoteException {
         waitForReady();
         warnOnNotMounted();
 
@@ -1525,21 +1593,44 @@
 
         synchronized (mObbMounts) {
             if (isObbMounted(filename)) {
-                throw new IllegalArgumentException("OBB file is already mounted");
+                try {
+                    token.onObbResult(filename, Environment.MEDIA_MOUNTED);
+                } catch (RemoteException e) {
+                    Slog.d(TAG, "Could not send unmount notification for: " + filename);
+                }
+                return;
             }
 
             final int callerUid = Binder.getCallingUid();
+
+            final Integer uidUsage = mObbUidUsage.get(callerUid);
+            if (uidUsage != null && uidUsage > MAX_OBBS) {
+                throw new IllegalStateException("Maximum number of OBBs mounted!");
+            }
+
             obbState = new ObbState(filename, token, callerUid);
             addObbState(obbState);
         }
 
-        try {
-            token.asBinder().linkToDeath(obbState, 0);
-        } catch (RemoteException rex) {
-            Slog.e(TAG, "Failed to link to listener death");
+        String hashedKey = null;
+        if (key != null) {
+            final MessageDigest md;
+            try {
+                md = MessageDigest.getInstance("MD5");
+            } catch (NoSuchAlgorithmException e) {
+                Slog.e(TAG, "Could not load MD5 algorithm", e);
+                try {
+                    token.onObbResult(filename, Environment.MEDIA_UNMOUNTED);
+                } catch (RemoteException e1) {
+                    Slog.d(TAG, "Could not send unmount notification for: " + filename);
+                }
+                return;
+            }
+
+            hashedKey = HexDump.toHexString(md.digest(key.getBytes()));
         }
 
-        MountObbAction action = new MountObbAction(obbState, key);
+        ObbAction action = new MountObbAction(obbState, hashedKey);
         mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
 
         if (DEBUG_OBB)
@@ -1557,18 +1648,24 @@
 
         synchronized (mObbMounts) {
             if (!isObbMounted(filename)) {
-                throw new IllegalArgumentException("OBB is not mounted");
+                try {
+                    token.onObbResult(filename, Environment.MEDIA_UNMOUNTED);
+                } catch (RemoteException e) {
+                    Slog.d(TAG, "Could not send unmount notification for: " + filename);
+                }
+                return;
             }
+
             obbState = mObbPathToStateMap.get(filename);
 
             if (Binder.getCallingUid() != obbState.callerUid) {
                 throw new SecurityException("caller UID does not match original mount caller UID");
-            } else if (!token.asBinder().equals(obbState.token.asBinder())) {
+            } else if (!token.asBinder().equals(obbState.getBinder())) {
                 throw new SecurityException("caller does not match original mount caller");
             }
         }
 
-        UnmountObbAction action = new UnmountObbAction(obbState, force);
+        ObbAction action = new UnmountObbAction(obbState, force);
         mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
 
         if (DEBUG_OBB)
@@ -1577,26 +1674,57 @@
 
     private void addObbState(ObbState obbState) {
         synchronized (mObbMounts) {
-            List<ObbState> obbStates = mObbMounts.get(obbState.token);
+            List<ObbState> obbStates = mObbMounts.get(obbState.getBinder());
             if (obbStates == null) {
                 obbStates = new ArrayList<ObbState>();
-                mObbMounts.put(obbState.token, obbStates);
+                mObbMounts.put(obbState.getBinder(), obbStates);
             }
             obbStates.add(obbState);
             mObbPathToStateMap.put(obbState.filename, obbState);
+
+            // Track the number of OBBs used by this UID.
+            final int uid = obbState.callerUid;
+            final Integer uidUsage = mObbUidUsage.get(uid);
+            if (uidUsage == null) {
+                mObbUidUsage.put(uid, 1);
+            } else {
+                mObbUidUsage.put(uid, uidUsage + 1);
+            }
         }
     }
 
     private void removeObbState(ObbState obbState) {
         synchronized (mObbMounts) {
-            final List<ObbState> obbStates = mObbMounts.get(obbState.token);
+            final List<ObbState> obbStates = mObbMounts.get(obbState.getBinder());
             if (obbStates != null) {
                 obbStates.remove(obbState);
             }
             if (obbStates == null || obbStates.isEmpty()) {
-                mObbMounts.remove(obbState.token);
+                mObbMounts.remove(obbState.getBinder());
+                obbState.cleanUp();
             }
             mObbPathToStateMap.remove(obbState.filename);
+
+            // Track the number of OBBs used by this UID.
+            final int uid = obbState.callerUid;
+            final Integer uidUsage = mObbUidUsage.get(uid);
+            if (uidUsage == null) {
+                Slog.e(TAG, "Called removeObbState for UID that isn't in map: " + uid);
+            } else {
+                final int newUsage = uidUsage - 1;
+                if (newUsage == 0) {
+                    mObbUidUsage.remove(uid);
+                } else {
+                    mObbUidUsage.put(uid, newUsage);
+                }
+            }
+        }
+    }
+
+    private void replaceObbState(ObbState oldObbState, ObbState newObbState) {
+        synchronized (mObbMounts) {
+            removeObbState(oldObbState);
+            addObbState(newObbState);
         }
     }
 
@@ -1627,20 +1755,16 @@
                             Slog.e(TAG, "Failed to bind to media container service");
                             action.handleError();
                             return;
-                        } else {
-                            // Once we bind to the service, the first
-                            // pending request will be processed.
-                            mActions.add(action);
-                        }
-                    } else {
-                        // Already bound to the service. Just make
-                        // sure we trigger off processing the first request.
-                        if (mActions.size() == 0) {
-                            mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
                         }
 
                         mActions.add(action);
+                        break;
                     }
+
+                    // Once we bind to the service, the first
+                    // pending request will be processed.
+                    mActions.add(action);
+                    mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
                     break;
                 }
                 case OBB_MCS_BOUND: {
@@ -1773,6 +1897,29 @@
 
         abstract void handleExecute() throws RemoteException, IOException;
         abstract void handleError();
+
+        protected ObbInfo getObbInfo() throws IOException {
+            ObbInfo obbInfo;
+            try {
+                obbInfo = mContainerService.getObbInfo(mObbState.filename);
+            } catch (RemoteException e) {
+                Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
+                        + mObbState.filename);
+                obbInfo = null;
+            }
+            if (obbInfo == null) {
+                throw new IOException("Couldn't read OBB file: " + mObbState.filename);
+            }
+            return obbInfo;
+        }
+
+        protected void sendNewStatusOrIgnore(String filename, String status) {
+            try {
+                mObbState.token.onObbResult(filename, status);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+            }
+        }
     }
 
     class MountObbAction extends ObbAction {
@@ -1783,56 +1930,90 @@
             mKey = key;
         }
 
-        public void handleExecute() throws RemoteException, IOException {
-            final ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
-            if (obbInfo == null) {
-                throw new IOException("Couldn't read OBB file: " + mObbState.filename);
+        public void handleExecute() throws IOException, RemoteException {
+            final ObbInfo obbInfo = getObbInfo();
+
+            /*
+             * If someone tried to trick us with some weird characters, rectify
+             * it here.
+             */
+            if (!mObbState.filename.equals(obbInfo.filename)) {
+                if (DEBUG_OBB)
+                    Slog.i(TAG, "OBB filename " + mObbState.filename + " is actually "
+                            + obbInfo.filename);
+
+                synchronized (mObbMounts) {
+                    /*
+                     * If the real filename is already mounted, discard this
+                     * state and notify the caller that the OBB is already
+                     * mounted.
+                     */
+                    if (isObbMounted(obbInfo.filename)) {
+                        if (DEBUG_OBB)
+                            Slog.i(TAG, "OBB already mounted as " + obbInfo.filename);
+
+                        removeObbState(mObbState);
+                        sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_MOUNTED);
+                        return;
+                    }
+
+                    /*
+                     * It's not already mounted, so we have to replace the state
+                     * with the state containing the actual filename.
+                     */
+                    ObbState newObbState = new ObbState(obbInfo.filename, mObbState.token,
+                            mObbState.callerUid);
+                    replaceObbState(mObbState, newObbState);
+                    mObbState = newObbState;
+                }
             }
 
             if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
                 throw new IllegalArgumentException("Caller package does not match OBB file");
             }
 
-            if (mKey == null) {
-                mKey = "none";
-            }
-
-            int rc = StorageResultCode.OperationSucceeded;
-            String cmd = String.format("obb mount %s %s %d", mObbState.filename, mKey,
-                    mObbState.callerUid);
-            try {
-                mConnector.doCommand(cmd);
-            } catch (NativeDaemonConnectorException e) {
-                int code = e.getCode();
-                if (code != VoldResponseCode.OpFailedStorageBusy) {
-                    rc = StorageResultCode.OperationFailedInternalError;
+            boolean mounted = false;
+            int rc;
+            synchronized (mObbState) {
+                if (mObbState.mounted) {
+                    sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
+                    return;
                 }
-            }
 
-            if (rc == StorageResultCode.OperationSucceeded) {
+                rc = StorageResultCode.OperationSucceeded;
+                String cmd = String.format("obb mount %s %s %d", mObbState.filename,
+                        mKey != null ? mKey : "none",
+                        mObbState.callerUid);
                 try {
-                    mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_MOUNTED);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+                    mConnector.doCommand(cmd);
+                } catch (NativeDaemonConnectorException e) {
+                    int code = e.getCode();
+                    if (code != VoldResponseCode.OpFailedStorageBusy) {
+                        rc = StorageResultCode.OperationFailedInternalError;
+                    }
                 }
+
+                if (rc == StorageResultCode.OperationSucceeded) {
+                    mObbState.mounted = mounted = true;
+                }
+            }
+
+            if (mounted) {
+                sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
             } else {
                 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
 
                 // We didn't succeed, so remove this from the mount-set.
                 removeObbState(mObbState);
 
-                mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
+                sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
             }
         }
 
         public void handleError() {
             removeObbState(mObbState);
 
-            try {
-                mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename);
-            }
+            sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
         }
 
         @Override
@@ -1858,51 +2039,69 @@
             mForceUnmount = force;
         }
 
-        public void handleExecute() throws RemoteException, IOException {
-            final ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
-            if (obbInfo == null) {
-                throw new IOException("Couldn't read OBB file: " + mObbState.filename);
-            }
+        public void handleExecute() throws IOException {
+            final ObbInfo obbInfo = getObbInfo();
 
-            int rc = StorageResultCode.OperationSucceeded;
-            String cmd = String.format("obb unmount %s%s", mObbState.filename,
-                    (mForceUnmount ? " force" : ""));
-            try {
-                mConnector.doCommand(cmd);
-            } catch (NativeDaemonConnectorException e) {
-                int code = e.getCode();
-                if (code == VoldResponseCode.OpFailedStorageBusy) {
-                    rc = StorageResultCode.OperationFailedStorageBusy;
-                } else {
-                    rc = StorageResultCode.OperationFailedInternalError;
+            /*
+             * If someone tried to trick us with some weird characters, rectify
+             * it here.
+             */
+            synchronized (mObbMounts) {
+                if (!isObbMounted(obbInfo.filename)) {
+                    removeObbState(mObbState);
+                    sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
+                    return;
+                }
+
+                if (!mObbState.filename.equals(obbInfo.filename)) {
+                    removeObbState(mObbState);
+                    mObbState = mObbPathToStateMap.get(obbInfo.filename);
                 }
             }
 
-            if (rc == StorageResultCode.OperationSucceeded) {
+            boolean unmounted = false;
+            synchronized (mObbState) {
+                if (!mObbState.mounted) {
+                    sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_UNMOUNTED);
+                    return;
+                }
+
+                int rc = StorageResultCode.OperationSucceeded;
+                String cmd = String.format("obb unmount %s%s", mObbState.filename,
+                        (mForceUnmount ? " force" : ""));
+                try {
+                    mConnector.doCommand(cmd);
+                } catch (NativeDaemonConnectorException e) {
+                    int code = e.getCode();
+                    if (code == VoldResponseCode.OpFailedStorageBusy) {
+                        rc = StorageResultCode.OperationFailedStorageBusy;
+                    } else if (code == VoldResponseCode.OpFailedStorageNotFound) {
+                        // If it's not mounted then we've already won.
+                        rc = StorageResultCode.OperationSucceeded;
+                    } else {
+                        rc = StorageResultCode.OperationFailedInternalError;
+                    }
+                }
+
+                if (rc == StorageResultCode.OperationSucceeded) {
+                    mObbState.mounted = false;
+                    unmounted = true;
+                }
+            }
+
+            if (unmounted) {
                 removeObbState(mObbState);
 
-                try {
-                    mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_UNMOUNTED);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
-                }
+                sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
             } else {
-                try {
-                    mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
-                }
+                sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
             }
         }
 
         public void handleError() {
             removeObbState(mObbState);
 
-            try {
-                mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename);
-            }
+            sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
         }
 
         @Override
@@ -1917,9 +2116,33 @@
             sb.append(mObbState.callerUid);
             sb.append(",token=");
             sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
+            sb.append(",binder=");
+            sb.append(mObbState.getBinder().toString());
             sb.append('}');
             return sb.toString();
         }
     }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump ActivityManager from from pid="
+                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+                    + " without permission " + android.Manifest.permission.DUMP);
+            return;
+        }
+
+        pw.println("  mObbMounts:");
+
+        synchronized (mObbMounts) {
+            final Collection<List<ObbState>> obbStateLists = mObbMounts.values();
+
+            for (final List<ObbState> obbStates : obbStateLists) {
+                for (final ObbState obbState : obbStates) {
+                    pw.print("    "); pw.println(obbState.toString());
+                }
+            }
+        }
+    }
 }
 
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 05abd99..8dbd3e7 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -55,7 +55,7 @@
 class NetworkManagementService extends INetworkManagementService.Stub {
 
     private static final String TAG = "NetworkManagmentService";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
     private static final String NETD_TAG = "NetdConnector";
 
     class NetdResponseCode {
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 8f90756..1f97bee 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -1102,27 +1102,6 @@
         final File permFile = new File(Environment.getRootDirectory(),
                 "etc/permissions/platform.xml");
         readPermissionsFromXml(permFile);
-
-        StringBuilder sb = new StringBuilder(128);
-        sb.append("Libs:");
-        Iterator<String> it = mSharedLibraries.keySet().iterator();
-        while (it.hasNext()) {
-            sb.append(' ');
-            String name = it.next();
-            sb.append(name);
-            sb.append(':');
-            sb.append(mSharedLibraries.get(name));
-        }
-        Log.i(TAG, sb.toString());
-
-        sb.setLength(0);
-        sb.append("Features:");
-        it = mAvailableFeatures.keySet().iterator();
-        while (it.hasNext()) {
-            sb.append(' ');
-            sb.append(it.next());
-        }
-        Log.i(TAG, sb.toString());
     }
 
     private void readPermissionsFromXml(File permFile) {
@@ -2632,7 +2611,7 @@
                     // The system package has been updated and the code path does not match
                     // Ignore entry. Skip it.
                     Log.i(TAG, "Package " + ps.name + " at " + scanFile
-                            + "ignored: updated version " + ps.versionCode
+                            + " ignored: updated version " + ps.versionCode
                             + " better than this " + pkg.mVersionCode);
                     mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
                     return null;
@@ -6866,7 +6845,18 @@
             return;
         }
 
+        boolean dumpStar = true;
+        boolean dumpLibs = false;
+        boolean dumpFeatures = false;
+        boolean dumpResolvers = false;
+        boolean dumpPermissions = false;
+        boolean dumpPackages = false;
+        boolean dumpSharedUsers = false;
+        boolean dumpMessages = false;
+        boolean dumpProviders = false;
+        
         String packageName = null;
+        boolean showFilters = false;
         
         int opti = 0;
         while (opti < args.length) {
@@ -6879,10 +6869,22 @@
                 // Right now we only know how to print all.
             } else if ("-h".equals(opt)) {
                 pw.println("Package manager dump options:");
-                pw.println("  [-h] [cmd] ...");
+                pw.println("  [-h] [-f] [cmd] ...");
+                pw.println("    -f: print details of intent filters");
+                pw.println("    -h: print this help");
                 pw.println("  cmd may be one of:");
-                pw.println("    [package.name]: info about given package");
+                pw.println("    l[ibraries]: list known shared libraries");
+                pw.println("    f[ibraries]: list device features");
+                pw.println("    r[esolvers]: dump intent resolvers");
+                pw.println("    perm[issions]: dump permissions");
+                pw.println("    prov[iders]: dump content providers");
+                pw.println("    p[ackages]: dump installed packages");
+                pw.println("    s[hared-users]: dump shared user IDs");
+                pw.println("    m[essages]: print collected runtime messages");
+                pw.println("    <package.name>: info about given package");
                 return;
+            } else if ("-f".equals(opt)) {
+                showFilters = true;
             } else {
                 pw.println("Unknown argument: " + opt + "; use -h for help");
             }
@@ -6895,32 +6897,87 @@
             // Is this a package name?
             if ("android".equals(cmd) || cmd.contains(".")) {
                 packageName = cmd;
+            } else if ("l".equals(cmd) || "libraries".equals(cmd)) {
+                dumpStar = false;
+                dumpLibs = true;
+            } else if ("f".equals(cmd) || "features".equals(cmd)) {
+                dumpStar = false;
+                dumpFeatures = true;
+            } else if ("r".equals(cmd) || "resolvers".equals(cmd)) {
+                dumpStar = false;
+                dumpResolvers = true;
+            } else if ("perm".equals(cmd) || "permissions".equals(cmd)) {
+                dumpStar = false;
+                dumpPermissions = true;
+            } else if ("p".equals(cmd) || "packages".equals(cmd)) {
+                dumpStar = false;
+                dumpPackages = true;
+            } else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
+                dumpStar = false;
+                dumpSharedUsers = true;
+            } else if ("prov".equals(cmd) || "providers".equals(cmd)) {
+                dumpStar = false;
+                dumpProviders = true;
+            } else if ("m".equals(cmd) || "messages".equals(cmd)) {
+                dumpStar = false;
+                dumpMessages = true;
             }
         }
         
         boolean printedTitle = false;
         
         synchronized (mPackages) {
-            if (mActivities.dump(pw, "Activity Resolver Table:", "  ", packageName)) {
+            if ((dumpStar || dumpLibs) && packageName == null) {
+                if (printedTitle) pw.println(" ");
                 printedTitle = true;
+                pw.println("Libraries:");
+                Iterator<String> it = mSharedLibraries.keySet().iterator();
+                while (it.hasNext()) {
+                    String name = it.next();
+                    pw.print("  ");
+                    pw.print(name);
+                    pw.print(" -> ");
+                    pw.println(mSharedLibraries.get(name));
+                }
             }
-            if (mReceivers.dump(pw, printedTitle
-                    ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:",
-                    "  ", packageName)) {
+
+            if ((dumpStar || dumpFeatures) && packageName == null) {
+                if (printedTitle) pw.println(" ");
                 printedTitle = true;
+                pw.println("Features:");
+                Iterator<String> it = mAvailableFeatures.keySet().iterator();
+                while (it.hasNext()) {
+                    String name = it.next();
+                    pw.print("  ");
+                    pw.println(name);
+                }
             }
-            if (mServices.dump(pw, printedTitle
-                    ? "\nService Resolver Table:" : "Service Resolver Table:",
-                    "  ", packageName)) {
-                printedTitle = true;
+
+            if (dumpStar || dumpResolvers) {
+                if (mActivities.dump(pw, printedTitle
+                        ? "\nActivity Resolver Table:" : "Activity Resolver Table:",
+                        "  ", packageName, showFilters)) {
+                    printedTitle = true;
+                }
+                if (mReceivers.dump(pw, printedTitle
+                        ? "\nReceiver Resolver Table:" : "Receiver Resolver Table:",
+                        "  ", packageName, showFilters)) {
+                    printedTitle = true;
+                }
+                if (mServices.dump(pw, printedTitle
+                        ? "\nService Resolver Table:" : "Service Resolver Table:",
+                        "  ", packageName, showFilters)) {
+                    printedTitle = true;
+                }
+                if (mSettings.mPreferredActivities.dump(pw, printedTitle
+                        ? "\nPreferred Activities:" : "Preferred Activities:",
+                        "  ", packageName, showFilters)) {
+                    printedTitle = true;
+                }
             }
-            if (mSettings.mPreferredActivities.dump(pw, printedTitle
-                    ? "\nPreferred Activities:" : "Preferred Activities:",
-                    "  ", packageName)) {
-                printedTitle = true;
-            }
+            
             boolean printedSomething = false;
-            {
+            if (dumpStar || dumpPermissions) {
                 for (BasePermission p : mSettings.mPermissions.values()) {
                     if (packageName != null && !packageName.equals(p.sourcePackage)) {
                         continue;
@@ -6947,9 +7004,27 @@
                     }
                 }
             }
+
+            if (dumpStar || dumpProviders) {
+                printedSomething = false;
+                for (PackageParser.Provider p : mProviders.values()) {
+                    if (packageName != null && !packageName.equals(p.info.packageName)) {
+                        continue;
+                    }
+                    if (!printedSomething) {
+                        if (printedTitle) pw.println(" ");
+                        pw.println("Registered ContentProviders:");
+                        printedSomething = true;
+                        printedTitle = true;
+                    }
+                    pw.print("  ["); pw.print(p.info.authority); pw.print("]: ");
+                            pw.println(p.toString());
+                }
+            }
+            
             printedSomething = false;
             SharedUserSetting packageSharedUser = null;
-            {
+            if (dumpStar || dumpPackages) {
                 for (PackageSetting ps : mSettings.mPackages.values()) {
                     if (packageName != null && !packageName.equals(ps.realName)
                             && !packageName.equals(ps.name)) {
@@ -7052,52 +7127,54 @@
                 }
             }
             printedSomething = false;
-            if (mSettings.mRenamedPackages.size() > 0) {
-                for (HashMap.Entry<String, String> e
-                        : mSettings.mRenamedPackages.entrySet()) {
-                    if (packageName != null && !packageName.equals(e.getKey())
-                            && !packageName.equals(e.getValue())) {
-                        continue;
+            if (dumpStar || dumpPackages) {
+                if (mSettings.mRenamedPackages.size() > 0) {
+                    for (HashMap.Entry<String, String> e
+                            : mSettings.mRenamedPackages.entrySet()) {
+                        if (packageName != null && !packageName.equals(e.getKey())
+                                && !packageName.equals(e.getValue())) {
+                            continue;
+                        }
+                        if (!printedSomething) {
+                            if (printedTitle) pw.println(" ");
+                            pw.println("Renamed packages:");
+                            printedSomething = true;
+                            printedTitle = true;
+                        }
+                        pw.print("  "); pw.print(e.getKey()); pw.print(" -> ");
+                                pw.println(e.getValue());
                     }
-                    if (!printedSomething) {
-                        if (printedTitle) pw.println(" ");
-                        pw.println("Renamed packages:");
-                        printedSomething = true;
-                        printedTitle = true;
+                }
+                printedSomething = false;
+                if (mSettings.mDisabledSysPackages.size() > 0) {
+                    for (PackageSetting ps : mSettings.mDisabledSysPackages.values()) {
+                        if (packageName != null && !packageName.equals(ps.realName)
+                                && !packageName.equals(ps.name)) {
+                            continue;
+                        }
+                        if (!printedSomething) {
+                            if (printedTitle) pw.println(" ");
+                            pw.println("Hidden system packages:");
+                            printedSomething = true;
+                            printedTitle = true;
+                        }
+                       pw.print("  Package [");
+                                pw.print(ps.realName != null ? ps.realName : ps.name);
+                                pw.print("] (");
+                                pw.print(Integer.toHexString(System.identityHashCode(ps)));
+                                pw.println("):");
+                        if (ps.realName != null) {
+                            pw.print("    compat name="); pw.println(ps.name);
+                        }
+                        pw.print("    userId="); pw.println(ps.userId);
+                        pw.print("    sharedUser="); pw.println(ps.sharedUser);
+                        pw.print("    codePath="); pw.println(ps.codePathString);
+                        pw.print("    resourcePath="); pw.println(ps.resourcePathString);
                     }
-                    pw.print("  "); pw.print(e.getKey()); pw.print(" -> ");
-                            pw.println(e.getValue());
                 }
             }
             printedSomething = false;
-            if (mSettings.mDisabledSysPackages.size() > 0) {
-                for (PackageSetting ps : mSettings.mDisabledSysPackages.values()) {
-                    if (packageName != null && !packageName.equals(ps.realName)
-                            && !packageName.equals(ps.name)) {
-                        continue;
-                    }
-                    if (!printedSomething) {
-                        if (printedTitle) pw.println(" ");
-                        pw.println("Hidden system packages:");
-                        printedSomething = true;
-                        printedTitle = true;
-                    }
-                   pw.print("  Package [");
-                            pw.print(ps.realName != null ? ps.realName : ps.name);
-                            pw.print("] (");
-                            pw.print(Integer.toHexString(System.identityHashCode(ps)));
-                            pw.println("):");
-                    if (ps.realName != null) {
-                        pw.print("    compat name="); pw.println(ps.name);
-                    }
-                    pw.print("    userId="); pw.println(ps.userId);
-                    pw.print("    sharedUser="); pw.println(ps.sharedUser);
-                    pw.print("    codePath="); pw.println(ps.codePathString);
-                    pw.print("    resourcePath="); pw.println(ps.resourcePathString);
-                }
-            }
-            printedSomething = false;
-            {
+            if (dumpStar || dumpSharedUsers) {
                 for (SharedUserSetting su : mSettings.mSharedUsers.values()) {
                     if (packageName != null && su != packageSharedUser) {
                         continue;
@@ -7120,11 +7197,11 @@
                 }
             }
             
-            if (packageName == null) {
+            if ((dumpStar || dumpMessages) && packageName == null) {
                 if (printedTitle) pw.println(" ");
                 printedTitle = true;
                 pw.println("Settings parse messages:");
-                pw.println(mSettings.mReadMessages.toString());
+                pw.print(mSettings.mReadMessages.toString());
                 
                 pw.println(" ");
                 pw.println("Package warning messages:");
@@ -7135,29 +7212,12 @@
                     int avail = in.available();
                     byte[] data = new byte[avail];
                     in.read(data);
-                    pw.println(new String(data));
+                    pw.print(new String(data));
                 } catch (FileNotFoundException e) {
                 } catch (IOException e) {
                 }
             }
         }
-
-        synchronized (mProviders) {
-            boolean printedSomething = false;
-            for (PackageParser.Provider p : mProviders.values()) {
-                if (packageName != null && !packageName.equals(p.info.packageName)) {
-                    continue;
-                }
-                if (!printedSomething) {
-                    if (printedTitle) pw.println(" ");
-                    pw.println("Registered ContentProviders:");
-                    printedSomething = true;
-                    printedTitle = true;
-                }
-                pw.print("  ["); pw.print(p.info.authority); pw.print("]: ");
-                        pw.println(p.toString());
-            }
-        }
     }
 
     static final class BasePermission {
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 4532c1c..29a9a7e 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -871,7 +871,7 @@
             mWakeLockState = mLocks.gatherState();
             // goes in the middle to reduce flicker
             if ((wl.flags & PowerManager.ON_AFTER_RELEASE) != 0) {
-                userActivity(SystemClock.uptimeMillis(), false);
+                userActivity(SystemClock.uptimeMillis(), -1, false, OTHER_EVENT, false);
             }
             setPowerState(mWakeLockState | mUserState);
         }
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 7100cc5..0727a79 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -40,6 +40,7 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.policy.impl.PhoneWindowManager;
+import com.android.internal.view.BaseInputHandler;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
@@ -51,6 +52,8 @@
 import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
+import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -93,6 +96,7 @@
 import android.util.SparseIntArray;
 import android.util.TypedValue;
 import android.view.Display;
+import android.view.DragEvent;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.IApplicationToken;
@@ -104,6 +108,8 @@
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEvent;
+import android.view.InputHandler;
+import android.view.InputQueue;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -159,6 +165,7 @@
     static final boolean DEBUG_STARTING_WINDOW = false;
     static final boolean DEBUG_REORDER = false;
     static final boolean DEBUG_WALLPAPER = false;
+    static final boolean DEBUG_DRAG = true;
     static final boolean SHOW_TRANSACTIONS = false;
     static final boolean HIDE_STACK_CRAWLS = true;
 
@@ -486,6 +493,311 @@
     boolean mTurnOnScreen;
 
     /**
+     * Drag/drop state
+     */
+    class DragState {
+        IBinder mToken;
+        Surface mSurface;
+        boolean mLocalOnly;
+        ClipData mData;
+        ClipDescription mDataDescription;
+        float mThumbOffsetX, mThumbOffsetY;
+        InputChannel mServerChannel, mClientChannel;
+        WindowState mTargetWindow;
+        ArrayList<WindowState> mNotifiedWindows;
+        boolean mDragEnded;
+
+        private final Rect tmpRect = new Rect();
+
+        DragState(IBinder token, Surface surface, boolean localOnly) {
+            mToken = token;
+            mSurface = surface;
+            mLocalOnly = localOnly;
+            mNotifiedWindows = new ArrayList<WindowState>();
+        }
+
+        void reset() {
+            if (mSurface != null) {
+                mSurface.destroy();
+            }
+            mSurface = null;
+            mLocalOnly = false;
+            mToken = null;
+            mData = null;
+            mThumbOffsetX = mThumbOffsetY = 0;
+            mNotifiedWindows = null;
+        }
+
+        void register() {
+            if (DEBUG_DRAG) Slog.d(TAG, "registering drag input channel");
+            if (mClientChannel != null) {
+                Slog.e(TAG, "Duplicate register of drag input channel");
+            } else {
+                InputChannel[] channels = InputChannel.openInputChannelPair("drag");
+                mServerChannel = channels[0];
+                mClientChannel = channels[1];
+                mInputManager.registerInputChannel(mServerChannel);
+                InputQueue.registerInputChannel(mClientChannel, mDragInputHandler,
+                        mH.getLooper().getQueue());
+            }
+        }
+
+        void unregister() {
+            if (DEBUG_DRAG) Slog.d(TAG, "unregistering drag input channel");
+            if (mClientChannel == null) {
+                Slog.e(TAG, "Unregister of nonexistent drag input channel");
+            } else {
+                mInputManager.unregisterInputChannel(mServerChannel);
+                InputQueue.unregisterInputChannel(mClientChannel);
+                mClientChannel.dispose();
+                mClientChannel = null;
+                mServerChannel = null;
+            }
+        }
+
+        /* call out to each visible window/session informing it about the drag
+         */
+        void broadcastDragStartedLw() {
+            // Cache a base-class instance of the clip metadata so that parceling
+            // works correctly in calling out to the apps.
+            mDataDescription = new ClipDescription(mData);
+            mNotifiedWindows.clear();
+
+            if (DEBUG_DRAG) {
+                Slog.d(TAG, "broadcasting DRAG_STARTED of " + mDataDescription);
+            }
+
+            DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, 0, 0,
+                    mDataDescription, null);
+            for (WindowState ws : mWindows) {
+                sendDragStartedLw(ws, evt);
+            }
+            evt.recycle();
+        }
+
+        /* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the
+         * designated window is potentially a drop recipient.  There are race situations
+         * around DRAG_ENDED broadcast, so we make sure that once we've declared that
+         * the drag has ended, we never send out another DRAG_STARTED for this drag action.
+         */
+        private void sendDragStartedLw(WindowState newWin, final DragEvent event) {
+            if (!mDragEnded && newWin.isPotentialDragTarget()) {
+                try {
+                    newWin.mClient.dispatchDragEvent(event);
+                    // track each window that we've notified that the drag is starting
+                    mNotifiedWindows.add(newWin);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Unable to drag-start window " + newWin);
+                }
+            }
+        }
+
+        /* helper - construct and send a DRAG_STARTED event only if the window has not
+         * previously been notified, i.e. it became visible after the drag operation
+         * was begun.  This is a rare case.
+         */
+        private void sendDragStartedIfNeededLw(WindowState newWin) {
+            // If we have sent the drag-started, we needn't do so again
+            for (WindowState ws : mNotifiedWindows) {
+                if (ws == newWin) {
+                    return;
+                }
+            }
+            if (DEBUG_DRAG) {
+                Slog.d(TAG, "sending DRAG_STARTED to new window " + newWin);
+            }
+            DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED, 0, 0,
+                    mDataDescription, null);
+            sendDragStartedLw(newWin, event);
+            event.recycle();
+        }
+
+        void broadcastDragEnded() {
+            if (DEBUG_DRAG) {
+                Slog.d(TAG, "broadcasting DRAG_ENDED");
+            }
+            DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED, 0, 0, null, null);
+            synchronized (mWindowMap) {
+                for (WindowState ws: mNotifiedWindows) {
+                    try {
+                        ws.mClient.dispatchDragEvent(evt);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Unable to drag-end window " + ws);
+                    }
+                }
+                mNotifiedWindows.clear();
+                mDragEnded = true;
+            }
+            evt.recycle();
+        }
+
+        void notifyMoveLw(float x, float y) {
+            WindowState touchedWin = getTouchedWinAtPointLw(x, y);
+            try {
+                // have we dragged over a new window?
+                if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
+                    if (DEBUG_DRAG) {
+                        Slog.d(TAG, "sending DRAG_EXITED to " + mTargetWindow);
+                    }
+                    // force DRAG_EXITED_EVENT if appropriate
+                    DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED,
+                            0, 0, null, null);
+                    mTargetWindow.mClient.dispatchDragEvent(evt);
+                    evt.recycle();
+                }
+                if (touchedWin != null) {
+                    if (DEBUG_DRAG) {
+                        Slog.d(TAG, "sending DRAG_LOCATION to " + touchedWin);
+                    }
+                    DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION,
+                            x, y, null, null);
+                    touchedWin.mClient.dispatchDragEvent(evt);
+                    evt.recycle();
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "can't send drag notification to windows");
+            }
+            mTargetWindow = touchedWin;
+        }
+
+        // Tell the drop target about the data, and then broadcast the drag-ended notification
+        void notifyDropLw(float x, float y) {
+            WindowState touchedWin = getTouchedWinAtPointLw(x, y);
+            if (touchedWin != null) {
+                if (DEBUG_DRAG) {
+                    Slog.d(TAG, "sending DROP to " + touchedWin);
+                }
+                DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP, x, y, null, mData);
+                try {
+                    touchedWin.mClient.dispatchDragEvent(evt);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "can't send drop notification to win " + touchedWin);
+                }
+                evt.recycle();
+            }
+        }
+
+        // Find the visible, touch-deliverable window under the given point
+        private WindowState getTouchedWinAtPointLw(float xf, float yf) {
+            WindowState touchedWin = null;
+            final int x = (int) xf;
+            final int y = (int) yf;
+            final ArrayList<WindowState> windows = mWindows;
+            final int N = windows.size();
+            for (int i = N - 1; i >= 0; i--) {
+                WindowState child = windows.get(i);
+                final int flags = child.mAttrs.flags;
+                if (!child.isVisibleLw()) {
+                    // not visible == don't tell about drags
+                    continue;
+                }
+                if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
+                    // not touchable == don't tell about drags
+                    continue;
+                }
+                // account for the window's decor etc
+                tmpRect.set(child.mFrame);
+                if (child.mTouchableInsets == ViewTreeObserver
+                            .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
+                    // The point is inside of the window if it is
+                    // inside the frame, AND the content part of that
+                    // frame that was given by the application.
+                    tmpRect.left += child.mGivenContentInsets.left;
+                    tmpRect.top += child.mGivenContentInsets.top;
+                    tmpRect.right -= child.mGivenContentInsets.right;
+                    tmpRect.bottom -= child.mGivenContentInsets.bottom;
+                } else if (child.mTouchableInsets == ViewTreeObserver
+                            .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) {
+                    // The point is inside of the window if it is
+                    // inside the frame, AND the visible part of that
+                    // frame that was given by the application.
+                    tmpRect.left += child.mGivenVisibleInsets.left;
+                    tmpRect.top += child.mGivenVisibleInsets.top;
+                    tmpRect.right -= child.mGivenVisibleInsets.right;
+                    tmpRect.bottom -= child.mGivenVisibleInsets.bottom;
+                }
+                final int touchFlags = flags &
+                    (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+                if (tmpRect.contains(x, y) || touchFlags == 0) {
+                    // Found it
+                    touchedWin = child;
+                    break;
+                }
+            }
+
+            return touchedWin;
+        }
+    }
+
+    DragState mDragState = null;
+    private final InputHandler mDragInputHandler = new BaseInputHandler() {
+        @Override
+        public void handleMotion(MotionEvent event, Runnable finishedCallback) {
+            boolean endDrag = false;
+            final float newX = event.getRawX();
+            final float newY = event.getRawY();
+
+            try {
+                if (mDragState != null) {
+                    switch (event.getAction()) {
+                    case MotionEvent.ACTION_DOWN: {
+                        if (DEBUG_DRAG) {
+                            Slog.w(TAG, "Unexpected ACTION_DOWN in drag layer");
+                        }
+                    } break;
+
+                    case MotionEvent.ACTION_MOVE: {
+                        synchronized (mWindowMap) {
+                            // move the surface to the latest touch point
+                            mDragState.mSurface.openTransaction();
+                            mDragState.mSurface.setPosition((int)(newX - mDragState.mThumbOffsetX),
+                                    (int)(newY - mDragState.mThumbOffsetY));
+                            mDragState.mSurface.closeTransaction();
+
+                            // tell the involved window(s) where we are
+                            mDragState.notifyMoveLw(newX, newY);
+                        }
+                    } break;
+
+                    case MotionEvent.ACTION_UP: {
+                        if (DEBUG_DRAG) Slog.d(TAG, "Got UP on move channel; dropping at "
+                                + newX + "," + newY);
+                        synchronized (mWindowMap) {
+                            mDragState.notifyDropLw(newX, newY);
+                        }
+                        endDrag = true;
+                    } break;
+
+                    case MotionEvent.ACTION_CANCEL: {
+                        if (DEBUG_DRAG) Slog.d(TAG, "Drag cancelled!");
+                        endDrag = true;
+                    } break;
+                    }
+
+                    if (endDrag) {
+                        if (DEBUG_DRAG) Slog.d(TAG, "Drag ended; tearing down state");
+                        // tell all the windows that the drag has ended
+                        mDragState.broadcastDragEnded();
+
+                        // stop intercepting input
+                        mDragState.unregister();
+                        mInputMonitor.updateInputWindowsLw();
+
+                        // free our resources and drop all the object references
+                        mDragState.reset();
+                        mDragState = null;
+                    }
+                }
+            } catch (Exception e) {
+                Slog.e(TAG, "Exception caught by drag handleMotion", e);
+            } finally {
+                finishedCallback.run();
+            }
+        }
+    };
+
+    /**
      * Whether the UI is currently running in touch mode (not showing
      * navigational focus because the user is directly pressing the screen).
      */
@@ -5046,7 +5358,61 @@
         mPolicy.adjustConfigurationLw(config);
         return true;
     }
-    
+
+    // -------------------------------------------------------------
+    // Drag and drop
+    // -------------------------------------------------------------
+
+    IBinder prepareDragSurface(IWindow window, SurfaceSession session,
+            boolean localOnly, int width, int height, Surface outSurface) {
+        if (DEBUG_DRAG) {
+            Slog.d(TAG, "prepare drag surface: w=" + width + " h=" + height
+                    + " local=" + localOnly + " win=" + window
+                    + " asbinder=" + window.asBinder());
+        }
+
+        final int callerPid = Binder.getCallingPid();
+        final long origId = Binder.clearCallingIdentity();
+        IBinder token = null;
+
+        try {
+            synchronized (mWindowMap) {
+                try {
+                    // !!! TODO: fail if the given window does not currently have touch focus?
+
+                    if (mDragState == null) {
+                        Surface surface = new Surface(session, callerPid, "drag surface", 0,
+                                width, height, PixelFormat.TRANSLUCENT, Surface.HIDDEN);
+                        outSurface.copyFrom(surface);
+                        token = new Binder();
+                        mDragState = new DragState(token, surface, localOnly);
+                        mDragState.mSurface = surface;
+                        mDragState.mLocalOnly = localOnly;
+                        token = mDragState.mToken = new Binder();
+
+                        // 5 second timeout for this window to actually begin the drag
+                        mH.removeMessages(H.DRAG_START_TIMEOUT, window);
+                        Message msg = mH.obtainMessage(H.DRAG_START_TIMEOUT, window.asBinder());
+                        mH.sendMessageDelayed(msg, 5000);
+                    } else {
+                        Slog.w(TAG, "Drag already in progress");
+                    }
+                } catch (Surface.OutOfResourcesException e) {
+                    Slog.e(TAG, "Can't allocate drag surface w=" + width + " h=" + height, e);
+                    if (mDragState != null) {
+                        mDragState.reset();
+                        mDragState = null;
+                    }
+                    
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+
+        return token;
+    }
+
     // -------------------------------------------------------------
     // Input Events and Focus Management
     // -------------------------------------------------------------
@@ -5145,7 +5511,42 @@
             
             return null;
         }
-        
+
+        private void addDragInputWindow(InputWindowList windowList) {
+            final InputWindow inputWindow = windowList.add();
+            inputWindow.inputChannel = mDragState.mServerChannel;
+            inputWindow.name = "drag";
+            inputWindow.layoutParamsFlags = 0;
+            inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
+            inputWindow.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+            inputWindow.visible = true;
+            inputWindow.canReceiveKeys = false;
+            inputWindow.hasFocus = true;
+            inputWindow.hasWallpaper = false;
+            inputWindow.paused = false;
+            inputWindow.layer = mPolicy.windowTypeToLayerLw(inputWindow.layoutParamsType)
+                    * TYPE_LAYER_MULTIPLIER
+                    + TYPE_LAYER_OFFSET;
+            inputWindow.ownerPid = Process.myPid();
+            inputWindow.ownerUid = Process.myUid();
+
+            // The drag window covers the entire display
+            inputWindow.frameLeft = 0;
+            inputWindow.frameTop = 0;
+            inputWindow.frameRight = mDisplay.getWidth();
+            inputWindow.frameBottom = mDisplay.getHeight();
+            
+            inputWindow.visibleFrameLeft = inputWindow.frameLeft;
+            inputWindow.visibleFrameTop = inputWindow.frameTop;
+            inputWindow.visibleFrameRight = inputWindow.frameRight;
+            inputWindow.visibleFrameBottom = inputWindow.frameBottom;
+
+            inputWindow.touchableAreaLeft = inputWindow.frameLeft;
+            inputWindow.touchableAreaTop = inputWindow.frameTop;
+            inputWindow.touchableAreaRight = inputWindow.frameRight;
+            inputWindow.touchableAreaBottom = inputWindow.frameBottom;
+        }
+
         /* Updates the cached window information provided to the input dispatcher. */
         public void updateInputWindowsLw() {
             // Populate the input window list with information about all of the windows that
@@ -5154,6 +5555,16 @@
             // out to be difficult because only the native code knows for sure which window
             // currently has touch focus.
             final ArrayList<WindowState> windows = mWindows;
+
+            // If there's a drag in flight, provide a pseudowindow to catch drag input
+            final boolean inDrag = (mDragState != null);
+            if (inDrag) {
+                if (DEBUG_DRAG) {
+                    Log.d(TAG, "Inserting drag window");
+                }
+                addDragInputWindow(mTempInputWindows);
+            }
+
             final int N = windows.size();
             for (int i = N - 1; i >= 0; i--) {
                 final WindowState child = windows.get(i);
@@ -5169,7 +5580,13 @@
                 final boolean isVisible = child.isVisibleLw();
                 final boolean hasWallpaper = (child == mWallpaperTarget)
                         && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
-                
+
+                // If there's a drag in progress and 'child' is a potential drop target,
+                // make sure it's been told about the drag
+                if (inDrag && isVisible) {
+                    mDragState.sendDragStartedIfNeededLw(child);
+                }
+
                 // Add a window to our list of input windows.
                 final InputWindow inputWindow = mTempInputWindows.add();
                 inputWindow.inputChannel = child.mInputChannel;
@@ -5741,6 +6158,87 @@
             }
         }
 
+        /* Drag/drop */
+        public IBinder prepareDrag(IWindow window, boolean localOnly,
+                int width, int height, Surface outSurface) {
+            return prepareDragSurface(window, mSurfaceSession, localOnly,
+                    width, height, outSurface);
+        }
+
+        public boolean performDrag(IWindow window, IBinder dragToken,
+                float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+                ClipData data) {
+            if (DEBUG_DRAG) {
+                Slog.d(TAG, "perform drag: win=" + window + " data=" + data);
+            }
+
+            synchronized (mWindowMap) {
+                if (mDragState == null) {
+                    Slog.w(TAG, "No drag prepared");
+                    throw new IllegalStateException("performDrag() without prepareDrag()");
+                }
+
+                if (dragToken != mDragState.mToken) {
+                    Slog.w(TAG, "Performing mismatched drag");
+                    throw new IllegalStateException("performDrag() does not match prepareDrag()");
+                }
+
+                WindowState callingWin = windowForClientLocked(null, window, false);
+                if (callingWin == null) {
+                    Slog.w(TAG, "Bad requesting window " + window);
+                    return false;  // !!! TODO: throw here?
+                }
+
+                // !!! TODO: if input is not still focused on the initiating window, fail
+                // the drag initiation (e.g. an alarm window popped up just as the application
+                // called performDrag()
+
+                mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());
+
+                // !!! TODO: call into the input monitor to sever the current touch event flow
+                // and redirect to the drag "window"; also extract the current touch (x, y)
+                // in screen coordinates
+
+                mDragState.register();
+                mInputMonitor.updateInputWindowsLw();
+                mInputManager.transferTouchFocus(callingWin.mInputChannel,
+                        mDragState.mServerChannel);
+
+                mDragState.mData = data;
+                mDragState.broadcastDragStartedLw();
+
+                // remember the thumb offsets for later
+                mDragState.mThumbOffsetX = thumbCenterX;
+                mDragState.mThumbOffsetY = thumbCenterY;
+
+                // Make the surface visible at the proper location
+                final Surface surface = mDragState.mSurface;
+                surface.openTransaction();
+                try {
+                    surface.setPosition((int)(touchX - thumbCenterX),
+                            (int)(touchY - thumbCenterY));
+                    surface.setAlpha(.5f);
+                    surface.show();
+                } finally {
+                    surface.closeTransaction();
+                }
+            }
+
+            return true;    // success!
+        }
+
+        public void dragRecipientEntered(IWindow window) {
+            if (DEBUG_DRAG) {
+                Slog.d(TAG, "Drag into new candidate view @ " + window);
+            }
+        }
+
+        public void dragRecipientExited(IWindow window) {
+            if (DEBUG_DRAG) {
+                Slog.d(TAG, "Drag from old candidate view @ " + window);
+            }
+        }
+
         public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
             synchronized(mWindowMap) {
                 long ident = Binder.clearCallingIdentity();
@@ -6882,6 +7380,15 @@
         }
 
         /**
+         * Can this window possibly be a drag/drop target?  The test here is
+         * a combination of the above "visible now" with the check that the
+         * Input Manager uses when discarding windows from input consideration.
+         */
+        boolean isPotentialDragTarget() {
+            return isVisibleNow() && (mInputChannel != null) && !mRemoved;
+        }
+
+        /**
          * Same as isVisible(), but we also count it as visible between the
          * call to IWindowSession.add() and the first relayout().
          */
@@ -6968,34 +7475,6 @@
                 && !mDrawPending && !mCommitDrawPending;
         }
 
-        public boolean fillsScreenLw(int screenWidth, int screenHeight,
-                                   boolean shownFrame, boolean onlyOpaque) {
-            if (mSurface == null) {
-                return false;
-            }
-            if (mAppToken != null && !mAppToken.appFullscreen) {
-                return false;
-            }
-            if (onlyOpaque && mAttrs.format != PixelFormat.OPAQUE) {
-                return false;
-            }
-            final Rect frame = shownFrame ? mShownFrame : mFrame;
-
-            if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) {
-                return frame.left <= mCompatibleScreenFrame.left &&
-                        frame.top <= mCompatibleScreenFrame.top &&
-                        frame.right >= mCompatibleScreenFrame.right &&
-                        frame.bottom >= mCompatibleScreenFrame.bottom;
-            } else {
-                if ((mAttrs.flags & FLAG_FULLSCREEN) != 0) {
-                    return true;
-                }
-                return frame.left <= 0 && frame.top <= 0
-                        && frame.right >= screenWidth
-                        && frame.bottom >= screenHeight;
-            }
-        }
-
         /**
          * Return true if the window is opaque and fully drawn.  This indicates
          * it may obscure windows behind it.
@@ -7810,6 +8289,7 @@
         public static final int APP_FREEZE_TIMEOUT = 17;
         public static final int SEND_NEW_CONFIGURATION = 18;
         public static final int REPORT_WINDOWS_CHANGE = 19;
+        public static final int DRAG_START_TIMEOUT = 20;
 
         private Session mLastReportedHold;
 
@@ -8152,6 +8632,20 @@
                     break;
                 }
 
+                case DRAG_START_TIMEOUT: {
+                    IBinder win = (IBinder)msg.obj;
+                    if (DEBUG_DRAG) {
+                        Slog.w(TAG, "Timeout starting drag by win " + win);
+                    }
+                    synchronized (mWindowMap) {
+                        // !!! TODO: ANR the app that has failed to start the drag in time
+                        if (mDragState != null) {
+                            mDragState.reset();
+                            mDragState = null;
+                        }
+                    }
+                }
+
             }
         }
     }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5c4b919..1eab7fc7 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -113,9 +113,15 @@
 import android.view.WindowManager;
 import android.view.WindowManagerPolicy;
 
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
@@ -6123,6 +6129,76 @@
         return mSystemReady;
     }
     
+    private static File getCalledPreBootReceiversFile() {
+        File dataDir = Environment.getDataDirectory();
+        File systemDir = new File(dataDir, "system");
+        File fname = new File(systemDir, "called_pre_boots.dat");
+        return fname;
+    }
+    
+    private static ArrayList<ComponentName> readLastDonePreBootReceivers() {
+        ArrayList<ComponentName> lastDoneReceivers = new ArrayList<ComponentName>();
+        File file = getCalledPreBootReceiversFile();
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(file);
+            DataInputStream dis = new DataInputStream(new BufferedInputStream(fis, 2048));
+            int vers = dis.readInt();
+            String codename = dis.readUTF();
+            if (vers == android.os.Build.VERSION.SDK_INT
+                    && codename.equals(android.os.Build.VERSION.CODENAME)) {
+                int num = dis.readInt();
+                while (num > 0) {
+                    num--;
+                    String pkg = dis.readUTF();
+                    String cls = dis.readUTF();
+                    lastDoneReceivers.add(new ComponentName(pkg, cls));
+                }
+            }
+        } catch (FileNotFoundException e) {
+        } catch (IOException e) {
+            Slog.w(TAG, "Failure reading last done pre-boot receivers", e);
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return lastDoneReceivers;
+    }
+    
+    private static void writeLastDonePreBootReceivers(ArrayList<ComponentName> list) {
+        File file = getCalledPreBootReceiversFile();
+        FileOutputStream fos = null;
+        DataOutputStream dos = null;
+        try {
+            Slog.i(TAG, "Writing new set of last done pre-boot receivers...");
+            fos = new FileOutputStream(file);
+            dos = new DataOutputStream(new BufferedOutputStream(fos, 2048));
+            dos.writeInt(android.os.Build.VERSION.SDK_INT);
+            dos.writeUTF(android.os.Build.VERSION.CODENAME);
+            dos.writeInt(list.size());
+            for (int i=0; i<list.size(); i++) {
+                dos.writeUTF(list.get(i).getPackageName());
+                dos.writeUTF(list.get(i).getClassName());
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Failure writing last done pre-boot receivers", e);
+            file.delete();
+        } finally {
+            if (dos != null) {
+                try {
+                    dos.close();
+                } catch (IOException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+    
     public void systemReady(final Runnable goingCallback) {
         // In the simulator, startRunning will never have been called, which
         // normally sets a few crucial variables. Do it here instead.
@@ -6157,9 +6233,24 @@
                         }
                     }
                     intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
+                    
+                    ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
+                    
+                    final ArrayList<ComponentName> doneReceivers = new ArrayList<ComponentName>();
                     for (int i=0; i<ris.size(); i++) {
                         ActivityInfo ai = ris.get(i).activityInfo;
-                        intent.setComponent(new ComponentName(ai.packageName, ai.name));
+                        ComponentName comp = new ComponentName(ai.packageName, ai.name);
+                        if (lastDoneReceivers.contains(comp)) {
+                            ris.remove(i);
+                            i--;
+                        }
+                    }
+                    
+                    for (int i=0; i<ris.size(); i++) {
+                        ActivityInfo ai = ris.get(i).activityInfo;
+                        ComponentName comp = new ComponentName(ai.packageName, ai.name);
+                        doneReceivers.add(comp);
+                        intent.setComponent(comp);
                         IIntentReceiver finisher = null;
                         if (i == ris.size()-1) {
                             finisher = new IIntentReceiver.Stub() {
@@ -6174,6 +6265,7 @@
                                             synchronized (ActivityManagerService.this) {
                                                 mDidUpdate = true;
                                             }
+                                            writeLastDonePreBootReceivers(doneReceivers);
                                             systemReady(goingCallback);
                                         }
                                     });
@@ -6213,19 +6305,19 @@
             }
         }
         
-        if (procsToKill != null) {
-            synchronized(this) {
+        synchronized(this) {
+            if (procsToKill != null) {
                 for (int i=procsToKill.size()-1; i>=0; i--) {
                     ProcessRecord proc = procsToKill.get(i);
                     Slog.i(TAG, "Removing system update proc: " + proc);
                     removeProcessLocked(proc, true);
                 }
-
-                // Now that we have cleaned up any update processes, we
-                // are ready to start launching real processes and know that
-                // we won't trample on them any more.
-                mProcessesReady = true;
             }
+            
+            // Now that we have cleaned up any update processes, we
+            // are ready to start launching real processes and know that
+            // we won't trample on them any more.
+            mProcessesReady = true;
         }
         
         Slog.i(TAG, "System now ready");
@@ -7741,7 +7833,7 @@
     
             pw.println(" ");
             pw.println("Receiver Resolver Table:");
-            mReceiverResolver.dump(pw, null, "  ", null);
+            mReceiverResolver.dump(pw, null, "  ", null, false);
             needSep = true;
         }
         
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index bf4db80..6bd89cc 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -444,10 +444,11 @@
                     sb.append(shortComponentName);
                     sb.append(": ");
                     TimeUtils.formatDuration(thisTime, sb);
-                    sb.append(" (total ");
-                    TimeUtils.formatDuration(totalTime, sb);
-                    sb.append(totalTime);
-                    sb.append(")");
+                    if (thisTime != totalTime) {
+                        sb.append(" (total ");
+                        TimeUtils.formatDuration(totalTime, sb);
+                        sb.append(")");
+                    }
                     Log.i(ActivityManagerService.TAG, sb.toString());
                 }
                 stack.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index a4497d6..30395c0 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1881,7 +1881,7 @@
             String resultWho, int requestCode,
             int callingPid, int callingUid, boolean onlyIfNeeded,
             boolean componentSpecified) {
-        Slog.i(TAG, "Starting activity: " + intent);
+        Slog.i(TAG, "Starting: " + intent);
 
         ActivityRecord sourceRecord = null;
         ActivityRecord resultRecord = null;
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index 3f15d0a..8463b5a 100644
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -57,6 +57,7 @@
 public final class UsageStatsService extends IUsageStats.Stub {
     public static final String SERVICE_NAME = "usagestats";
     private static final boolean localLOGV = false;
+    private static final boolean REPORT_UNEXPECTED = false;
     private static final String TAG = "UsageStats";
     
     // Current on-disk Parcel version
@@ -404,11 +405,11 @@
                 new Thread("UsageStatsService_DiskWriter") {
                     public void run() {
                         try {
-                            Slog.d(TAG, "Disk writer thread starting.");
+                            if (localLOGV) Slog.d(TAG, "Disk writer thread starting.");
                             writeStatsToFile(true);
                         } finally {
                             mUnforcedDiskWriteRunning.set(false);
-                            Slog.d(TAG, "Disk writer thread ending.");
+                            if (localLOGV) Slog.d(TAG, "Disk writer thread ending.");
                         }
                     }
                 }.start();
@@ -458,7 +459,7 @@
                 }
             }
         }
-        Slog.d(TAG, "Dumped usage stats.");
+        if (localLOGV) Slog.d(TAG, "Dumped usage stats.");
     }
 
     private void writeStatsFLOCK(File file) throws IOException {
@@ -493,7 +494,7 @@
     }
     
     public void shutdown() {
-        Slog.w(TAG, "Writing usage stats before shutdown...");
+        Slog.i(TAG, "Writing usage stats before shutdown...");
         writeStatsToFile(true);
     }
     
@@ -520,7 +521,7 @@
                 if (mLastResumedPkg != null) {
                     // We last resumed some other package...  just pause it now
                     // to recover.
-                    Slog.i(TAG, "Unexpected resume of " + pkgName
+                    if (REPORT_UNEXPECTED) Slog.i(TAG, "Unexpected resume of " + pkgName
                             + " while already resumed in " + mLastResumedPkg);
                     PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg);
                     if (pus != null) {
@@ -559,7 +560,7 @@
                 return;
             }
             if (!mIsResumed) {
-                Slog.i(TAG, "Something wrong here, didn't expect "
+                if (REPORT_UNEXPECTED) Slog.i(TAG, "Something wrong here, didn't expect "
                         + pkgName + " to be paused");
                 return;
             }
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index e1e54fc..3d67e8d 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -182,7 +182,6 @@
 
     virtual bool getDisplayInfo(int32_t displayId,
             int32_t* width, int32_t* height, int32_t* orientation);
-    virtual void virtualKeyDownFeedback();
     virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
             bool down, int32_t keyCode, int32_t scanCode, uint32_t& policyFlags);
     virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
@@ -464,17 +463,6 @@
     return android_server_PowerManagerService_isScreenBright();
 }
 
-void NativeInputManager::virtualKeyDownFeedback() {
-#if DEBUG_INPUT_READER_POLICY
-    LOGD("virtualKeyDownFeedback");
-#endif
-
-    JNIEnv* env = jniEnv();
-
-    env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyDownFeedback);
-    checkAndClearExceptionFromCallback(env, "virtualKeyDownFeedback");
-}
-
 int32_t NativeInputManager::interceptKey(nsecs_t when,
         int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t& policyFlags) {
 #if DEBUG_INPUT_READER_POLICY
@@ -483,6 +471,12 @@
             when, deviceId, down, keyCode, scanCode, policyFlags);
 #endif
 
+    if (down && (policyFlags & POLICY_FLAG_VIRTUAL)) {
+        JNIEnv* env = jniEnv();
+        env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyDownFeedback);
+        checkAndClearExceptionFromCallback(env, "virtualKeyDownFeedback");
+    }
+
     const int32_t WM_ACTION_PASS_TO_USER = 1;
     const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
     const int32_t WM_ACTION_GO_TO_SLEEP = 4;
@@ -1289,6 +1283,7 @@
 static jboolean android_server_InputManager_nativeTransferTouchFocus(JNIEnv* env,
         jclass clazz, jobject fromChannelObj, jobject toChannelObj) {
     if (checkInputManagerUnitialized(env)) {
+        LOGD("input manager uninitialized; bailing");
         return false;
     }
 
@@ -1298,6 +1293,7 @@
             android_view_InputChannel_getInputChannel(env, toChannelObj);
 
     if (fromChannel == NULL || toChannel == NULL) {
+        LOGD("bailing because from=%p to=%p", fromChannel, toChannel);
         return false;
     }
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 1b21a8d..fb76720 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -271,6 +271,17 @@
     }
 }
 
+void Layer::drawForSreenShot() const
+{
+    bool currentFixedSize = mFixedSize;
+    bool currentBlending = mNeedsBlending;
+    const_cast<Layer*>(this)->mFixedSize = false;
+    const_cast<Layer*>(this)->mFixedSize = true;
+    LayerBase::drawForSreenShot();
+    const_cast<Layer*>(this)->mFixedSize = currentFixedSize;
+    const_cast<Layer*>(this)->mNeedsBlending = currentBlending;
+}
+
 void Layer::onDraw(const Region& clip) const
 {
     Texture tex(mBufferManager.getActiveTexture());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 188da6a..caa6d17 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -70,6 +70,7 @@
     // LayerBase interface
     virtual void setGeometry(hwc_layer_t* hwcl);
     virtual void setPerFrameData(hwc_layer_t* hwcl);
+    virtual void drawForSreenShot() const;
     virtual void onDraw(const Region& clip) const;
     virtual uint32_t doTransaction(uint32_t transactionFlags);
     virtual void lockPageFlip(bool& recomputeVisibleRegions);
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 3d049a7..14191cb 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -326,6 +326,12 @@
     onDraw(clip);
 }
 
+void LayerBase::drawForSreenShot() const
+{
+    const DisplayHardware& hw(graphicPlane(0).displayHardware());
+    onDraw( Region(hw.bounds()) );
+}
+
 void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red,
                                 GLclampf green, GLclampf blue,
                                 GLclampf alpha) const
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index c66dc34..bdee05b 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -121,6 +121,7 @@
      * to perform the actual drawing.  
      */
     virtual void draw(const Region& clip) const;
+    virtual void drawForSreenShot() const;
     
     /**
      * onDraw - draws the surface.
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index fdf9abc..c060895 100644
--- a/services/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -132,6 +132,12 @@
     LayerBase::unlockPageFlip(planeTransform, outDirtyRegion);    
 }
 
+void LayerBuffer::drawForSreenShot() const
+{
+    const DisplayHardware& hw(graphicPlane(0).displayHardware());
+    clearWithOpenGL( Region(hw.bounds()) );
+}
+
 void LayerBuffer::onDraw(const Region& clip) const
 {
     sp<Source> source(getSource());
diff --git a/services/surfaceflinger/LayerBuffer.h b/services/surfaceflinger/LayerBuffer.h
index 1c0bf83..fece858 100644
--- a/services/surfaceflinger/LayerBuffer.h
+++ b/services/surfaceflinger/LayerBuffer.h
@@ -64,6 +64,7 @@
     virtual sp<LayerBaseClient::Surface> createSurface() const;
     virtual status_t ditch();
     virtual void onDraw(const Region& clip) const;
+    virtual void drawForSreenShot() const;
     virtual uint32_t doTransaction(uint32_t flags);
     virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 17b98a6..e6bdfd1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1653,9 +1653,117 @@
 
 // ---------------------------------------------------------------------------
 
+status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,
+        sp<IMemoryHeap>* heap,
+        uint32_t* w, uint32_t* h, PixelFormat* f,
+        uint32_t sw, uint32_t sh)
+{
+    status_t result = PERMISSION_DENIED;
+
+    // only one display supported for now
+    if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+        return BAD_VALUE;
+
+    if (!GLExtensions::getInstance().haveFramebufferObject())
+        return INVALID_OPERATION;
+
+    // get screen geometry
+    const DisplayHardware& hw(graphicPlane(dpy).displayHardware());
+    const uint32_t hw_w = hw.getWidth();
+    const uint32_t hw_h = hw.getHeight();
+
+    if ((sw > hw_w) || (sh > hw_h))
+        return BAD_VALUE;
+
+    sw = (!sw) ? hw_w : sw;
+    sh = (!sh) ? hw_h : sh;
+    const size_t size = sw * sh * 4;
+
+    // make sure to clear all GL error flags
+    while ( glGetError() != GL_NO_ERROR ) ;
+
+    // create a FBO
+    GLuint name, tname;
+    glGenRenderbuffersOES(1, &tname);
+    glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
+    glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
+    glGenFramebuffersOES(1, &name);
+    glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
+    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
+            GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
+
+    GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
+    if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
+
+        // invert everything, b/c glReadPixel() below will invert the FB
+        glViewport(0, 0, sw, sh);
+        glMatrixMode(GL_PROJECTION);
+        glPushMatrix();
+        glLoadIdentity();
+        glOrthof(0, hw_w, 0, hw_h, 0, 1);
+        glMatrixMode(GL_MODELVIEW);
+
+        // redraw the screen entirely...
+        glClearColor(0,0,0,1);
+        glClear(GL_COLOR_BUFFER_BIT);
+        const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+        const size_t count = layers.size();
+        for (size_t i=0 ; i<count ; ++i) {
+            const sp<LayerBase>& layer(layers[i]);
+            layer->drawForSreenShot();
+        }
+
+        // XXX: this is needed on tegra
+        glScissor(0, 0, sw, sh);
+
+        // check for errors and return screen capture
+        if (glGetError() != GL_NO_ERROR) {
+            // error while rendering
+            result = INVALID_OPERATION;
+        } else {
+            // allocate shared memory large enough to hold the
+            // screen capture
+            sp<MemoryHeapBase> base(
+                    new MemoryHeapBase(size, 0, "screen-capture") );
+            void* const ptr = base->getBase();
+            if (ptr) {
+                // capture the screen with glReadPixels()
+                glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
+                if (glGetError() == GL_NO_ERROR) {
+                    *heap = base;
+                    *w = sw;
+                    *h = sh;
+                    *f = PIXEL_FORMAT_RGBA_8888;
+                    result = NO_ERROR;
+                }
+            } else {
+                result = NO_MEMORY;
+            }
+        }
+
+        glEnable(GL_SCISSOR_TEST);
+        glViewport(0, 0, hw_w, hw_h);
+        glMatrixMode(GL_PROJECTION);
+        glPopMatrix();
+        glMatrixMode(GL_MODELVIEW);
+
+
+    } else {
+        result = BAD_VALUE;
+    }
+
+    // release FBO resources
+    glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+    glDeleteRenderbuffersOES(1, &tname);
+    glDeleteFramebuffersOES(1, &name);
+    return result;
+}
+
+
 status_t SurfaceFlinger::captureScreen(DisplayID dpy,
         sp<IMemoryHeap>* heap,
-        uint32_t* width, uint32_t* height, PixelFormat* format)
+        uint32_t* width, uint32_t* height, PixelFormat* format,
+        uint32_t sw, uint32_t sh)
 {
     // only one display supported for now
     if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
@@ -1671,12 +1779,15 @@
         uint32_t* w;
         uint32_t* h;
         PixelFormat* f;
+        uint32_t sw;
+        uint32_t sh;
         status_t result;
     public:
         MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy,
-                sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f)
+                sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f,
+                uint32_t sw, uint32_t sh)
             : flinger(flinger), dpy(dpy),
-              heap(heap), w(w), h(h), f(f), result(PERMISSION_DENIED)
+              heap(heap), w(w), h(h), f(f), sw(sw), sh(sh), result(PERMISSION_DENIED)
         {
         }
         status_t getResult() const {
@@ -1689,94 +1800,15 @@
             if (flinger->mSecureFrameBuffer)
                 return true;
 
-            // make sure to clear all GL error flags
-            while ( glGetError() != GL_NO_ERROR ) ;
+            result = flinger->captureScreenImplLocked(dpy,
+                    heap, w, h, f, sw, sh);
 
-            // get screen geometry
-            const DisplayHardware& hw(flinger->graphicPlane(dpy).displayHardware());
-            const uint32_t sw = hw.getWidth();
-            const uint32_t sh = hw.getHeight();
-            const Region screenBounds(hw.bounds());
-            const size_t size = sw * sh * 4;
-
-            // create a FBO
-            GLuint name, tname;
-            glGenRenderbuffersOES(1, &tname);
-            glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
-            glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
-            glGenFramebuffersOES(1, &name);
-            glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
-            glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
-                    GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
-
-            GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
-            if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
-
-                // invert everything, b/c glReadPixel() below will invert the FB
-                glMatrixMode(GL_PROJECTION);
-                glPushMatrix();
-                glLoadIdentity();
-                glOrthof(0, sw, 0, sh, 0, 1);
-                glMatrixMode(GL_MODELVIEW);
-
-                // redraw the screen entirely...
-                glClearColor(0,0,0,1);
-                glClear(GL_COLOR_BUFFER_BIT);
-                const Vector< sp<LayerBase> >& layers(
-                        flinger->mVisibleLayersSortedByZ);
-                const size_t count = layers.size();
-                for (size_t i=0 ; i<count ; ++i) {
-                    const sp<LayerBase>& layer(layers[i]);
-                    if (!strcmp(layer->getTypeId(), "LayerBuffer")) {
-                        // we cannot render LayerBuffer because it doens't
-                        // use OpenGL, and won't show-up in the FBO.
-                        continue;
-                    }
-                    layer->draw(screenBounds);
-                }
-
-                glMatrixMode(GL_PROJECTION);
-                glPopMatrix();
-                glMatrixMode(GL_MODELVIEW);
-
-                // check for errors and return screen capture
-                if (glGetError() != GL_NO_ERROR) {
-                    // error while rendering
-                    result = INVALID_OPERATION;
-                } else {
-                    // allocate shared memory large enough to hold the
-                    // screen capture
-                    sp<MemoryHeapBase> base(
-                            new MemoryHeapBase(size, 0, "screen-capture") );
-                    void* const ptr = base->getBase();
-                    if (ptr) {
-                        // capture the screen with glReadPixels()
-                        glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
-                        if (glGetError() == GL_NO_ERROR) {
-                            *heap = base;
-                            *w = sw;
-                            *h = sh;
-                            *f = PIXEL_FORMAT_RGBA_8888;
-                            result = NO_ERROR;
-                        }
-                    } else {
-                        result = NO_MEMORY;
-                    }
-                }
-            } else {
-                result = BAD_VALUE;
-            }
-
-            // release FBO resources
-            glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
-            glDeleteRenderbuffersOES(1, &tname);
-            glDeleteFramebuffersOES(1, &name);
             return true;
         }
     };
 
     sp<MessageBase> msg = new MessageCaptureScreen(this,
-            dpy, heap, width, height, format);
+            dpy, heap, width, height, format, sw, sh);
     status_t res = postMessageSync(msg);
     if (res == NO_ERROR) {
         res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 6e9ecbd..732e57e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -197,7 +197,9 @@
                                                       sp<IMemoryHeap>* heap,
                                                       uint32_t* width,
                                                       uint32_t* height,
-                                                      PixelFormat* format);
+                                                      PixelFormat* format,
+                                                      uint32_t reqWidth,
+                                                      uint32_t reqHeight);
 
             void                        screenReleased(DisplayID dpy);
             void                        screenAcquired(DisplayID dpy);
@@ -319,6 +321,11 @@
             void        commitTransaction();
 
 
+            status_t captureScreenImplLocked(DisplayID dpy,
+                    sp<IMemoryHeap>* heap,
+                    uint32_t* width, uint32_t* height, PixelFormat* format,
+                    uint32_t reqWidth = 0, uint32_t reqHeight = 0);
+
             friend class FreezeLock;
             sp<FreezeLock> getFreezeLock() const;
             inline void incFreezeCount() {
diff --git a/services/surfaceflinger/tests/screencap/screencap.cpp b/services/surfaceflinger/tests/screencap/screencap.cpp
index 9e893f4..6cf1504 100644
--- a/services/surfaceflinger/tests/screencap/screencap.cpp
+++ b/services/surfaceflinger/tests/screencap/screencap.cpp
@@ -42,7 +42,7 @@
     sp<IMemoryHeap> heap;
     uint32_t w, h;
     PixelFormat f;
-    status_t err = composer->captureScreen(0, &heap, &w, &h, &f);
+    status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);
     if (err != NO_ERROR) {
         fprintf(stderr, "screen capture failed: %s\n", strerror(-err));
         exit(0);
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 7c3508f..09b7d05 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -861,6 +861,25 @@
     }
 
     /**
+     * Enables or disables echo suppression.
+     */
+    public void setEchoSuppressionEnabled(boolean enabled) {
+        if (VDBG) {
+            Log.d(LOG_TAG, " setEchoSuppression(" + enabled + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+
+        if (hasActiveFgCall()) {
+            getActiveFgCall().getPhone().setEchoSuppressionEnabled(enabled);
+        }
+
+        if (VDBG) {
+            Log.d(LOG_TAG, "End setEchoSuppression(" + enabled + ")");
+            Log.d(LOG_TAG, this.toString());
+        }
+    }
+
+    /**
      * Play a DTMF tone on the active call.
      *
      * @param c should be one of 0-9, '*' or '#'. Other values will be
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index d753973..52839be 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -35,7 +35,7 @@
  *
  */
 public abstract class DataConnectionTracker extends Handler {
-    protected static final boolean DBG = true;
+    protected static final boolean DBG = false;
     protected final String LOG_TAG = "DataConnectionTracker";
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index ec6c0232..2957c7e 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -1187,6 +1187,11 @@
     boolean getMute();
 
     /**
+     * Enables or disables echo suppression.
+     */
+    void setEchoSuppressionEnabled(boolean enabled);
+
+    /**
      * Invokes RIL_REQUEST_OEM_HOOK_RAW on RIL implementation.
      *
      * @param data The data for the request.
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 5412768..74e8c1b 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -505,6 +505,10 @@
         mCM.unregisterForResendIncallMute(h);
     }
 
+    public void setEchoSuppressionEnabled(boolean enabled) {
+        // no need for regular phone
+    }
+
     /**
      * Subclasses of Phone probably want to replace this with a
      * version scoped to their packages
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index 9e0c087..02fdf28 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -578,6 +578,10 @@
         return mActivePhone.getMute();
     }
 
+    public void setEchoSuppressionEnabled(boolean enabled) {
+        mActivePhone.setEchoSuppressionEnabled(enabled);
+    }
+
     public void invokeOemRilRequestRaw(byte[] data, Message response) {
         mActivePhone.invokeOemRilRequestRaw(data, response);
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
old mode 100644
new mode 100755
index b71cf13..7d2013b
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -551,32 +551,53 @@
     }
 
     @Override
-    protected void powerOffRadioSafely(){
-        // clean data connection
+    protected void powerOffRadioSafely() {
         DataConnectionTracker dcTracker = phone.mDataConnection;
 
         Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION);
-        msg.arg1 = 1; // tearDown is true
         msg.obj = CDMAPhone.REASON_RADIO_TURNED_OFF;
-        dcTracker.sendMessage(msg);
 
-        synchronized(this) {
-            if (!mPendingRadioPowerOffAfterDataOff) {
-                DataConnectionTracker.State currentState = dcTracker.getState();
-                if (currentState != DataConnectionTracker.State.CONNECTED
-                        && currentState != DataConnectionTracker.State.DISCONNECTING
-                        && currentState != DataConnectionTracker.State.INITING) {
-                    if (DBG) log("Data disconnected, turn off radio right away.");
-                    hangupAndPowerOff();
-                }
-                else if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
-                    if (DBG) {
-                        log("Wait up to 30 sec for data to disconnect, then turn off radio.");
+        synchronized (this) {
+            if (networkType == ServiceState.RADIO_TECHNOLOGY_1xRTT) {
+                /*
+                 * In 1x CDMA , during radio power off modem will disconnect the
+                 * data call and sends the power down registration message along
+                 * with the data call release message to the network
+                 */
+
+                msg.arg1 = 0; // tearDown is false since modem does it anyway for 1X
+                dcTracker.sendMessage(msg);
+
+                Log.w(LOG_TAG, "Turn off the radio right away");
+                hangupAndPowerOff();
+            } else {
+                if (!mPendingRadioPowerOffAfterDataOff) {
+                    DataConnectionTracker.State currentState = dcTracker.getState();
+                    if (currentState != DataConnectionTracker.State.CONNECTED
+                            && currentState != DataConnectionTracker.State.DISCONNECTING
+                            && currentState != DataConnectionTracker.State.INITING) {
+
+                        msg.arg1 = 0; // tearDown is false as it is not needed.
+                        dcTracker.sendMessage(msg);
+
+                        if (DBG)
+                            log("Data disconnected, turn off radio right away.");
+                        hangupAndPowerOff();
+                    } else {
+                        // clean data connection
+                        msg.arg1 = 1; // tearDown is true
+                        dcTracker.sendMessage(msg);
+
+                        if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
+                            if (DBG) {
+                                log("Wait upto 30s for data to disconnect, then turn off radio.");
+                            }
+                            mPendingRadioPowerOffAfterDataOff = true;
+                        } else {
+                            Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
+                            hangupAndPowerOff();
+                        }
                     }
-                    mPendingRadioPowerOffAfterDataOff = true;
-                } else {
-                    Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
-                    hangupAndPowerOff();
                 }
             }
         }
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index d93852c..189d97d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -56,7 +56,7 @@
      * 0, with the resulting code of 0x20.
      *
      * Note this mapping is also equivalent to that used by both the
-     * IS5 and the IS-91 encodings.  For the former this is defined
+     * IA5 and the IS-91 encodings.  For the former this is defined
      * using CCITT Rec. T.50 Tables 1 and 3.  For the latter IS 637 B,
      * Table 4.3.1.4.1-1 -- and note the encoding uses only 6 bits,
      * and hence only maps entries up to the '_' character.
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index 37dd971..38700cd 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -330,6 +330,20 @@
         Log.e(LOG_TAG, "call waiting not supported");
     }
 
+    @Override
+    public void setEchoSuppressionEnabled(boolean enabled) {
+        synchronized (SipPhone.class) {
+            AudioGroup audioGroup = foregroundCall.getAudioGroup();
+            if (audioGroup == null) return;
+            int mode = audioGroup.getMode();
+            audioGroup.setMode(enabled
+                    ? AudioGroup.MODE_ECHO_SUPPRESSION
+                    : AudioGroup.MODE_NORMAL);
+            Log.d(LOG_TAG, String.format("audioGroup mode change: %d --> %d",
+                    mode, audioGroup.getMode()));
+        }
+    }
+
     public void setMute(boolean muted) {
         synchronized (SipPhone.class) {
             foregroundCall.setMute(muted);
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index af71a0f..df3c3ed 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -40,6 +40,15 @@
         </activity>
         
         <activity
+                android:name="LabelsActivity"
+                android:label="_Labels">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
                 android:name="ResizeActivity"
                 android:label="_Resize"
                 android:windowSoftInputMode="adjustResize">
@@ -164,6 +173,15 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        
+        <activity
+                android:name="GradientsActivity"
+                android:label="_Gradients">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
 
         <activity
                 android:name="ShadersActivity"
diff --git a/tests/HwAccelerationTest/res/layout/labels.xml b/tests/HwAccelerationTest/res/layout/labels.xml
new file mode 100644
index 0000000..695a2cc
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/labels.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <TextView
+        android:layout_width="200dip"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:text="This is a long text view for ellipsizing" />
+
+    <TextView
+        android:layout_width="200dip"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:gravity="center_horizontal"
+        android:text="This is a very long text view for ellipsizing" />
+
+    <TextView
+        android:layout_width="200dip"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:gravity="right"
+        android:text="This is a very long text view for ellipsizing" />
+   
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java
new file mode 100644
index 0000000..b70f3a9
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java
@@ -0,0 +1,108 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class GradientsActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(new ShadersView(this));
+    }
+
+    static class ShadersView extends View {
+        private final Paint mPaint;
+        private final float mDrawWidth;
+        private final float mDrawHeight;
+        private final LinearGradient mGradient;
+        private final Matrix mMatrix;
+
+        ShadersView(Context c) {
+            super(c);
+
+            mDrawWidth = 200;
+            mDrawHeight = 200;
+
+            mGradient = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, Shader.TileMode.CLAMP);
+            mMatrix = new Matrix();
+
+            mPaint = new Paint();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRGB(255, 255, 255);
+
+            // Gradients
+            canvas.save();
+            float top = 40.0f;
+            float right = 40.0f + mDrawWidth;
+            float left = 40.0f;
+            float bottom = 40.0f + mDrawHeight;
+
+            mPaint.setShader(mGradient);
+
+            mMatrix.setScale(1, mDrawWidth);
+            mMatrix.postRotate(90);
+            mMatrix.postTranslate(right, top);
+            mGradient.setLocalMatrix(mMatrix);
+            canvas.drawRect(right - mDrawWidth, top, right, top + mDrawHeight, mPaint);
+
+            top += 40.0f + mDrawHeight;
+            bottom += 40.0f + mDrawHeight;
+            
+            mMatrix.setScale(1, mDrawHeight);
+            mMatrix.postTranslate(left, top);
+            mGradient.setLocalMatrix(mMatrix);
+            canvas.drawRect(left, top, right, top + mDrawHeight, mPaint);
+            
+            left += 40.0f + mDrawWidth;
+            right += 40.0f + mDrawWidth;
+            top -= 40.0f + mDrawHeight;
+            bottom -= 40.0f + mDrawHeight;
+
+            mMatrix.setScale(1, mDrawHeight);
+            mMatrix.postRotate(180);
+            mMatrix.postTranslate(left, bottom);
+            mGradient.setLocalMatrix(mMatrix);
+            canvas.drawRect(left, bottom - mDrawHeight, right, bottom, mPaint);
+
+            top += 40.0f + mDrawHeight;
+            bottom += 40.0f + mDrawHeight;
+            
+            mMatrix.setScale(1, mDrawWidth);
+            mMatrix.postRotate(-90);
+            mMatrix.postTranslate(left, top);
+            mGradient.setLocalMatrix(mMatrix);
+            canvas.drawRect(left, top, left + mDrawWidth, bottom, mPaint);
+           
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LabelsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LabelsActivity.java
new file mode 100644
index 0000000..bae0500
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LabelsActivity.java
@@ -0,0 +1,29 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class LabelsActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.labels);
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java
index 9c8e7ec..2db1071 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java
@@ -77,8 +77,13 @@
             m2.setScale(0.5f, 0.5f);
             mScaledShader.setLocalMatrix(m2);
 
-            mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
+            mHorGradient = new LinearGradient(0.0f, 0.0f, 1.0f, 0.0f,
                     Color.RED, Color.GREEN, Shader.TileMode.CLAMP);
+            Matrix m3 = new Matrix();
+            m3.setScale(mDrawHeight, 1.0f);
+            m3.postRotate(-90.0f);
+            m3.postTranslate(0.0f, mDrawHeight);
+            mHorGradient.setLocalMatrix(m3);
             
             mDiagGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth / 1.5f, mDrawHeight,
                     Color.BLUE, Color.MAGENTA, Shader.TileMode.CLAMP);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
index f47b00f..763169a 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
@@ -87,6 +87,7 @@
         ListView list = (ListView) findViewById(R.id.list);
         list.setAdapter(adapter);
         list.setCacheColorHint(0);
+        list.setVerticalFadingEdgeEnabled(true);
 
         registerForContextMenu(list);
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index f91f601..0553f5e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -30,6 +30,8 @@
 import com.android.tools.layoutlib.create.MethodAdapter;
 import com.android.tools.layoutlib.create.OverrideMethod;
 
+import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -46,6 +48,7 @@
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.BridgeInflater;
+import android.view.DragEvent;
 import android.view.InputChannel;
 import android.view.IWindow;
 import android.view.IWindowSession;
@@ -1073,6 +1076,33 @@
         }
 
         @SuppressWarnings("unused")
+        public IBinder prepareDrag(IWindow window, boolean localOnly,
+                int thumbnailWidth, int thumbnailHeight, Surface outSurface)
+                throws RemoteException {
+            // pass for now
+            return null;
+        }
+
+        @SuppressWarnings("unused")
+        public boolean performDrag(IWindow window, IBinder dragToken,
+                float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+                ClipData data)
+                throws RemoteException {
+            // pass for now
+            return false;
+        }
+
+        @SuppressWarnings("unused")
+        public void dragRecipientEntered(IWindow window) throws RemoteException {
+            // pass for now
+        }
+
+        @SuppressWarnings("unused")
+        public void dragRecipientExited(IWindow window) throws RemoteException {
+            // pass for now
+        }
+
+        @SuppressWarnings("unused")
         public void setWallpaperPosition(IBinder window, float x, float y,
             float xStep, float yStep) {
             // pass for now.
@@ -1170,6 +1200,11 @@
             // pass for now.
         }
 
+        @SuppressWarnings("unused")
+        public void dispatchDragEvent(DragEvent event) {
+            // pass for now.
+        }
+
         public IBinder asBinder() {
             // pass for now.
             return null;
diff --git a/tools/obbtool/mkobb.sh b/tools/obbtool/mkobb.sh
index f4cae9a1..ba5256f 100755
--- a/tools/obbtool/mkobb.sh
+++ b/tools/obbtool/mkobb.sh
@@ -21,7 +21,7 @@
 MOUNTDIR=/tmp
 
 # Presets. Changing these will probably break your OBB on the device
-CRYPTO=blowfish
+CRYPTO=twofish
 FS=vfat
 MKFS=mkfs.vfat
 LOSETUP=losetup
@@ -122,7 +122,12 @@
         rmdir ${temp_mount}
     fi
     if [ "x${loop_dev}" != "x" ]; then \
-        ${LOSETUPBIN} -d ${loop_dev}
+        if [ ${use_crypto} -eq 1 ]; then \
+            dmsetup remove -f ${loop_dev}
+            ${LOSETUPBIN} -d ${old_loop_dev}
+        else \
+            ${LOSETUPBIN} -d ${loop_dev}
+        fi
     fi
     if [ "x${tempfile}" != "x" -a -f "${tempfile}" ]; then \
         rm -f ${tempfile}
@@ -156,11 +161,10 @@
 
 while true; do \
     case "$1" in
-        -c) use_crypto=1; shift;;
         -d) directory=$2; shift 2;;
         -h) usage; exit 1;;
-        -k) key=$2; shift 2;;
-        -K) prompt_key=1; shift;;
+        -k) key=$2; use_crypto=1; shift 2;;
+        -K) prompt_key=1; use_crypto=1; shift;;
         -v) verbose=1; shift;;
         -o) filename=$2; shift 2;;
         --) shift; break;;
@@ -202,7 +206,7 @@
 
 tempfile=$(tempfile -d ${outdir}) || ( echo "ERROR: couldn't create temporary file in ${outdir}"; exit 1 )
 
-block_count=`du --apparent-size --block-size=512 ${directory} | awk '{ print $1; }'`
+block_count=`du -s --apparent-size --block-size=512 ${directory} | awk '{ print $1; }'`
 if [ $? -ne 0 ]; then \
     echo "ERROR: Couldn't read size of input directory ${directory}"
     exit 1
@@ -216,12 +220,14 @@
 
 loop_dev=$(${LOSETUPBIN} -f) || ( echo "ERROR: losetup wouldn't tell us the next unused device"; exit 1 )
 
+${LOSETUPBIN} ${loop_dev} ${tempfile} || ( echo "ERROR: couldn't create loopback device"; exit 1 )
+
 if [ ${use_crypto} -eq 1 ]; then \
-    keyfile=$(tempfile -d ${outdir}) || ( echo "ERROR: could not create temporary key file"; exit 1 )
-    ${LOSETUPBIN} -p 5 -e ${CRYPTO} ${loop_dev} ${tempfile} 5< ${keyfile} || ( echo "ERROR: couldn't create loopback device"; exit 1 )
-    rm -f ${keyfile}
-else \
-    ${LOSETUPBIN} ${loop_dev} ${tempfile} || ( echo "ERROR: couldn't create loopback device"; exit 1 )
+    hashed_key=`echo -n "${key}" | md5sum | awk '{ print $1 }'`
+    unique_dm_name=`basename ${tempfile}`
+    echo "0 `blockdev --getsize ${loop_dev}` crypt ${CRYPTO} ${hashed_key} 0 ${loop_dev} 0" | dmsetup create ${unique_dm_name}
+    old_loop_dev=${loop_dev}
+    loop_dev=/dev/mapper/${unique_dm_name}
 fi
 
 #
@@ -252,7 +258,12 @@
 #
 umount ${temp_mount}
 rmdir ${temp_mount}
-${LOSETUPBIN} -d ${loop_dev}
+if [ ${use_crypto} -eq 1 ]; then \
+    dmsetup remove -f ${loop_dev}
+    ${LOSETUPBIN} -d ${old_loop_dev}
+else \
+    ${LOSETUPBIN} -d ${loop_dev}
+fi
 mv ${tempfile} ${filename}
 
 trap - ERR
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index aff1439..405dff8 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -66,6 +66,7 @@
  */
 public final class SipService extends ISipService.Stub {
     private static final String TAG = "SipService";
+    private static final boolean DEBUGV = false;
     private static final boolean DEBUG = true;
     private static final boolean DEBUG_TIMER = DEBUG && false;
     private static final int EXPIRY_TIME = 3600;
@@ -597,7 +598,7 @@
                 if (notCurrentSession(session)) return;
 
                 session = session.duplicate();
-                if (DEBUG) Log.d(TAG, "~~~ keepalive");
+                if (DEBUGV) Log.v(TAG, "~~~ keepalive");
                 mTimer.cancel(this);
                 session.sendKeepAlive();
                 if (session.isReRegisterRequired()) {
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index a953d38..5214518 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -776,11 +776,14 @@
     char c;
     while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
 
-    // Start your engine!
-    track.start();
+    // Start AudioRecord before AudioTrack. This prevents AudioTrack from being
+    // disabled due to buffer underrun while waiting for AudioRecord.
     if (mode != MUTED) {
         record.start();
+        int16_t one;
+        record.read(&one, sizeof(one));
     }
+    track.start();
 
     while (!exitPending()) {
         int16_t output[sampleCount];
@@ -806,34 +809,30 @@
                     track.releaseBuffer(&buffer);
                 } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
                     LOGE("cannot write to AudioTrack");
-                    break;
+                    return true;
                 }
             }
 
             if (toRead > 0) {
                 AudioRecord::Buffer buffer;
-                buffer.frameCount = record.frameCount();
+                buffer.frameCount = toRead;
 
                 status_t status = record.obtainBuffer(&buffer, 1);
                 if (status == NO_ERROR) {
-                    int count = ((int)buffer.frameCount < toRead) ?
-                            buffer.frameCount : toRead;
-                    memcpy(&input[sampleCount - toRead], buffer.i8, count * 2);
-                    toRead -= count;
-                    if (buffer.frameCount < record.frameCount()) {
-                        buffer.frameCount = count;
-                    }
+                    int offset = sampleCount - toRead;
+                    memcpy(&input[offset], buffer.i8, buffer.size);
+                    toRead -= buffer.frameCount;
                     record.releaseBuffer(&buffer);
                 } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
                     LOGE("cannot read from AudioRecord");
-                    break;
+                    return true;
                 }
             }
         }
 
         if (chances <= 0) {
-            LOGE("device loop timeout");
-            break;
+            LOGW("device loop timeout");
+            while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
         }
 
         if (mode != MUTED) {