Merge "Fix bug #4509967 (layout_gravity not always respected)"
diff --git a/Android.mk b/Android.mk
index c838600..270ee2e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -110,6 +110,7 @@
 	core/java/android/net/IConnectivityManager.aidl \
 	core/java/android/net/INetworkManagementEventObserver.aidl \
 	core/java/android/net/IThrottleManager.aidl \
+	core/java/android/net/INetworkPolicyListener.aidl \
 	core/java/android/net/INetworkPolicyManager.aidl \
 	core/java/android/nfc/ILlcpConnectionlessSocket.aidl \
 	core/java/android/nfc/ILlcpServiceSocket.aidl \
diff --git a/api/current.txt b/api/current.txt
index a621508..6550007 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -179,9 +179,9 @@
   public static final class R.attr {
     ctor public R.attr();
     field public static final int absListViewStyle = 16842858; // 0x101006a
-    field public static final int accessibilityEventTypes = 16843642; // 0x101037a
-    field public static final int accessibilityFeedbackType = 16843644; // 0x101037c
-    field public static final int accessibilityFlags = 16843646; // 0x101037e
+    field public static final int accessibilityEventTypes = 16843643; // 0x101037b
+    field public static final int accessibilityFeedbackType = 16843645; // 0x101037d
+    field public static final int accessibilityFlags = 16843647; // 0x101037f
     field public static final int accountPreferences = 16843423; // 0x101029f
     field public static final int accountType = 16843407; // 0x101028f
     field public static final int action = 16842797; // 0x101002d
@@ -201,6 +201,7 @@
     field public static final int actionModeCopyDrawable = 16843538; // 0x1010312
     field public static final int actionModeCutDrawable = 16843537; // 0x1010311
     field public static final int actionModePasteDrawable = 16843539; // 0x1010313
+    field public static final int actionModeSelectAllDrawable = 16843641; // 0x1010379
     field public static final int actionOverflowButtonStyle = 16843510; // 0x10102f6
     field public static final int actionViewClass = 16843516; // 0x10102fc
     field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
@@ -272,7 +273,7 @@
     field public static final int cacheColorHint = 16843009; // 0x1010101
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
-    field public static final int canRetrieveWindowContent = 16843647; // 0x101037f
+    field public static final int canRetrieveWindowContent = 16843648; // 0x1010380
     field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
     field public static final deprecated int capitalize = 16843113; // 0x1010169
     field public static final int centerBright = 16842956; // 0x10100cc
@@ -530,7 +531,7 @@
     field public static final int installLocation = 16843447; // 0x10102b7
     field public static final int interpolator = 16843073; // 0x1010141
     field public static final int isAlwaysSyncable = 16843571; // 0x1010333
-    field public static final int isAuxiliary = 16843641; // 0x1010379
+    field public static final int isAuxiliary = 16843642; // 0x101037a
     field public static final int isDefault = 16843297; // 0x1010221
     field public static final int isIndicator = 16843079; // 0x1010147
     field public static final int isModifier = 16843334; // 0x1010246
@@ -659,7 +660,7 @@
     field public static final int nextFocusUp = 16842979; // 0x10100e3
     field public static final int noHistory = 16843309; // 0x101022d
     field public static final int normalScreens = 16843397; // 0x1010285
-    field public static final int notificationTimeout = 16843645; // 0x101037d
+    field public static final int notificationTimeout = 16843646; // 0x101037e
     field public static final int numColumns = 16843032; // 0x1010118
     field public static final int numStars = 16843076; // 0x1010144
     field public static final deprecated int numeric = 16843109; // 0x1010165
@@ -676,7 +677,7 @@
     field public static final int overScrollFooter = 16843459; // 0x10102c3
     field public static final int overScrollHeader = 16843458; // 0x10102c2
     field public static final int overScrollMode = 16843457; // 0x10102c1
-    field public static final int packageNames = 16843643; // 0x101037b
+    field public static final int packageNames = 16843644; // 0x101037c
     field public static final int padding = 16842965; // 0x10100d5
     field public static final int paddingBottom = 16842969; // 0x10100d9
     field public static final int paddingLeft = 16842966; // 0x10100d6
@@ -6813,6 +6814,7 @@
     method public void setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory);
     method public void setDistinct(boolean);
     method public void setProjectionMap(java.util.Map<java.lang.String, java.lang.String>);
+    method public void setStrict(boolean);
     method public void setTables(java.lang.String);
   }
 
@@ -11040,6 +11042,7 @@
     method public static android.net.NetworkInfo.DetailedState valueOf(java.lang.String);
     method public static final android.net.NetworkInfo.DetailedState[] values();
     enum_constant public static final android.net.NetworkInfo.DetailedState AUTHENTICATING;
+    enum_constant public static final android.net.NetworkInfo.DetailedState BLOCKED;
     enum_constant public static final android.net.NetworkInfo.DetailedState CONNECTED;
     enum_constant public static final android.net.NetworkInfo.DetailedState CONNECTING;
     enum_constant public static final android.net.NetworkInfo.DetailedState DISCONNECTED;
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index f3f04326..e81f799 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -34,11 +34,12 @@
     IBackupManager mBackupManager;
 
     public static void main(String[] args) {
+        Log.d(TAG, "Beginning: " + args[0]);
         mArgs = args;
         try {
             new Backup().run();
         } catch (Exception e) {
-            Log.e(TAG, "Error running backup", e);
+            Log.e(TAG, "Error running backup/restore", e);
         }
         Log.d(TAG, "Finished.");
     }
@@ -46,7 +47,7 @@
     public void run() {
         mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
         if (mBackupManager == null) {
-            System.err.println("ERROR: could not contact backup manager");
+            Log.e(TAG, "Can't obtain Backup Manager binder");
             return;
         }
 
@@ -56,7 +57,7 @@
         } else if (arg.equals("restore")) {
             doFullRestore();
         } else {
-            System.err.println("ERROR: invalid operation '" + arg + "'");
+            Log.e(TAG, "Invalid operation '" + arg + "'");
         }
     }
 
@@ -80,7 +81,6 @@
                 } else if ("-all".equals(arg)) {
                     doEverything = true;
                 } else {
-                    System.err.println("WARNING: unknown backup flag " + arg);
                     Log.w(TAG, "Unknown backup flag " + arg);
                     continue;
                 }
@@ -91,13 +91,10 @@
         }
 
         if (doEverything && packages.size() > 0) {
-            System.err.println("WARNING: -all used with explicit backup package set");
             Log.w(TAG, "-all passed for backup along with specific package names");
         }
 
         if (!doEverything && !saveShared && packages.size() == 0) {
-            System.err.println(
-                    "ERROR: no packages supplied for backup and neither -shared nor -all given");
             Log.e(TAG, "no backup packages supplied and neither -shared nor -all given");
             return;
         }
@@ -108,13 +105,22 @@
             mBackupManager.fullBackup(fd, saveApks, saveShared, doEverything,
                     packages.toArray(packArray));
         } catch (IOException e) {
-            System.err.println("ERROR: cannot dup System.out");
+            Log.e(TAG, "Can't dup out");
         } catch (RemoteException e) {
-            System.err.println("ERROR: unable to invoke backup manager service");
+            Log.e(TAG, "Unable to invoke backup manager for backup");
         }
     }
 
     private void doFullRestore() {
+        // No arguments to restore
+        try {
+            ParcelFileDescriptor fd = ParcelFileDescriptor.dup(FileDescriptor.in);
+            mBackupManager.fullRestore(fd);
+        } catch (IOException e) {
+            Log.e(TAG, "Can't dup System.in");
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to invoke backup manager for restore");
+        }
     }
 
     private String nextArg() {
diff --git a/cmds/keystore/Android.mk b/cmds/keystore/Android.mk
index 15a199f..67dd9f8 100644
--- a/cmds/keystore/Android.mk
+++ b/cmds/keystore/Android.mk
@@ -19,14 +19,14 @@
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := keystore.c
+LOCAL_SRC_FILES := keystore.cpp
 LOCAL_C_INCLUDES := external/openssl/include
 LOCAL_SHARED_LIBRARIES := libcutils libcrypto
 LOCAL_MODULE:= keystore
 include $(BUILD_EXECUTABLE)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := keystore_cli.c
+LOCAL_SRC_FILES := keystore_cli.cpp
 LOCAL_C_INCLUDES := external/openssl/include
 LOCAL_SHARED_LIBRARIES := libcutils libcrypto
 LOCAL_MODULE:= keystore_cli
diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c
deleted file mode 100644
index e34053b..0000000
--- a/cmds/keystore/keystore.c
+++ /dev/null
@@ -1,590 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <arpa/inet.h>
-
-#include <openssl/aes.h>
-#include <openssl/evp.h>
-#include <openssl/md5.h>
-
-#define LOG_TAG "keystore"
-#include <cutils/log.h>
-#include <cutils/sockets.h>
-#include <private/android_filesystem_config.h>
-
-#include "keystore.h"
-
-/* KeyStore is a secured storage for key-value pairs. In this implementation,
- * each file stores one key-value pair. Keys are encoded in file names, and
- * values are encrypted with checksums. The encryption key is protected by a
- * user-defined password. To keep things simple, buffers are always larger than
- * the maximum space we needed, so boundary checks on buffers are omitted. */
-
-#define KEY_SIZE        ((NAME_MAX - 15) / 2)
-#define VALUE_SIZE      32768
-#define PASSWORD_SIZE   VALUE_SIZE
-
-/* Here is the encoding of keys. This is necessary in order to allow arbitrary
- * characters in keys. Characters in [0-~] are not encoded. Others are encoded
- * into two bytes. The first byte is one of [+-.] which represents the first
- * two bits of the character. The second byte encodes the rest of the bits into
- * [0-o]. Therefore in the worst case the length of a key gets doubled. Note
- * that Base64 cannot be used here due to the need of prefix match on keys. */
-
-static int encode_key(char *out, uint8_t *in, int length)
-{
-    int i;
-    for (i = length; i > 0; --i, ++in, ++out) {
-        if (*in >= '0' && *in <= '~') {
-            *out = *in;
-        } else {
-            *out = '+' + (*in >> 6);
-            *++out = '0' + (*in & 0x3F);
-            ++length;
-        }
-    }
-    *out = 0;
-    return length;
-}
-
-static int decode_key(uint8_t *out, char *in, int length)
-{
-    int i;
-    for (i = 0; i < length; ++i, ++in, ++out) {
-        if (*in >= '0' && *in <= '~') {
-            *out = *in;
-        } else {
-            *out = (*in - '+') << 6;
-            *out |= (*++in - '0') & 0x3F;
-            --length;
-        }
-    }
-    *out = 0;
-    return length;
-}
-
-/* Here is the protocol used in both requests and responses:
- *     code [length_1 message_1 ... length_n message_n] end-of-file
- * where code is one byte long and lengths are unsigned 16-bit integers in
- * network order. Thus the maximum length of a message is 65535 bytes. */
-
-static int the_socket = -1;
-
-static int recv_code(int8_t *code)
-{
-    return recv(the_socket, code, 1, 0) == 1;
-}
-
-static int recv_message(uint8_t *message, int length)
-{
-    uint8_t bytes[2];
-    if (recv(the_socket, &bytes[0], 1, 0) != 1 ||
-        recv(the_socket, &bytes[1], 1, 0) != 1) {
-        return -1;
-    } else {
-        int offset = bytes[0] << 8 | bytes[1];
-        if (length < offset) {
-            return -1;
-        }
-        length = offset;
-        offset = 0;
-        while (offset < length) {
-            int n = recv(the_socket, &message[offset], length - offset, 0);
-            if (n <= 0) {
-                return -1;
-            }
-            offset += n;
-        }
-    }
-    return length;
-}
-
-static int recv_end_of_file()
-{
-    uint8_t byte;
-    return recv(the_socket, &byte, 1, 0) == 0;
-}
-
-static void send_code(int8_t code)
-{
-    send(the_socket, &code, 1, 0);
-}
-
-static void send_message(uint8_t *message, int length)
-{
-    uint16_t bytes = htons(length);
-    send(the_socket, &bytes, 2, 0);
-    send(the_socket, message, length, 0);
-}
-
-/* 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)) {
-    uint8_t reserved[3];
-    uint8_t info;
-    uint8_t vector[AES_BLOCK_SIZE];
-    uint8_t encrypted[0];
-    uint8_t digest[MD5_DIGEST_LENGTH];
-    uint8_t digested[0];
-    int32_t length;
-    uint8_t value[VALUE_SIZE + AES_BLOCK_SIZE];
-} blob;
-
-static int8_t encrypt_blob(char *name, AES_KEY *aes_key)
-{
-    uint8_t vector[AES_BLOCK_SIZE];
-    int length;
-    int fd;
-
-    if (read(the_entropy, blob.vector, AES_BLOCK_SIZE) != AES_BLOCK_SIZE) {
-        return SYSTEM_ERROR;
-    }
-
-    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);
-
-    memcpy(vector, blob.vector, AES_BLOCK_SIZE);
-    AES_cbc_encrypt(blob.encrypted, blob.encrypted, length, aes_key, vector,
-                    AES_ENCRYPT);
-
-    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);
-    close(fd);
-    return (length || rename(".tmp", name)) ? SYSTEM_ERROR : NO_ERROR;
-}
-
-static int8_t decrypt_blob(char *name, AES_KEY *aes_key)
-{
-    int fd = open(name, O_RDONLY);
-    int length;
-
-    if (fd == -1) {
-        return (errno == ENOENT) ? KEY_NOT_FOUND : SYSTEM_ERROR;
-    }
-    length = read(fd, &blob, sizeof(blob));
-    close(fd);
-
-    length -= (blob.encrypted - (uint8_t *)&blob) + blob.info;
-    if (length < blob.value - blob.encrypted || length % AES_BLOCK_SIZE != 0) {
-        return VALUE_CORRUPTED;
-    }
-
-    AES_cbc_encrypt(blob.encrypted, blob.encrypted, length, aes_key,
-                    blob.vector, AES_DECRYPT);
-    length -= blob.digested - blob.encrypted;
-    if (memcmp(blob.digest, MD5(blob.digested, length, NULL),
-               MD5_DIGEST_LENGTH)) {
-        return VALUE_CORRUPTED;
-    }
-
-    length -= blob.value - blob.digested;
-    blob.length = ntohl(blob.length);
-    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
- * information is defined in global variables, which are set properly before
- * performing an action. The number of parameters required by each action is
- * fixed and defined in a table. If the return value of an action is positive,
- * it will be treated as a response code and transmitted to the client. Note
- * that the lengths of parameters are checked when they are received, so
- * boundary checks on parameters are omitted. */
-
-#define MAX_PARAM   2
-#define MAX_RETRY   4
-
-static uid_t uid = -1;
-static int8_t state = UNINITIALIZED;
-static int8_t retry = MAX_RETRY;
-
-static struct {
-    int length;
-    uint8_t value[VALUE_SIZE];
-} params[MAX_PARAM];
-
-static AES_KEY encryption_key;
-static AES_KEY decryption_key;
-
-static int8_t test()
-{
-    return state;
-}
-
-static int8_t get()
-{
-    char name[NAME_MAX];
-    int n = sprintf(name, "%u_", uid);
-    encode_key(&name[n], params[0].value, params[0].length);
-    n = decrypt_blob(name, &decryption_key);
-    if (n != NO_ERROR) {
-        return n;
-    }
-    send_code(NO_ERROR);
-    send_message(blob.value, blob.length);
-    return -NO_ERROR;
-}
-
-static int8_t insert()
-{
-    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);
-}
-
-static int8_t delete()
-{
-    char name[NAME_MAX];
-    int n = sprintf(name, "%u_", uid);
-    encode_key(&name[n], params[0].value, params[0].length);
-    return (unlink(name) && errno != ENOENT) ? SYSTEM_ERROR : NO_ERROR;
-}
-
-static int8_t exist()
-{
-    char name[NAME_MAX];
-    int n = sprintf(name, "%u_", uid);
-    encode_key(&name[n], params[0].value, params[0].length);
-    if (access(name, R_OK) == -1) {
-        return (errno != ENOENT) ? SYSTEM_ERROR : KEY_NOT_FOUND;
-    }
-    return NO_ERROR;
-}
-
-static int8_t saw()
-{
-    DIR *dir = opendir(".");
-    struct dirent *file;
-    char name[NAME_MAX];
-    int n;
-
-    if (!dir) {
-        return SYSTEM_ERROR;
-    }
-    n = sprintf(name, "%u_", uid);
-    n += encode_key(&name[n], params[0].value, params[0].length);
-    send_code(NO_ERROR);
-    while ((file = readdir(dir)) != NULL) {
-        if (!strncmp(name, file->d_name, n)) {
-            char *p = &file->d_name[n];
-            params[0].length = decode_key(params[0].value, p, strlen(p));
-            send_message(params[0].value, params[0].length);
-        }
-    }
-    closedir(dir);
-    return -NO_ERROR;
-}
-
-static int8_t reset()
-{
-    DIR *dir = opendir(".");
-    struct dirent *file;
-
-    memset(&encryption_key, 0, sizeof(encryption_key));
-    memset(&decryption_key, 0, sizeof(decryption_key));
-    state = UNINITIALIZED;
-    retry = MAX_RETRY;
-
-    if (!dir) {
-        return SYSTEM_ERROR;
-    }
-    while ((file = readdir(dir)) != NULL) {
-        unlink(file->d_name);
-    }
-    closedir(dir);
-    return NO_ERROR;
-}
-
-#define MASTER_KEY_FILE ".masterkey"
-#define MASTER_KEY_SIZE 16
-#define SALT_SIZE       16
-
-static void set_key(uint8_t *key, uint8_t *password, int length, uint8_t *salt)
-{
-    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;
-    int8_t response = SYSTEM_ERROR;
-
-    if (state == UNINITIALIZED) {
-        if (read(the_entropy, blob.value, MASTER_KEY_SIZE) != MASTER_KEY_SIZE) {
-           return SYSTEM_ERROR;
-        }
-    } else {
-        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);
-        response = decrypt_blob(MASTER_KEY_FILE, &aes_key);
-        if (response == SYSTEM_ERROR) {
-            return SYSTEM_ERROR;
-        }
-        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 {
-        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);
-        blob.info = SALT_SIZE;
-        blob.length = MASTER_KEY_SIZE;
-        response = encrypt_blob(MASTER_KEY_FILE, &aes_key);
-    }
-
-    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 response;
-}
-
-static int8_t lock()
-{
-    memset(&encryption_key, 0, sizeof(encryption_key));
-    memset(&decryption_key, 0, sizeof(decryption_key));
-    state = LOCKED;
-    return NO_ERROR;
-}
-
-static int8_t unlock()
-{
-    params[1].length = -1;
-    return password();
-}
-
-/* Here are the permissions, actions, users, and the main function. */
-
-enum perm {
-    TEST     =   1,
-    GET      =   2,
-    INSERT   =   4,
-    DELETE   =   8,
-    EXIST    =  16,
-    SAW      =  32,
-    RESET    =  64,
-    PASSWORD = 128,
-    LOCK     = 256,
-    UNLOCK   = 512,
-};
-
-static struct action {
-    int8_t (*run)();
-    int8_t code;
-    int8_t state;
-    uint32_t perm;
-    int lengths[MAX_PARAM];
-} actions[] = {
-    {test,     't', 0,        TEST,     {0}},
-    {get,      'g', NO_ERROR, GET,      {KEY_SIZE}},
-    {insert,   'i', NO_ERROR, INSERT,   {KEY_SIZE, VALUE_SIZE}},
-    {delete,   'd', 0,        DELETE,   {KEY_SIZE}},
-    {exist,    'e', 0,        EXIST,    {KEY_SIZE}},
-    {saw,      's', 0,        SAW,      {KEY_SIZE}},
-    {reset,    'r', 0,        RESET,    {0}},
-    {password, 'p', 0,        PASSWORD, {PASSWORD_SIZE, PASSWORD_SIZE}},
-    {lock,     'l', NO_ERROR, LOCK,     {0}},
-    {unlock,   'u', LOCKED,   UNLOCK,   {PASSWORD_SIZE}},
-    {NULL,      0 , 0,        0,        {0}},
-};
-
-static struct user {
-    uid_t uid;
-    uid_t euid;
-    uint32_t perms;
-} users[] = {
-    {AID_SYSTEM,   ~0,         ~GET},
-    {AID_VPN,      AID_SYSTEM, GET},
-    {AID_WIFI,     AID_SYSTEM, GET},
-    {AID_ROOT,     AID_SYSTEM, GET},
-    {AID_KEYCHAIN, AID_SYSTEM, TEST | GET | SAW},
-    {~0,           ~0,         TEST | GET | INSERT | DELETE | EXIST | SAW},
-};
-
-static int8_t process(int8_t code) {
-    struct user *user = users;
-    struct action *action = actions;
-    int i;
-
-    while (~user->uid && user->uid != uid) {
-        ++user;
-    }
-    while (action->code && action->code != code) {
-        ++action;
-    }
-    if (!action->code) {
-        return UNDEFINED_ACTION;
-    }
-    if (!(action->perm & user->perms)) {
-        return PERMISSION_DENIED;
-    }
-    if (action->state && action->state != state) {
-        return state;
-    }
-    if (~user->euid) {
-        uid = user->euid;
-    }
-    for (i = 0; i < MAX_PARAM && action->lengths[i]; ++i) {
-        params[i].length = recv_message(params[i].value, action->lengths[i]);
-        if (params[i].length == -1) {
-            return PROTOCOL_ERROR;
-        }
-    }
-    if (!recv_end_of_file()) {
-        return PROTOCOL_ERROR;
-    }
-    return action->run();
-}
-
-#define RANDOM_DEVICE   "/dev/urandom"
-
-int main(int argc, char **argv)
-{
-    int control_socket = android_get_control_socket("keystore");
-    if (argc < 2) {
-        LOGE("A directory must be specified!");
-        return 1;
-    }
-    if (chdir(argv[1]) == -1) {
-        LOGE("chdir: %s: %s", argv[1], strerror(errno));
-        return 1;
-    }
-    if ((the_entropy = open(RANDOM_DEVICE, O_RDONLY)) == -1) {
-        LOGE("open: %s: %s", RANDOM_DEVICE, strerror(errno));
-        return 1;
-    }
-    if (listen(control_socket, 3) == -1) {
-        LOGE("listen: %s", strerror(errno));
-        return 1;
-    }
-
-    signal(SIGPIPE, SIG_IGN);
-    if (access(MASTER_KEY_FILE, R_OK) == 0) {
-        state = LOCKED;
-    }
-
-    while ((the_socket = accept(control_socket, NULL, 0)) != -1) {
-        struct timeval tv = {.tv_sec = 3};
-        struct ucred cred;
-        socklen_t size = sizeof(cred);
-        int8_t request;
-
-        setsockopt(the_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
-        setsockopt(the_socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
-
-        if (getsockopt(the_socket, SOL_SOCKET, SO_PEERCRED, &cred, &size)) {
-            LOGW("getsockopt: %s", strerror(errno));
-        } else if (recv_code(&request)) {
-            int8_t old_state = state;
-            int8_t response;
-            uid = cred.uid;
-
-            if ((response = process(request)) > 0) {
-                send_code(response);
-                response = -response;
-            }
-
-            LOGI("uid: %d action: %c -> %d state: %d -> %d retry: %d",
-                 cred.uid, request, -response, old_state, state, retry);
-        }
-        close(the_socket);
-    }
-    LOGE("accept: %s", strerror(errno));
-    return 1;
-}
diff --git a/cmds/keystore/keystore.cpp b/cmds/keystore/keystore.cpp
new file mode 100644
index 0000000..31db9fd
--- /dev/null
+++ b/cmds/keystore/keystore.cpp
@@ -0,0 +1,812 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <openssl/md5.h>
+
+#define LOG_TAG "keystore"
+#include <cutils/log.h>
+#include <cutils/sockets.h>
+#include <private/android_filesystem_config.h>
+
+#include "keystore.h"
+
+/* KeyStore is a secured storage for key-value pairs. In this implementation,
+ * each file stores one key-value pair. Keys are encoded in file names, and
+ * values are encrypted with checksums. The encryption key is protected by a
+ * user-defined password. To keep things simple, buffers are always larger than
+ * the maximum space we needed, so boundary checks on buffers are omitted. */
+
+#define KEY_SIZE        ((NAME_MAX - 15) / 2)
+#define VALUE_SIZE      32768
+#define PASSWORD_SIZE   VALUE_SIZE
+
+struct Value {
+    int length;
+    uint8_t value[VALUE_SIZE];
+};
+
+/* Here is the encoding of keys. This is necessary in order to allow arbitrary
+ * characters in keys. Characters in [0-~] are not encoded. Others are encoded
+ * into two bytes. The first byte is one of [+-.] which represents the first
+ * two bits of the character. The second byte encodes the rest of the bits into
+ * [0-o]. Therefore in the worst case the length of a key gets doubled. Note
+ * that Base64 cannot be used here due to the need of prefix match on keys. */
+
+static int encode_key(char* out, uid_t uid, const Value* key) {
+    int n = snprintf(out, NAME_MAX, "%u_", uid);
+    out += n;
+    const uint8_t* in = key->value;
+    int length = key->length;
+    for (int i = length; i > 0; --i, ++in, ++out) {
+        if (*in >= '0' && *in <= '~') {
+            *out = *in;
+        } else {
+            *out = '+' + (*in >> 6);
+            *++out = '0' + (*in & 0x3F);
+            ++length;
+        }
+    }
+    *out = '\0';
+    return n + length;
+}
+
+static int decode_key(uint8_t* out, char* in, int length) {
+    for (int i = 0; i < length; ++i, ++in, ++out) {
+        if (*in >= '0' && *in <= '~') {
+            *out = *in;
+        } else {
+            *out = (*in - '+') << 6;
+            *out |= (*++in - '0') & 0x3F;
+            --length;
+        }
+    }
+    *out = '\0';
+    return length;
+}
+
+static size_t readFully(int fd, uint8_t* data, size_t size) {
+    size_t remaining = size;
+    while (remaining > 0) {
+        ssize_t n = TEMP_FAILURE_RETRY(read(fd, data, size));
+        if (n == -1 || n == 0) {
+            return size-remaining;
+        }
+        data += n;
+        remaining -= n;
+    }
+    return size;
+}
+
+static size_t writeFully(int fd, uint8_t* data, size_t size) {
+    size_t remaining = size;
+    while (remaining > 0) {
+        ssize_t n = TEMP_FAILURE_RETRY(write(fd, data, size));
+        if (n == -1 || n == 0) {
+            return size-remaining;
+        }
+        data += n;
+        remaining -= n;
+    }
+    return size;
+}
+
+class Entropy {
+public:
+    Entropy() : mRandom(-1) {}
+    ~Entropy() {
+        if (mRandom != -1) {
+            close(mRandom);
+        }
+    }
+
+    bool open() {
+        const char* randomDevice = "/dev/urandom";
+        mRandom = ::open(randomDevice, O_RDONLY);
+        if (mRandom == -1) {
+            LOGE("open: %s: %s", randomDevice, strerror(errno));
+            return false;
+        }
+        return true;
+    }
+
+    bool generate_random_data(uint8_t* data, size_t size) {
+        return (readFully(mRandom, data, size) == size);
+    }
+
+private:
+    int mRandom;
+};
+
+/* 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 encryptBlob()
+ * and decryptBlob(). Thus they should not be accessed from outside. */
+
+struct __attribute__((packed)) blob {
+    uint8_t reserved[3];
+    uint8_t info;
+    uint8_t vector[AES_BLOCK_SIZE];
+    uint8_t encrypted[0];
+    uint8_t digest[MD5_DIGEST_LENGTH];
+    uint8_t digested[0];
+    int32_t length; // in network byte order when encrypted
+    uint8_t value[VALUE_SIZE + AES_BLOCK_SIZE];
+};
+
+class Blob {
+public:
+    Blob(uint8_t* value, int32_t valueLength, uint8_t* info, uint8_t infoLength) {
+        mBlob.length = valueLength;
+        memcpy(mBlob.value, value, valueLength);
+
+        mBlob.info = infoLength;
+        memcpy(mBlob.value + valueLength, info, infoLength);
+    }
+
+    Blob(blob b) {
+        mBlob = b;
+    }
+
+    Blob() {}
+
+    uint8_t* getValue() {
+        return mBlob.value;
+    }
+
+    int32_t getLength() {
+        return mBlob.length;
+    }
+
+    uint8_t getInfo() {
+        return mBlob.info;
+    }
+
+    ResponseCode encryptBlob(const char* filename, AES_KEY *aes_key, Entropy* entropy) {
+        if (!entropy->generate_random_data(mBlob.vector, AES_BLOCK_SIZE)) {
+            return SYSTEM_ERROR;
+        }
+
+        // data includes the value and the value's length
+        size_t dataLength = mBlob.length + sizeof(mBlob.length);
+        // pad data to the AES_BLOCK_SIZE
+        size_t digestedLength = ((dataLength + AES_BLOCK_SIZE - 1)
+                                 / AES_BLOCK_SIZE * AES_BLOCK_SIZE);
+        // encrypted data includes the digest value
+        size_t encryptedLength = digestedLength + MD5_DIGEST_LENGTH;
+        // move info after space for padding
+        memmove(&mBlob.encrypted[encryptedLength], &mBlob.value[mBlob.length], mBlob.info);
+        // zero padding area
+        memset(mBlob.value + mBlob.length, 0, digestedLength - dataLength);
+
+        mBlob.length = htonl(mBlob.length);
+        MD5(mBlob.digested, digestedLength, mBlob.digest);
+
+        uint8_t vector[AES_BLOCK_SIZE];
+        memcpy(vector, mBlob.vector, AES_BLOCK_SIZE);
+        AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength,
+                        aes_key, vector, AES_ENCRYPT);
+
+        memset(mBlob.reserved, 0, sizeof(mBlob.reserved));
+        size_t headerLength = (mBlob.encrypted - (uint8_t*) &mBlob);
+        size_t fileLength = encryptedLength + headerLength + mBlob.info;
+
+        const char* tmpFileName = ".tmp";
+        int out = open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
+        if (out == -1) {
+            return SYSTEM_ERROR;
+        }
+        size_t writtenBytes = writeFully(out, (uint8_t*) &mBlob, fileLength);
+        if (close(out) != 0) {
+            return SYSTEM_ERROR;
+        }
+        if (writtenBytes != fileLength) {
+            unlink(tmpFileName);
+            return SYSTEM_ERROR;
+        }
+        return (rename(tmpFileName, filename) == 0) ? NO_ERROR : SYSTEM_ERROR;
+    }
+
+    ResponseCode decryptBlob(const char* filename, AES_KEY *aes_key) {
+        int in = open(filename, O_RDONLY);
+        if (in == -1) {
+            return (errno == ENOENT) ? KEY_NOT_FOUND : SYSTEM_ERROR;
+        }
+        // fileLength may be less than sizeof(mBlob) since the in
+        // memory version has extra padding to tolerate rounding up to
+        // the AES_BLOCK_SIZE
+        size_t fileLength = readFully(in, (uint8_t*) &mBlob, sizeof(mBlob));
+        if (close(in) != 0) {
+            return SYSTEM_ERROR;
+        }
+        size_t headerLength = (mBlob.encrypted - (uint8_t*) &mBlob);
+        if (fileLength < headerLength) {
+            return VALUE_CORRUPTED;
+        }
+
+        ssize_t encryptedLength = fileLength - (headerLength + mBlob.info);
+        if (encryptedLength < 0 || encryptedLength % AES_BLOCK_SIZE != 0) {
+            return VALUE_CORRUPTED;
+        }
+        AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, aes_key,
+                        mBlob.vector, AES_DECRYPT);
+        size_t digestedLength = encryptedLength - MD5_DIGEST_LENGTH;
+        uint8_t computedDigest[MD5_DIGEST_LENGTH];
+        MD5(mBlob.digested, digestedLength, computedDigest);
+        if (memcmp(mBlob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) {
+            return VALUE_CORRUPTED;
+        }
+
+        ssize_t maxValueLength = digestedLength - sizeof(mBlob.length);
+        mBlob.length = ntohl(mBlob.length);
+        if (mBlob.length < 0 || mBlob.length > maxValueLength) {
+            return VALUE_CORRUPTED;
+        }
+        if (mBlob.info != 0) {
+            // move info from after padding to after data
+            memmove(&mBlob.value[mBlob.length], &mBlob.value[maxValueLength], mBlob.info);
+        }
+        return NO_ERROR;
+    }
+
+private:
+    struct blob mBlob;
+};
+
+class KeyStore {
+public:
+    KeyStore(Entropy* entropy) : mEntropy(entropy), mRetry(MAX_RETRY) {
+        if (access(MASTER_KEY_FILE, R_OK) == 0) {
+            setState(STATE_LOCKED);
+        } else {
+            setState(STATE_UNINITIALIZED);
+        }
+    }
+
+    State getState() {
+        return mState;
+    }
+
+    int8_t getRetry() {
+        return mRetry;
+    }
+
+    ResponseCode initialize(Value* pw) {
+        if (!generateMasterKey()) {
+            return SYSTEM_ERROR;
+        }
+        ResponseCode response = writeMasterKey(pw);
+        if (response != NO_ERROR) {
+            return response;
+        }
+        setupMasterKeys();
+        return NO_ERROR;
+    }
+
+    ResponseCode writeMasterKey(Value* pw) {
+        uint8_t passwordKey[MASTER_KEY_SIZE_BYTES];
+        generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, mSalt);
+        AES_KEY passwordAesKey;
+        AES_set_encrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey);
+        Blob masterKeyBlob(mMasterKey, sizeof(mMasterKey), mSalt, sizeof(mSalt));
+        return masterKeyBlob.encryptBlob(MASTER_KEY_FILE, &passwordAesKey, mEntropy);
+    }
+
+    ResponseCode readMasterKey(Value* pw) {
+        int in = open(MASTER_KEY_FILE, O_RDONLY);
+        if (in == -1) {
+            return SYSTEM_ERROR;
+        }
+
+        // we read the raw blob to just to get the salt to generate
+        // the AES key, then we create the Blob to use with decryptBlob
+        blob rawBlob;
+        size_t length = readFully(in, (uint8_t*) &rawBlob, sizeof(rawBlob));
+        if (close(in) != 0) {
+            return SYSTEM_ERROR;
+        }
+        // find salt at EOF if present, otherwise we have an old file
+        uint8_t* salt;
+        if (length > SALT_SIZE && rawBlob.info == SALT_SIZE) {
+            salt = (uint8_t*) &rawBlob + length - SALT_SIZE;
+        } else {
+            salt = NULL;
+        }
+        uint8_t passwordKey[MASTER_KEY_SIZE_BYTES];
+        generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, salt);
+        AES_KEY passwordAesKey;
+        AES_set_decrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey);
+        Blob masterKeyBlob(rawBlob);
+        ResponseCode response = masterKeyBlob.decryptBlob(MASTER_KEY_FILE, &passwordAesKey);
+        if (response == SYSTEM_ERROR) {
+            return SYSTEM_ERROR;
+        }
+        if (response == NO_ERROR && masterKeyBlob.getLength() == MASTER_KEY_SIZE_BYTES) {
+            // if salt was missing, generate one and write a new master key file with the salt.
+            if (salt == NULL) {
+                if (!generateSalt()) {
+                    return SYSTEM_ERROR;
+                }
+                response = writeMasterKey(pw);
+            }
+            if (response == NO_ERROR) {
+                setupMasterKeys();
+            }
+            return response;
+        }
+        if (mRetry <= 0) {
+            reset();
+            return UNINITIALIZED;
+        }
+        --mRetry;
+        switch (mRetry) {
+            case 0: return WRONG_PASSWORD_0;
+            case 1: return WRONG_PASSWORD_1;
+            case 2: return WRONG_PASSWORD_2;
+            case 3: return WRONG_PASSWORD_3;
+            default: return WRONG_PASSWORD_3;
+        }
+    }
+
+    bool reset() {
+        clearMasterKeys();
+        setState(STATE_UNINITIALIZED);
+
+        DIR* dir = opendir(".");
+        struct dirent* file;
+
+        if (!dir) {
+            return false;
+        }
+        while ((file = readdir(dir)) != NULL) {
+            if (isKeyFile(file->d_name)) {
+                unlink(file->d_name);
+            }
+        }
+        closedir(dir);
+        return true;
+    }
+
+    bool isEmpty() {
+        DIR* dir = opendir(".");
+        struct dirent* file;
+        if (!dir) {
+            return true;
+        }
+        bool result = true;
+        while ((file = readdir(dir)) != NULL) {
+            if (isKeyFile(file->d_name)) {
+                result = false;
+                break;
+            }
+        }
+        closedir(dir);
+        return result;
+    }
+
+    void lock() {
+        clearMasterKeys();
+        setState(STATE_LOCKED);
+    }
+
+    ResponseCode get(const char* filename, Blob* keyBlob) {
+        return keyBlob->decryptBlob(filename, &mMasterKeyDecryption);
+    }
+
+    ResponseCode put(const char* filename, Blob* keyBlob) {
+        return keyBlob->encryptBlob(filename, &mMasterKeyEncryption, mEntropy);
+    }
+
+private:
+    static const char* MASTER_KEY_FILE;
+    static const int MASTER_KEY_SIZE_BYTES = 16;
+    static const int MASTER_KEY_SIZE_BITS = MASTER_KEY_SIZE_BYTES * 8;
+
+    static const int MAX_RETRY = 4;
+    static const size_t SALT_SIZE = 16;
+
+    Entropy* mEntropy;
+
+    State mState;
+    int8_t mRetry;
+
+    uint8_t mMasterKey[MASTER_KEY_SIZE_BYTES];
+    uint8_t mSalt[SALT_SIZE];
+
+    AES_KEY mMasterKeyEncryption;
+    AES_KEY mMasterKeyDecryption;
+
+    void setState(State state) {
+        mState = state;
+        if (mState == STATE_NO_ERROR || mState == STATE_UNINITIALIZED) {
+            mRetry = MAX_RETRY;
+        }
+    }
+
+    bool generateSalt() {
+        return mEntropy->generate_random_data(mSalt, sizeof(mSalt));
+    }
+
+    bool generateMasterKey() {
+        if (!mEntropy->generate_random_data(mMasterKey, sizeof(mMasterKey))) {
+            return false;
+        }
+        if (!generateSalt()) {
+            return false;
+        }
+        return true;
+    }
+
+    void setupMasterKeys() {
+        AES_set_encrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyEncryption);
+        AES_set_decrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyDecryption);
+        setState(STATE_NO_ERROR);
+    }
+
+    void clearMasterKeys() {
+        memset(mMasterKey, 0, sizeof(mMasterKey));
+        memset(mSalt, 0, sizeof(mSalt));
+        memset(&mMasterKeyEncryption, 0, sizeof(mMasterKeyEncryption));
+        memset(&mMasterKeyDecryption, 0, sizeof(mMasterKeyDecryption));
+    }
+
+    static void generateKeyFromPassword(uint8_t* key, ssize_t keySize, Value* pw, uint8_t* salt) {
+        size_t saltSize;
+        if (salt != NULL) {
+            saltSize = SALT_SIZE;
+        } else {
+            // pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found
+            salt = (uint8_t*) "keystore";
+            // sizeof = 9, not strlen = 8
+            saltSize = sizeof("keystore");
+        }
+        PKCS5_PBKDF2_HMAC_SHA1((char*) pw->value, pw->length, salt, saltSize, 8192, keySize, key);
+    }
+
+    static bool isKeyFile(const char* filename) {
+        return ((strcmp(filename, MASTER_KEY_FILE) != 0)
+                && (strcmp(filename, ".") != 0)
+                && (strcmp(filename, "..") != 0));
+    }
+};
+
+const char* KeyStore::MASTER_KEY_FILE = ".masterkey";
+
+/* Here is the protocol used in both requests and responses:
+ *     code [length_1 message_1 ... length_n message_n] end-of-file
+ * where code is one byte long and lengths are unsigned 16-bit integers in
+ * network order. Thus the maximum length of a message is 65535 bytes. */
+
+static int recv_code(int sock, int8_t* code) {
+    return recv(sock, code, 1, 0) == 1;
+}
+
+static int recv_message(int sock, uint8_t* message, int length) {
+    uint8_t bytes[2];
+    if (recv(sock, &bytes[0], 1, 0) != 1 ||
+        recv(sock, &bytes[1], 1, 0) != 1) {
+        return -1;
+    } else {
+        int offset = bytes[0] << 8 | bytes[1];
+        if (length < offset) {
+            return -1;
+        }
+        length = offset;
+        offset = 0;
+        while (offset < length) {
+            int n = recv(sock, &message[offset], length - offset, 0);
+            if (n <= 0) {
+                return -1;
+            }
+            offset += n;
+        }
+    }
+    return length;
+}
+
+static int recv_end_of_file(int sock) {
+    uint8_t byte;
+    return recv(sock, &byte, 1, 0) == 0;
+}
+
+static void send_code(int sock, int8_t code) {
+    send(sock, &code, 1, 0);
+}
+
+static void send_message(int sock, uint8_t* message, int length) {
+    uint16_t bytes = htons(length);
+    send(sock, &bytes, 2, 0);
+    send(sock, message, length, 0);
+}
+
+/* Here are the actions. Each of them is a function without arguments. All
+ * information is defined in global variables, which are set properly before
+ * performing an action. The number of parameters required by each action is
+ * fixed and defined in a table. If the return value of an action is positive,
+ * it will be treated as a response code and transmitted to the client. Note
+ * that the lengths of parameters are checked when they are received, so
+ * boundary checks on parameters are omitted. */
+
+static const ResponseCode NO_ERROR_RESPONSE_CODE_SENT = (ResponseCode) 0;
+
+static ResponseCode test(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) {
+    return (ResponseCode) keyStore->getState();
+}
+
+static ResponseCode get(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) {
+    char filename[NAME_MAX];
+    encode_key(filename, uid, keyName);
+    Blob keyBlob;
+    ResponseCode responseCode = keyStore->get(filename, &keyBlob);
+    if (responseCode != NO_ERROR) {
+        return responseCode;
+    }
+    send_code(sock, NO_ERROR);
+    send_message(sock, keyBlob.getValue(), keyBlob.getLength());
+    return NO_ERROR_RESPONSE_CODE_SENT;
+}
+
+static ResponseCode insert(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value* val) {
+    char filename[NAME_MAX];
+    encode_key(filename, uid, keyName);
+    Blob keyBlob(val->value, val->length, 0, NULL);
+    return keyStore->put(filename, &keyBlob);
+}
+
+static ResponseCode del(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) {
+    char filename[NAME_MAX];
+    encode_key(filename, uid, keyName);
+    return (unlink(filename) && errno != ENOENT) ? SYSTEM_ERROR : NO_ERROR;
+}
+
+static ResponseCode exist(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) {
+    char filename[NAME_MAX];
+    encode_key(filename, uid, keyName);
+    if (access(filename, R_OK) == -1) {
+        return (errno != ENOENT) ? SYSTEM_ERROR : KEY_NOT_FOUND;
+    }
+    return NO_ERROR;
+}
+
+static ResponseCode saw(KeyStore* keyStore, int sock, uid_t uid, Value* keyPrefix, Value*) {
+    DIR* dir = opendir(".");
+    if (!dir) {
+        return SYSTEM_ERROR;
+    }
+    char filename[NAME_MAX];
+    int n = encode_key(filename, uid, keyPrefix);
+    send_code(sock, NO_ERROR);
+
+    struct dirent* file;
+    while ((file = readdir(dir)) != NULL) {
+        if (!strncmp(filename, file->d_name, n)) {
+            char* p = &file->d_name[n];
+            keyPrefix->length = decode_key(keyPrefix->value, p, strlen(p));
+            send_message(sock, keyPrefix->value, keyPrefix->length);
+        }
+    }
+    closedir(dir);
+    return NO_ERROR_RESPONSE_CODE_SENT;
+}
+
+static ResponseCode reset(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) {
+    return keyStore->reset() ? NO_ERROR : SYSTEM_ERROR;
+}
+
+/* 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 ResponseCode password(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value*) {
+    switch (keyStore->getState()) {
+        case STATE_UNINITIALIZED: {
+            // generate master key, encrypt with password, write to file, initialize mMasterKey*.
+            return keyStore->initialize(pw);
+        }
+        case STATE_NO_ERROR: {
+            // rewrite master key with new password.
+            return keyStore->writeMasterKey(pw);
+        }
+        case STATE_LOCKED: {
+            // read master key, decrypt with password, initialize mMasterKey*.
+            return keyStore->readMasterKey(pw);
+        }
+    }
+    return SYSTEM_ERROR;
+}
+
+static ResponseCode lock(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) {
+    keyStore->lock();
+    return NO_ERROR;
+}
+
+static ResponseCode unlock(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value* unused) {
+    return password(keyStore, sock, uid, pw, unused);
+}
+
+static ResponseCode zero(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) {
+    return keyStore->isEmpty() ? KEY_NOT_FOUND : NO_ERROR;
+}
+
+/* Here are the permissions, actions, users, and the main function. */
+
+enum perm {
+    TEST     =    1,
+    GET      =    2,
+    INSERT   =    4,
+    DELETE   =    8,
+    EXIST    =   16,
+    SAW      =   32,
+    RESET    =   64,
+    PASSWORD =  128,
+    LOCK     =  256,
+    UNLOCK   =  512,
+    ZERO     = 1024,
+};
+
+static const int MAX_PARAM = 2;
+
+static const State STATE_ANY = (State) 0;
+
+static struct action {
+    ResponseCode (*run)(KeyStore* keyStore, int sock, uid_t uid, Value* param1, Value* param2);
+    int8_t code;
+    State state;
+    uint32_t perm;
+    int lengths[MAX_PARAM];
+} actions[] = {
+    {test,     't', STATE_ANY,      TEST,     {0, 0}},
+    {get,      'g', STATE_NO_ERROR, GET,      {KEY_SIZE, 0}},
+    {insert,   'i', STATE_NO_ERROR, INSERT,   {KEY_SIZE, VALUE_SIZE}},
+    {del,      'd', STATE_ANY,      DELETE,   {KEY_SIZE, 0}},
+    {exist,    'e', STATE_ANY,      EXIST,    {KEY_SIZE, 0}},
+    {saw,      's', STATE_ANY,      SAW,      {KEY_SIZE, 0}},
+    {reset,    'r', STATE_ANY,      RESET,    {0, 0}},
+    {password, 'p', STATE_ANY,      PASSWORD, {PASSWORD_SIZE, 0}},
+    {lock,     'l', STATE_NO_ERROR, LOCK,     {0, 0}},
+    {unlock,   'u', STATE_LOCKED,   UNLOCK,   {PASSWORD_SIZE, 0}},
+    {zero,     'z', STATE_ANY,      ZERO,     {0, 0}},
+    {NULL,      0 , STATE_ANY,      0,        {0, 0}},
+};
+
+static struct user {
+    uid_t uid;
+    uid_t euid;
+    uint32_t perms;
+} users[] = {
+    {AID_SYSTEM,   ~0,         ~GET},
+    {AID_VPN,      AID_SYSTEM, GET},
+    {AID_WIFI,     AID_SYSTEM, GET},
+    {AID_ROOT,     AID_SYSTEM, GET},
+    {AID_KEYCHAIN, AID_SYSTEM, TEST | GET | SAW},
+    {~0,           ~0,         TEST | GET | INSERT | DELETE | EXIST | SAW},
+};
+
+static ResponseCode process(KeyStore* keyStore, int sock, uid_t uid, int8_t code) {
+    struct user* user = users;
+    struct action* action = actions;
+    int i;
+
+    while (~user->uid && user->uid != uid) {
+        ++user;
+    }
+    while (action->code && action->code != code) {
+        ++action;
+    }
+    if (!action->code) {
+        return UNDEFINED_ACTION;
+    }
+    if (!(action->perm & user->perms)) {
+        return PERMISSION_DENIED;
+    }
+    if (action->state != STATE_ANY && action->state != keyStore->getState()) {
+        return (ResponseCode) keyStore->getState();
+    }
+    if (~user->euid) {
+        uid = user->euid;
+    }
+    Value params[MAX_PARAM];
+    for (i = 0; i < MAX_PARAM && action->lengths[i] != 0; ++i) {
+        params[i].length = recv_message(sock, params[i].value, action->lengths[i]);
+        if (params[i].length < 0) {
+            return PROTOCOL_ERROR;
+        }
+    }
+    if (!recv_end_of_file(sock)) {
+        return PROTOCOL_ERROR;
+    }
+    return action->run(keyStore, sock, uid, &params[0], &params[1]);
+}
+
+int main(int argc, char* argv[]) {
+    int controlSocket = android_get_control_socket("keystore");
+    if (argc < 2) {
+        LOGE("A directory must be specified!");
+        return 1;
+    }
+    if (chdir(argv[1]) == -1) {
+        LOGE("chdir: %s: %s", argv[1], strerror(errno));
+        return 1;
+    }
+
+    Entropy entropy;
+    if (!entropy.open()) {
+        return 1;
+    }
+    if (listen(controlSocket, 3) == -1) {
+        LOGE("listen: %s", strerror(errno));
+        return 1;
+    }
+
+    signal(SIGPIPE, SIG_IGN);
+
+    KeyStore keyStore(&entropy);
+    int sock;
+    while ((sock = accept(controlSocket, NULL, 0)) != -1) {
+        struct timeval tv;
+        tv.tv_sec = 3;
+        setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+        setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+
+        struct ucred cred;
+        socklen_t size = sizeof(cred);
+        int credResult = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cred, &size);
+        if (credResult != 0) {
+            LOGW("getsockopt: %s", strerror(errno));
+        } else {
+            int8_t request;
+            if (recv_code(sock, &request)) {
+                State old_state = keyStore.getState();
+                ResponseCode response = process(&keyStore, sock, cred.uid, request);
+                if (response == NO_ERROR_RESPONSE_CODE_SENT) {
+                    response = NO_ERROR;
+                } else {
+                    send_code(sock, response);
+                }
+                LOGI("uid: %d action: %c -> %d state: %d -> %d retry: %d",
+                     cred.uid,
+                     request, response,
+                     old_state, keyStore.getState(),
+                     keyStore.getRetry());
+            }
+        }
+        close(sock);
+    }
+    LOGE("accept: %s", strerror(errno));
+    return 1;
+}
diff --git a/cmds/keystore/keystore.h b/cmds/keystore/keystore.h
index 5ef51e9..5ae3d24 100644
--- a/cmds/keystore/keystore.h
+++ b/cmds/keystore/keystore.h
@@ -17,17 +17,27 @@
 #ifndef __KEYSTORE_H__
 #define __KEYSTORE_H__
 
-enum response_code {
-    NO_ERROR          =  1,
-    LOCKED            =  2,
-    UNINITIALIZED     =  3,
+// note state values overlap with ResponseCode for the purposes of the state() API
+enum State {
+    STATE_NO_ERROR      = 1,
+    STATE_LOCKED        = 2,
+    STATE_UNINITIALIZED = 3,
+};
+
+enum ResponseCode {
+    NO_ERROR          =  STATE_NO_ERROR, // 1
+    LOCKED            =  STATE_LOCKED, // 2
+    UNINITIALIZED     =  STATE_UNINITIALIZED, // 3
     SYSTEM_ERROR      =  4,
     PROTOCOL_ERROR    =  5,
     PERMISSION_DENIED =  6,
     KEY_NOT_FOUND     =  7,
     VALUE_CORRUPTED   =  8,
     UNDEFINED_ACTION  =  9,
-    WRONG_PASSWORD    = 10,
+    WRONG_PASSWORD_0  = 10,
+    WRONG_PASSWORD_1  = 11,
+    WRONG_PASSWORD_2  = 12,
+    WRONG_PASSWORD_3  = 13, // MAX_RETRY = 4
 };
 
 #endif
diff --git a/cmds/keystore/keystore_cli.c b/cmds/keystore/keystore_cli.cpp
similarity index 67%
rename from cmds/keystore/keystore_cli.c
rename to cmds/keystore/keystore_cli.cpp
index e8afb5a..dcd3bcb 100644
--- a/cmds/keystore/keystore_cli.c
+++ b/cmds/keystore/keystore_cli.cpp
@@ -24,44 +24,40 @@
 
 #include "keystore.h"
 
-char *responses[256] = {
-    [NO_ERROR]           = "No error",
-    [LOCKED]             = "Locked",
-    [UNINITIALIZED]      = "Uninitialized",
-    [SYSTEM_ERROR]       = "System error",
-    [PROTOCOL_ERROR]     = "Protocol error",
-    [PERMISSION_DENIED]  = "Permission denied",
-    [KEY_NOT_FOUND]      = "Key not found",
-    [VALUE_CORRUPTED]    = "Value corrupted",
-    [UNDEFINED_ACTION]   = "Undefined action",
-    [WRONG_PASSWORD]     = "Wrong password (last chance)",
-    [WRONG_PASSWORD + 1] = "Wrong password (2 tries left)",
-    [WRONG_PASSWORD + 2] = "Wrong password (3 tries left)",
-    [WRONG_PASSWORD + 3] = "Wrong password (4 tries left)",
+static const char* responses[] = {
+    NULL,
+    /* [NO_ERROR]           = */ "No error",
+    /* [LOCKED]             = */ "Locked",
+    /* [UNINITIALIZED]      = */ "Uninitialized",
+    /* [SYSTEM_ERROR]       = */ "System error",
+    /* [PROTOCOL_ERROR]     = */ "Protocol error",
+    /* [PERMISSION_DENIED]  = */ "Permission denied",
+    /* [KEY_NOT_FOUND]      = */ "Key not found",
+    /* [VALUE_CORRUPTED]    = */ "Value corrupted",
+    /* [UNDEFINED_ACTION]   = */ "Undefined action",
+    /* [WRONG_PASSWORD]     = */ "Wrong password (last chance)",
+    /* [WRONG_PASSWORD + 1] = */ "Wrong password (2 tries left)",
+    /* [WRONG_PASSWORD + 2] = */ "Wrong password (3 tries left)",
+    /* [WRONG_PASSWORD + 3] = */ "Wrong password (4 tries left)",
 };
 
-#define MAX_RESPONSE (WRONG_PASSWORD + 3)
-
-int main(int argc, char **argv)
+int main(int argc, char* argv[])
 {
-    uint8_t bytes[65536];
-    uint8_t code;
-    int sock, i;
-
     if (argc < 2) {
         printf("Usage: %s action [parameter ...]\n", argv[0]);
         return 0;
     }
 
-    sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED,
-                               SOCK_STREAM);
+    int sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                   SOCK_STREAM);
     if (sock == -1) {
         puts("Failed to connect");
         return 1;
     }
 
     send(sock, argv[1], 1, 0);
-    for (i = 2; i < argc; ++i) {
+    uint8_t bytes[65536];
+    for (int i = 2; i < argc; ++i) {
         uint16_t length = strlen(argv[i]);
         bytes[0] = length >> 8;
         bytes[1] = length;
@@ -70,11 +66,13 @@
     }
     shutdown(sock, SHUT_WR);
 
+    uint8_t code;
     if (recv(sock, &code, 1, 0) != 1) {
         puts("Failed to receive");
         return 1;
     }
     printf("%d %s\n", code , responses[code] ? responses[code] : "Unknown");
+    int i;
     while ((i = recv(sock, &bytes[0], 1, 0)) == 1) {
         int length;
         int offset;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ef2e54a..a6658cc 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -138,6 +138,25 @@
         }
     }
 
+    /** @hide */
+    public boolean getPackageAskScreenCompat(String packageName) {
+        try {
+            return ActivityManagerNative.getDefault().getPackageAskScreenCompat(packageName);
+        } catch (RemoteException e) {
+            // System dead, we will be dead too soon!
+            return false;
+        }
+    }
+
+    /** @hide */
+    public void setPackageAskScreenCompat(String packageName, boolean ask) {
+        try {
+            ActivityManagerNative.getDefault().setPackageAskScreenCompat(packageName, ask);
+        } catch (RemoteException e) {
+            // System dead, we will be dead too soon!
+        }
+    }
+
     /**
      * Return the approximate per-application memory class of the current
      * device.  This gives you an idea of how hard a memory limit you should
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 2a0d798..85f40c9 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1483,6 +1483,26 @@
             return true;
         }
 
+        case GET_PACKAGE_ASK_SCREEN_COMPAT_TRANSACTION:
+        {
+            data.enforceInterface(IActivityManager.descriptor);
+            String pkg = data.readString();
+            boolean ask = getPackageAskScreenCompat(pkg);
+            reply.writeNoException();
+            reply.writeInt(ask ? 1 : 0);
+            return true;
+        }
+
+        case SET_PACKAGE_ASK_SCREEN_COMPAT_TRANSACTION:
+        {
+            data.enforceInterface(IActivityManager.descriptor);
+            String pkg = data.readString();
+            boolean ask = data.readInt() != 0;
+            setPackageAskScreenCompat(pkg, ask);
+            reply.writeNoException();
+            return true;
+        }
+
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -3254,7 +3274,8 @@
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
-        mRemote.transact(SET_PACKAGE_SCREEN_COMPAT_MODE_TRANSACTION, data, reply, 0);
+        data.writeString(packageName);
+        mRemote.transact(GET_PACKAGE_SCREEN_COMPAT_MODE_TRANSACTION, data, reply, 0);
         reply.readException();
         int mode = reply.readInt();
         reply.recycle();
@@ -3275,6 +3296,32 @@
         data.recycle();
     }
 
+    public boolean getPackageAskScreenCompat(String packageName) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(packageName);
+        mRemote.transact(GET_PACKAGE_ASK_SCREEN_COMPAT_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean ask = reply.readInt() != 0;
+        reply.recycle();
+        data.recycle();
+        return ask;
+    }
+
+    public void setPackageAskScreenCompat(String packageName, boolean ask)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(packageName);
+        data.writeInt(ask ? 1 : 0);
+        mRemote.transact(SET_PACKAGE_ASK_SCREEN_COMPAT_TRANSACTION, data, reply, 0);
+        reply.readException();
+        reply.recycle();
+        data.recycle();
+    }
+
     public boolean switchUser(int userid) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 85e59b3..955cef2 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1980,7 +1980,8 @@
         BackupAgent agent = null;
         String classname = data.appInfo.backupAgentName;
 
-        if (data.backupMode == IApplicationThread.BACKUP_MODE_FULL) {
+        if (data.backupMode == IApplicationThread.BACKUP_MODE_FULL
+                || data.backupMode == IApplicationThread.BACKUP_MODE_RESTORE_FULL) {
             classname = "android.app.backup.FullBackupAgent";
             if ((data.appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                 // system packages can supply their own full-backup agent
@@ -2011,7 +2012,8 @@
                 // If this is during restore, fail silently; otherwise go
                 // ahead and let the user see the crash.
                 Slog.e(TAG, "Agent threw during creation: " + e);
-                if (data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE) {
+                if (data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE
+                        && data.backupMode != IApplicationThread.BACKUP_MODE_RESTORE_FULL) {
                     throw e;
                 }
                 // falling through with 'binder' still null
@@ -3658,12 +3660,16 @@
         Application app = data.info.makeApplication(data.restrictedBackupMode, null);
         mInitialApplication = app;
 
-        List<ProviderInfo> providers = data.providers;
-        if (providers != null) {
-            installContentProviders(app, providers);
-            // For process that contains content providers, we want to
-            // ensure that the JIT is enabled "at some point".
-            mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
+        // don't bring up providers in restricted mode; they may depend on the
+        // app's custom Application class
+        if (!data.restrictedBackupMode){ 
+            List<ProviderInfo> providers = data.providers;
+            if (providers != null) {
+                installContentProviders(app, providers);
+                // For process that contains content providers, we want to
+                // ensure that the JIT is enabled "at some point".
+                mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
+            }
         }
 
         try {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 1f53c0e..e2588cf 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -347,6 +347,9 @@
     public int getPackageScreenCompatMode(String packageName) throws RemoteException;
     public void setPackageScreenCompatMode(String packageName, int mode)
             throws RemoteException;
+    public boolean getPackageAskScreenCompat(String packageName) throws RemoteException;
+    public void setPackageAskScreenCompat(String packageName, boolean ask)
+            throws RemoteException;
     
     // Multi-user APIs
     public boolean switchUser(int userid) throws RemoteException;
@@ -577,9 +580,11 @@
     int SET_FRONT_ACTIVITY_SCREEN_COMPAT_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+124;
     int GET_PACKAGE_SCREEN_COMPAT_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+125;
     int SET_PACKAGE_SCREEN_COMPAT_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+126;
-    int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+127;
-    int REMOVE_SUB_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+128;
-    int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+129;
-    int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+130;
-    int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131;
+    int GET_PACKAGE_ASK_SCREEN_COMPAT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+127;
+    int SET_PACKAGE_ASK_SCREEN_COMPAT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+128;
+    int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+129;
+    int REMOVE_SUB_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+130;
+    int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131;
+    int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+132;
+    int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+133;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 8c31559..05a68a8 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -68,6 +68,7 @@
     static final int BACKUP_MODE_INCREMENTAL = 0;
     static final int BACKUP_MODE_FULL = 1;
     static final int BACKUP_MODE_RESTORE = 2;
+    static final int BACKUP_MODE_RESTORE_FULL = 3;
     void scheduleCreateBackupAgent(ApplicationInfo app, CompatibilityInfo compatInfo,
             int backupMode) throws RemoteException;
     void scheduleDestroyBackupAgent(ApplicationInfo app, CompatibilityInfo compatInfo)
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 52fc623..8af78fa 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -79,4 +79,23 @@
      */
     void doRestore(in ParcelFileDescriptor data, int appVersionCode,
             in ParcelFileDescriptor newState, int token, IBackupManager callbackBinder);
+
+    /**
+     * Restore a single "file" to the application.  The file was typically obtained from
+     * a full-backup dataset.  The agent reads 'size' bytes of file content
+     * from the provided file descriptor.
+     *
+     * @param data Read-only pipe delivering the file content itself.
+     *
+     * @param size Size of the file being restored.
+     * @param type Type of file system entity, e.g. FullBackup.TYPE_DIRECTORY.
+     * @param domain Name of the file's semantic domain to which the 'path' argument is a
+     *        relative path.  e.g. FullBackup.DATABASE_TREE_TOKEN.
+     * @param path Relative path of the file within its semantic domain.
+     * @param mode Access mode of the file system entity, e.g. 0660.
+     * @param mtime Last modification time of the file system entity.
+     */
+    void doRestoreFile(in ParcelFileDescriptor data, long size,
+            int type, String domain, String path, long mode, long mtime,
+            int token, IBackupManager callbackBinder);
 }
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index dc60e24..17f8adb 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -179,10 +179,18 @@
             throws IOException;
 
     /**
+     * @hide
+     */
+    public void onRestoreFile(ParcelFileDescriptor data, long size,
+            int type, String domain, String path, long mode, long mtime)
+            throws IOException {
+        // empty stub implementation
+    }
+
+    /**
      * Package-private, used only for dispatching an extra step during full backup
      */
     void onSaveApk(BackupDataOutput data) {
-        if (DEBUG) Log.v(TAG, "--- base onSaveApk() ---");
     }
 
     // ----- Core implementation -----
@@ -203,6 +211,7 @@
     private class BackupServiceBinder extends IBackupAgent.Stub {
         private static final String TAG = "BackupServiceBinder";
 
+        @Override
         public void doBackup(ParcelFileDescriptor oldState,
                 ParcelFileDescriptor data,
                 ParcelFileDescriptor newState,
@@ -236,6 +245,7 @@
             }
         }
 
+        @Override
         public void doRestore(ParcelFileDescriptor data, int appVersionCode,
                 ParcelFileDescriptor newState,
                 int token, IBackupManager callbackBinder) throws RemoteException {
@@ -261,5 +271,25 @@
                 }
             }
         }
+
+        @Override
+        public void doRestoreFile(ParcelFileDescriptor data, long size,
+                int type, String domain, String path, long mode, long mtime,
+                int token, IBackupManager callbackBinder) throws RemoteException {
+            long ident = Binder.clearCallingIdentity();
+            try {
+Log.d(TAG, "doRestoreFile() => onRestoreFile()");
+                BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+                try {
+                    callbackBinder.opComplete(token);
+                } catch (RemoteException e) {
+                    // we'll time out anyway, so we're safe
+                }
+            }
+        }
     }
 }
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 9850566..dfb0dd7 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -16,6 +16,17 @@
 
 package android.app.backup;
 
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+
 /**
  * Global constant definitions et cetera related to the full-backup-to-fd
  * binary format.
@@ -23,18 +34,95 @@
  * @hide
  */
 public class FullBackup {
-    public static String APK_TREE_TOKEN = "a";
-    public static String OBB_TREE_TOKEN = "obb";
-    public static String ROOT_TREE_TOKEN = "r";
-    public static String DATA_TREE_TOKEN = "f";
-    public static String DATABASE_TREE_TOKEN = "db";
-    public static String SHAREDPREFS_TREE_TOKEN = "sp";
-    public static String CACHE_TREE_TOKEN = "c";
+    static final String TAG = "FullBackup";
 
-    public static String FULL_BACKUP_INTENT_ACTION = "fullback";
-    public static String FULL_RESTORE_INTENT_ACTION = "fullrest";
-    public static String CONF_TOKEN_INTENT_EXTRA = "conftoken";
+    public static final String APK_TREE_TOKEN = "a";
+    public static final String OBB_TREE_TOKEN = "obb";
+    public static final String ROOT_TREE_TOKEN = "r";
+    public static final String DATA_TREE_TOKEN = "f";
+    public static final String DATABASE_TREE_TOKEN = "db";
+    public static final String SHAREDPREFS_TREE_TOKEN = "sp";
+    public static final String CACHE_TREE_TOKEN = "c";
+    public static final String SHARED_STORAGE_TOKEN = "shared";
+
+    public static final String APPS_PREFIX = "apps/";
+    public static final String SHARED_PREFIX = "shared/";
+
+    public static final String FULL_BACKUP_INTENT_ACTION = "fullback";
+    public static final String FULL_RESTORE_INTENT_ACTION = "fullrest";
+    public static final String CONF_TOKEN_INTENT_EXTRA = "conftoken";
+
+    public static final int TYPE_EOF = 0;
+    public static final int TYPE_FILE = 1;
+    public static final int TYPE_DIRECTORY = 2;
+    public static final int TYPE_SYMLINK = 3;
 
     static public native int backupToTar(String packageName, String domain,
             String linkdomain, String rootpath, String path, BackupDataOutput output);
+
+    static public void restoreToFile(ParcelFileDescriptor data,
+            long size, int type, long mode, long mtime, File outFile) throws IOException {
+        if (type == FullBackup.TYPE_DIRECTORY) {
+            // Canonically a directory has no associated content, so we don't need to read
+            // anything from the pipe in this case.  Just create the directory here and
+            // drop down to the final metadata adjustment.
+            if (outFile != null) outFile.mkdirs();
+        } else {
+            FileOutputStream out = null;
+
+            // Pull the data from the pipe, copying it to the output file, until we're done
+            try {
+                if (outFile != null) {
+                    File parent = outFile.getParentFile();
+                    if (!parent.exists()) {
+                        // in practice this will only be for the default semantic directories,
+                        // and using the default mode for those is appropriate.
+                        // TODO: support the edge case of apps that have adjusted the
+                        //       permissions on these core directories
+                        parent.mkdirs();
+                    }
+                    out = new FileOutputStream(outFile);
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Unable to create/open file " + outFile.getPath(), e);
+            }
+
+            byte[] buffer = new byte[32 * 1024];
+            final long origSize = size;
+            FileInputStream in = new FileInputStream(data.getFileDescriptor());
+            while (size > 0) {
+                int toRead = (size > buffer.length) ? buffer.length : (int)size;
+                int got = in.read(buffer, 0, toRead);
+                if (got <= 0) {
+                    Log.w(TAG, "Incomplete read: expected " + size + " but got "
+                            + (origSize - size));
+                    break;
+                }
+                if (out != null) {
+                    try {
+                        out.write(buffer, 0, got);
+                    } catch (IOException e) {
+                        // Problem writing to the file.  Quit copying data and delete
+                        // the file, but of course keep consuming the input stream.
+                        Log.e(TAG, "Unable to write to file " + outFile.getPath(), e);
+                        out.close();
+                        out = null;
+                        outFile.delete();
+                    }
+                }
+                size -= got;
+            }
+            if (out != null) out.close();
+        }
+
+        // Now twiddle the state to match the backup, assuming all went well
+        if (outFile != null) {
+            try {
+                Libcore.os.chmod(outFile.getPath(), (int)mode);
+            } catch (ErrnoException e) {
+                e.rethrowAsIOException();
+            }
+            outFile.setLastModified(mtime);
+        }
+    }
 }
diff --git a/core/java/android/app/backup/FullBackupAgent.java b/core/java/android/app/backup/FullBackupAgent.java
index f0a1f2a..4dca593 100644
--- a/core/java/android/app/backup/FullBackupAgent.java
+++ b/core/java/android/app/backup/FullBackupAgent.java
@@ -28,6 +28,9 @@
 import libcore.io.StructStat;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.util.HashSet;
 import java.util.LinkedList;
 
@@ -53,8 +56,12 @@
     private String mCacheDir;
     private String mLibDir;
 
+    private File NULL_FILE;
+
     @Override
     public void onCreate() {
+        NULL_FILE = new File("/dev/null");
+
         mPm = getPackageManager();
         try {
             ApplicationInfo appInfo = mPm.getApplicationInfo(getPackageName(), 0);
@@ -177,7 +184,40 @@
         }
     }
 
+    /**
+     * Dummy -- We're never used for restore of an incremental dataset
+     */
     @Override
-    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) {
+    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+            throws IOException {
+    }
+
+    /**
+     * Restore the described file from the given pipe.
+     */
+    @Override
+    public void onRestoreFile(ParcelFileDescriptor data, long size,
+            int type, String domain, String relpath, long mode, long mtime) 
+            throws IOException {
+        String basePath = null;
+        File outFile = null;
+
+        if (DEBUG) Log.d(TAG, "onRestoreFile() size=" + size + " type=" + type
+                + " domain=" + domain + " relpath=" + relpath + " mode=" + mode
+                + " mtime=" + mtime);
+
+        // Parse out the semantic domains into the correct physical location
+        if (domain.equals(FullBackup.DATA_TREE_TOKEN)) basePath = mFilesDir;
+        else if (domain.equals(FullBackup.DATABASE_TREE_TOKEN)) basePath = mDatabaseDir;
+        else if (domain.equals(FullBackup.ROOT_TREE_TOKEN)) basePath = mMainDir;
+        else if (domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)) basePath = mSharedPrefsDir;
+
+        // Not a supported output location?  We need to consume the data
+        // anyway, so send it to /dev/null
+        outFile = (basePath != null) ? new File(basePath, relpath) : null;
+        if (DEBUG) Log.i(TAG, "[" + domain + " : " + relpath + "] mapped to " + outFile.getPath());
+
+        // Now that we've figured out where the data goes, send it on its way
+        FullBackup.restoreToFile(data, size, type, mode, mtime, outFile);
     }
 }
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 94e31a8..bac874e 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -147,6 +147,14 @@
             boolean allApps, in String[] packageNames);
 
     /**
+     * Restore device content from the data stream passed through the given socket.  The
+     * data stream must be in the format emitted by fullBackup().
+     *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     */
+    void fullRestore(in ParcelFileDescriptor fd);
+
+    /**
      * Confirm that the requested full backup/restore operation can proceed.  The system will
      * not actually perform the operation described to fullBackup() / fullRestore() unless the
      * UI calls back into the Backup Manager to confirm, passing the correct token.  At
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 854d410..dca53a8 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -113,8 +113,13 @@
     public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, boolean forceCompat) {
         int compatFlags = 0;
 
+        // We can't rely on the application always setting
+        // FLAG_RESIZEABLE_FOR_SCREENS so will compute it based on various input.
+        boolean anyResizeable = false;
+
         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
             compatFlags |= LARGE_SCREENS;
+            anyResizeable = true;
             if (!forceCompat) {
                 // If we aren't forcing the app into compatibility mode, then
                 // assume if it supports large screens that we should allow it
@@ -123,9 +128,13 @@
             }
         }
         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
-            compatFlags |= XLARGE_SCREENS | EXPANDABLE;
+            anyResizeable = true;
+            if (!forceCompat) {
+                compatFlags |= XLARGE_SCREENS | EXPANDABLE;
+            }
         }
         if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
+            anyResizeable = true;
             compatFlags |= EXPANDABLE;
         }
 
@@ -160,7 +169,7 @@
         if ((screenLayout&Configuration.SCREENLAYOUT_COMPAT_NEEDED) != 0) {
             if ((compatFlags&EXPANDABLE) != 0) {
                 supportsScreen = true;
-            } else if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) == 0) {
+            } else if (!anyResizeable) {
                 compatFlags |= ALWAYS_COMPAT;
             }
         }
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index b6aca2b..9c09e81 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -24,8 +24,8 @@
 
 import java.util.Iterator;
 import java.util.Map;
-import java.util.Set;
 import java.util.Map.Entry;
+import java.util.Set;
 import java.util.regex.Pattern;
 
 /**
@@ -43,7 +43,7 @@
     private StringBuilder mWhereClause = null;  // lazily created
     private boolean mDistinct;
     private SQLiteDatabase.CursorFactory mFactory;
-    private boolean mStrictProjectionMap;
+    private boolean mStrict;
 
     public SQLiteQueryBuilder() {
         mDistinct = false;
@@ -145,10 +145,37 @@
     }
 
     /**
+     * Need to keep this to not break the build until ContactsProvider2 has been changed to
+     * use the new API
+     * TODO: Remove this
      * @hide
      */
     public void setStrictProjectionMap(boolean flag) {
-        mStrictProjectionMap = flag;
+    }
+
+    /**
+     * When set, the selection is verified against malicious arguments.
+     * When using this class to create a statement using
+     * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)},
+     * non-numeric limits will raise an exception. If a projection map is specified, fields
+     * not in that map will be ignored.
+     * If this class is used to execute the statement directly using
+     * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String)}
+     * or
+     * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String, String)},
+     * additionally also parenthesis escaping selection are caught.
+     *
+     * To summarize: To get maximum protection against malicious third party apps (for example
+     * content provider consumers), make sure to do the following:
+     * <ul>
+     * <li>Set this value to true</li>
+     * <li>Use a projection map</li>
+     * <li>Use one of the query overloads instead of getting the statement as a sql string</li>
+     * </ul>
+     * By default, this value is false.
+     */
+    public void setStrict(boolean flag) {
+        mStrict = flag;
     }
 
     /**
@@ -217,13 +244,6 @@
         }
     }
 
-    private static void appendClauseEscapeClause(StringBuilder s, String name, String clause) {
-        if (!TextUtils.isEmpty(clause)) {
-            s.append(name);
-            DatabaseUtils.appendEscapedSQLString(s, clause);
-        }
-    }
-
     /**
      * Add the names that are non-null in columns to s, separating
      * them with commas.
@@ -320,6 +340,19 @@
             return null;
         }
 
+        if (mStrict && selection != null && selection.length() > 0) {
+            // Validate the user-supplied selection to detect syntactic anomalies
+            // in the selection string that could indicate a SQL injection attempt.
+            // The idea is to ensure that the selection clause is a valid SQL expression
+            // by compiling it twice: once wrapped in parentheses and once as
+            // originally specified. An attacker cannot create an expression that
+            // would escape the SQL expression while maintaining balanced parentheses
+            // in both the wrapped and original forms.
+            String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy,
+                    having, sortOrder, limit);
+            validateSql(db, sqlForValidation); // will throw if query is invalid
+        }
+
         String sql = buildQuery(
                 projectionIn, selection, groupBy, having,
                 sortOrder, limit);
@@ -329,7 +362,20 @@
         }
         return db.rawQueryWithFactory(
                 mFactory, sql, selectionArgs,
-                SQLiteDatabase.findEditTable(mTables));
+                SQLiteDatabase.findEditTable(mTables)); // will throw if query is invalid
+    }
+
+    /**
+     * Verifies that a SQL statement is valid by compiling it.
+     * If the SQL statement is not valid, this method will throw a {@link SQLiteException}.
+     */
+    private void validateSql(SQLiteDatabase db, String sql) {
+        db.lock(sql);
+        try {
+            new SQLiteCompiledSql(db, sql).releaseSqlStatement();
+        } finally {
+            db.unlock();
+        }
     }
 
     /**
@@ -541,7 +587,7 @@
                         continue;
                     }
 
-                    if (!mStrictProjectionMap &&
+                    if (!mStrict &&
                             ( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
                         /* A column alias already exist */
                         projection[i] = userColumn;
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 419288b..c72c4b0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -22,7 +22,6 @@
 import android.os.RemoteException;
 
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 
 /**
  * Class that answers queries about the state of network connectivity. It also
@@ -40,8 +39,9 @@
  * state of the available networks</li>
  * </ol>
  */
-public class ConnectivityManager
-{
+public class ConnectivityManager {
+    private static final String TAG = "ConnectivityManager";
+
     /**
      * A change in network connectivity has occurred. A connection has either
      * been established or lost. The NetworkInfo for the affected network is
@@ -109,7 +109,7 @@
      * The lookup key for an int that provides information about
      * our connection to the internet at large.  0 indicates no connection,
      * 100 indicates a great connection.  Retrieve it with
-     * {@link android.content.Intent@getIntExtra(String)}.
+     * {@link android.content.Intent#getIntExtra(String, int)}.
      * {@hide}
      */
     public static final String EXTRA_INET_CONDITION = "inetCondition";
@@ -120,13 +120,12 @@
      * <p>
      * If an application uses the network in the background, it should listen
      * for this broadcast and stop using the background data if the value is
-     * false.
+     * {@code false}.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED =
             "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
 
-
     /**
      * Broadcast Action: The network connection may not be good
      * uses {@code ConnectivityManager.EXTRA_INET_CONDITION} and
@@ -255,7 +254,7 @@
 
     public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
 
-    private IConnectivityManager mService;
+    private final IConnectivityManager mService;
 
     static public boolean isNetworkTypeValid(int networkType) {
         return networkType >= 0 && networkType <= MAX_NETWORK_TYPE;
@@ -284,6 +283,15 @@
         }
     }
 
+    /** {@hide} */
+    public NetworkInfo getActiveNetworkInfoForUid(int uid) {
+        try {
+            return mService.getActiveNetworkInfoForUid(uid);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
     public NetworkInfo getNetworkInfo(int networkType) {
         try {
             return mService.getNetworkInfo(networkType);
@@ -300,7 +308,7 @@
         }
     }
 
-    /** @hide */
+    /** {@hide} */
     public LinkProperties getActiveLinkProperties() {
         try {
             return mService.getActiveLinkProperties();
@@ -309,7 +317,7 @@
         }
     }
 
-    /** @hide */
+    /** {@hide} */
     public LinkProperties getLinkProperties(int networkType) {
         try {
             return mService.getLinkProperties(networkType);
@@ -479,19 +487,11 @@
     }
 
     /**
-     * Don't allow use of default constructor.
-     */
-    @SuppressWarnings({"UnusedDeclaration"})
-    private ConnectivityManager() {
-    }
-
-    /**
      * {@hide}
      */
     public ConnectivityManager(IConnectivityManager service) {
         if (service == null) {
-            throw new IllegalArgumentException(
-                "ConnectivityManager() cannot be constructed with null service");
+            throw new IllegalArgumentException("missing IConnectivityManager");
         }
         mService = service;
     }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 8be492c..647a60a 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -33,13 +33,11 @@
     int getNetworkPreference();
 
     NetworkInfo getActiveNetworkInfo();
-
+    NetworkInfo getActiveNetworkInfoForUid(int uid);
     NetworkInfo getNetworkInfo(int networkType);
-
     NetworkInfo[] getAllNetworkInfo();
 
     LinkProperties getActiveLinkProperties();
-
     LinkProperties getLinkProperties(int networkType);
 
     boolean setRadios(boolean onOff);
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
new file mode 100644
index 0000000..9230151
--- /dev/null
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2011 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.net;
+
+/** {@hide} */
+oneway interface INetworkPolicyListener {
+
+    void onRulesChanged(int uid, int uidRules);
+
+}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index d9351ee..c1f3530 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import android.net.INetworkPolicyListener;
+
 /**
  * Interface that creates and modifies network policy rules.
  *
@@ -26,6 +28,11 @@
     void setUidPolicy(int uid, int policy);
     int getUidPolicy(int uid);
 
+    boolean isUidForeground(int uid);
+
+    void registerListener(INetworkPolicyListener listener);
+    void unregisterListener(INetworkPolicyListener listener);
+
     // TODO: build API to surface stats details for settings UI
 
 }
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 5f5e11c..537750a 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -74,7 +74,9 @@
         /** IP traffic not available. */
         DISCONNECTED,
         /** Attempt to connect failed. */
-        FAILED
+        FAILED,
+        /** Access to this network is blocked. */
+        BLOCKED
     }
 
     /**
@@ -96,6 +98,7 @@
         stateMap.put(DetailedState.DISCONNECTING, State.DISCONNECTING);
         stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED);
         stateMap.put(DetailedState.FAILED, State.DISCONNECTED);
+        stateMap.put(DetailedState.BLOCKED, State.DISCONNECTED);
     }
 
     private int mNetworkType;
@@ -138,6 +141,23 @@
         mIsRoaming = false;
     }
 
+    /** {@hide} */
+    public NetworkInfo(NetworkInfo source) {
+        if (source != null) {
+            mNetworkType = source.mNetworkType;
+            mSubtype = source.mSubtype;
+            mTypeName = source.mTypeName;
+            mSubtypeName = source.mSubtypeName;
+            mState = source.mState;
+            mDetailedState = source.mDetailedState;
+            mReason = source.mReason;
+            mExtraInfo = source.mExtraInfo;
+            mIsFailover = source.mIsFailover;
+            mIsRoaming = source.mIsRoaming;
+            mIsAvailable = source.mIsAvailable;
+        }
+    }
+
     /**
      * Reports the type of network (currently mobile or Wi-Fi) to which the
      * info in this object pertains.
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 1913aa7..dd7c1b0 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -19,6 +19,8 @@
 import android.content.Context;
 import android.os.RemoteException;
 
+import java.io.PrintWriter;
+
 /**
  * Manager for creating and modifying network policy rules.
  *
@@ -28,12 +30,13 @@
 
     /** No specific network policy, use system default. */
     public static final int POLICY_NONE = 0x0;
-    /** Reject network usage when application in background. */
-    public static final int POLICY_REJECT_BACKGROUND = 0x1;
-    /** Reject network usage on paid network connections. */
-    public static final int POLICY_REJECT_PAID = 0x2;
-    /** Application should conserve data. */
-    public static final int POLICY_CONSERVE_DATA = 0x4;
+    /** Reject network usage on paid networks when application in background. */
+    public static final int POLICY_REJECT_PAID_BACKGROUND = 0x1;
+
+    /** All network traffic should be allowed. */
+    public static final int RULE_ALLOW_ALL = 0x0;
+    /** Reject traffic on paid networks. */
+    public static final int RULE_REJECT_PAID = 0x1;
 
     private INetworkPolicyManager mService;
 
@@ -51,9 +54,8 @@
     /**
      * Set policy flags for specific UID.
      *
-     * @param policy {@link #POLICY_NONE} or combination of
-     *            {@link #POLICY_REJECT_BACKGROUND}, {@link #POLICY_REJECT_PAID},
-     *            or {@link #POLICY_CONSERVE_DATA}.
+     * @param policy {@link #POLICY_NONE} or combination of flags like
+     *            {@link #POLICY_REJECT_PAID_BACKGROUND}.
      */
     public void setUidPolicy(int uid, int policy) {
         try {
@@ -69,5 +71,23 @@
             return POLICY_NONE;
         }
     }
+    
+    /** {@hide} */
+    public static void dumpPolicy(PrintWriter fout, int policy) {
+        fout.write("[");
+        if ((policy & POLICY_REJECT_PAID_BACKGROUND) != 0) {
+            fout.write("REJECT_PAID_BACKGROUND");
+        }
+        fout.write("]");
+    }
+
+    /** {@hide} */
+    public static void dumpRules(PrintWriter fout, int rules) {
+        fout.write("[");
+        if ((rules & RULE_REJECT_PAID) != 0) {
+            fout.write("REJECT_PAID");
+        }
+        fout.write("]");
+    }
 
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 017e5e3..c9db697 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -982,10 +982,21 @@
      */
     static final int HORIZONTAL_DIRECTION_MASK = 0xC0000000;
 
+    /*
+     * Array of horizontal direction flags for mapping attribute "horizontalDirection" to correct
+     * flag value.
+     * {@hide}
+     */
     private static final int[] HORIZONTAL_DIRECTION_FLAGS = { HORIZONTAL_DIRECTION_LTR,
             HORIZONTAL_DIRECTION_RTL, HORIZONTAL_DIRECTION_INHERIT, HORIZONTAL_DIRECTION_LOCALE};
 
     /**
+     * Default horizontalDirection.
+     * {@hide}
+     */
+    private static final int HORIZONTAL_DIRECTION_DEFAULT = HORIZONTAL_DIRECTION_INHERIT;
+
+    /**
      * View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
      * should add all focusable Views regardless if they are focusable in touch mode.
      */
@@ -2442,7 +2453,7 @@
     public View(Context context) {
         mContext = context;
         mResources = context != null ? context.getResources() : null;
-        mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
+        mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | HORIZONTAL_DIRECTION_INHERIT;
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
     }
@@ -2641,12 +2652,18 @@
                     }
                     break;
                 case com.android.internal.R.styleable.View_horizontalDirection:
-                  final int layoutDirection = a.getInt(attr, 0);
-                  if (layoutDirection != 0) {
-                      viewFlagValues |= HORIZONTAL_DIRECTION_FLAGS[layoutDirection];
-                      viewFlagMasks |= HORIZONTAL_DIRECTION_MASK;
-                  }
-                  break;
+                    // Clear any HORIZONTAL_DIRECTION flag already set
+                    viewFlagValues &= ~HORIZONTAL_DIRECTION_MASK;
+                    // Set the HORIZONTAL_DIRECTION flags depending on the value of the attribute
+                    final int horizontalDirection = a.getInt(attr, -1);
+                    if (horizontalDirection != -1) {
+                        viewFlagValues |= HORIZONTAL_DIRECTION_FLAGS[horizontalDirection];
+                    } else {
+                        // Set to default (HORIZONTAL_DIRECTION_INHERIT)
+                        viewFlagValues |= HORIZONTAL_DIRECTION_DEFAULT;
+                    }
+                    viewFlagMasks |= HORIZONTAL_DIRECTION_MASK;
+                    break;
                 case com.android.internal.R.styleable.View_drawingCacheQuality:
                     final int cacheQuality = a.getInt(attr, 0);
                     if (cacheQuality != 0) {
@@ -8513,10 +8530,14 @@
             mPrivateFlags &= ~AWAKEN_SCROLL_BARS_ON_ATTACH;
         }
         jumpDrawablesToCurrentState();
+        resolveHorizontalDirection();
+    }
 
-        // We are supposing here that the parent directionality will be resolved before its children
-        // View horizontalDirection public attribute resolution to an internal var.
-        // Resolving the layout direction. LTR is set initially.
+    /**
+     * Resolving the layout direction. LTR is set initially.
+     * We are supposing here that the parent directionality will be resolved before its children
+     */
+    private void resolveHorizontalDirection() {
         mPrivateFlags2 &= ~RESOLVED_LAYOUT_RTL;
         switch (getHorizontalDirection()) {
             case HORIZONTAL_DIRECTION_INHERIT:
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index 1004b5f..57cda97 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -199,6 +199,9 @@
         mVideoSurfaceView.getHolder().setFixedSize(mVideoWidth, mVideoHeight);
     }
 
+    public boolean fullScreenExited() {
+        return (mLayout == null);
+    }
 
     private final WebChromeClient.CustomViewCallback mCallback =
         new WebChromeClient.CustomViewCallback() {
diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java
index 25921bc..ef1906c 100644
--- a/core/java/android/webkit/HTML5VideoInline.java
+++ b/core/java/android/webkit/HTML5VideoInline.java
@@ -12,10 +12,15 @@
  */
 public class HTML5VideoInline extends HTML5VideoView{
 
-    // Due to the fact that SurfaceTexture consume a lot of memory, we make it
-    // as static. m_textureNames is the texture bound with this SurfaceTexture.
+    // Due to the fact that the decoder consume a lot of memory, we make the
+    // surface texture as singleton. But the GL texture (m_textureNames)
+    // associated with the surface texture can be used for showing the screen
+    // shot when paused, so they are not singleton.
     private static SurfaceTexture mSurfaceTexture = null;
-    private static int[] mTextureNames;
+    private int[] mTextureNames;
+    // Every time when the VideoLayer Id change, we need to recreate the
+    // SurfaceTexture in order to delete the old video's decoder memory.
+    private static int mVideoLayerUsingSurfaceTexture = -1;
 
     // Video control FUNCTIONS:
     @Override
@@ -28,11 +33,12 @@
     HTML5VideoInline(int videoLayerId, int position,
             boolean autoStart) {
         init(videoLayerId, position, autoStart);
+        mTextureNames = null;
     }
 
     @Override
     public void decideDisplayMode() {
-        mPlayer.setTexture(getSurfaceTextureInstance());
+        mPlayer.setTexture(getSurfaceTexture(getVideoLayerId()));
     }
 
     // Normally called immediately after setVideoURI. But for full screen,
@@ -52,31 +58,38 @@
     // Inline Video specific FUNCTIONS:
 
     @Override
-    public SurfaceTexture getSurfaceTexture() {
+    public SurfaceTexture getSurfaceTexture(int videoLayerId) {
+        // Create the surface texture.
+        if (videoLayerId != mVideoLayerUsingSurfaceTexture
+            || mSurfaceTexture == null) {
+            if (mTextureNames == null) {
+                mTextureNames = new int[1];
+                GLES20.glGenTextures(1, mTextureNames, 0);
+            }
+            mSurfaceTexture = new SurfaceTexture(mTextureNames[0]);
+        }
+        mVideoLayerUsingSurfaceTexture = videoLayerId;
         return mSurfaceTexture;
     }
 
+    public boolean surfaceTextureDeleted() {
+        return (mSurfaceTexture == null);
+    }
+
     @Override
     public void deleteSurfaceTexture() {
         mSurfaceTexture = null;
+        mVideoLayerUsingSurfaceTexture = -1;
         return;
     }
 
-    // SurfaceTexture is a singleton here , too
-    private SurfaceTexture getSurfaceTextureInstance() {
-        // Create the surface texture.
-        if (mSurfaceTexture == null)
-        {
-            mTextureNames = new int[1];
-            GLES20.glGenTextures(1, mTextureNames, 0);
-            mSurfaceTexture = new SurfaceTexture(mTextureNames[0]);
-        }
-        return mSurfaceTexture;
-    }
-
     @Override
     public int getTextureName() {
-        return mTextureNames[0];
+        if (mTextureNames != null) {
+            return mTextureNames[0];
+        } else {
+            return 0;
+        }
     }
 
     private void setFrameAvailableListener(SurfaceTexture.OnFrameAvailableListener l) {
diff --git a/core/java/android/webkit/HTML5VideoView.java b/core/java/android/webkit/HTML5VideoView.java
index c05498a..5983a44 100644
--- a/core/java/android/webkit/HTML5VideoView.java
+++ b/core/java/android/webkit/HTML5VideoView.java
@@ -287,7 +287,7 @@
         return false;
     }
 
-    public SurfaceTexture getSurfaceTexture() {
+    public SurfaceTexture getSurfaceTexture(int videoLayerId) {
         return null;
     }
 
@@ -315,4 +315,14 @@
         // Only used in HTML5VideoFullScreen
     }
 
+    public boolean surfaceTextureDeleted() {
+        // Only meaningful for HTML5VideoInline
+        return false;
+    }
+
+    public boolean fullScreenExited() {
+        // Only meaningful for HTML5VideoFullScreen
+        return false;
+    }
+
 }
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index 7d8669bf..d0237b5 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -106,12 +106,14 @@
         public static void setBaseLayer(int layer) {
             // Don't do this for full screen mode.
             if (mHTML5VideoView != null
-                    && !mHTML5VideoView.isFullScreenMode()) {
+                && !mHTML5VideoView.isFullScreenMode()
+                && !mHTML5VideoView.surfaceTextureDeleted()) {
                 mBaseLayer = layer;
-                SurfaceTexture surfTexture = mHTML5VideoView.getSurfaceTexture();
-                int textureName = mHTML5VideoView.getTextureName();
 
                 int currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
+                SurfaceTexture surfTexture = mHTML5VideoView.getSurfaceTexture(currentVideoLayerId);
+                int textureName = mHTML5VideoView.getTextureName();
+
                 if (layer != 0 && surfTexture != null && currentVideoLayerId != -1) {
                     int playerState = mHTML5VideoView.getCurrentState();
                     if (mHTML5VideoView.getPlayerBuffering())
@@ -171,14 +173,12 @@
             boolean backFromFullScreenMode = false;
             if (mHTML5VideoView != null) {
                 currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
-                if (mHTML5VideoView instanceof HTML5VideoFullScreen) {
-                    backFromFullScreenMode = true;
-                }
+                backFromFullScreenMode = mHTML5VideoView.fullScreenExited();
             }
 
             if (backFromFullScreenMode
-                ||  currentVideoLayerId != videoLayerId
-                || mHTML5VideoView.getSurfaceTexture() == null) {
+                || currentVideoLayerId != videoLayerId
+                || mHTML5VideoView.surfaceTextureDeleted()) {
                 // Here, we handle the case when switching to a new video,
                 // either inside a WebView or across WebViews
                 // For switching videos within a WebView or across the WebView,
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index e41dd1c..0573d88 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -906,7 +906,7 @@
         // scaleAll(), we need to post a Runnable to ensure requestLayout().
         // Additionally, only update the text wrap scale if the width changed.
         mWebView.post(new PostScale(w != ow &&
-            !mWebView.getSettings().getUseFixedViewport(), mInZoomOverview));
+            !mWebView.getSettings().getUseFixedViewport(), mInZoomOverview, w < ow));
     }
 
     private class PostScale implements Runnable {
@@ -915,10 +915,14 @@
         // it could be changed between the time this callback is initiated and
         // the time it's actually run.
         final boolean mInZoomOverviewBeforeSizeChange;
+        final boolean mInPortraitMode;
 
-        public PostScale(boolean updateTextWrap, boolean inZoomOverview) {
+        public PostScale(boolean updateTextWrap,
+                         boolean inZoomOverview,
+                         boolean inPortraitMode) {
             mUpdateTextWrap = updateTextWrap;
             mInZoomOverviewBeforeSizeChange = inZoomOverview;
+            mInPortraitMode = inPortraitMode;
         }
 
         public void run() {
@@ -927,10 +931,10 @@
                 // still want to send the notification over to webkit.
                 // Keep overview mode unchanged when rotating.
                 float newScale = mActualScale;
-                if (mWebView.getSettings().getUseWideViewPort()) {
-                    final float zoomOverviewScale = getZoomOverviewScale();
-                    newScale = (mInZoomOverviewBeforeSizeChange) ?
-                        zoomOverviewScale : Math.max(mActualScale, zoomOverviewScale);
+                if (mWebView.getSettings().getUseWideViewPort() &&
+                    mInPortraitMode &&
+                    mInZoomOverviewBeforeSizeChange) {
+                    newScale = getZoomOverviewScale();
                 }
                 setZoomScale(newScale, mUpdateTextWrap, true);
                 // update the zoom buttons as the scale can be changed
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 9933d68..586ece8 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -93,6 +93,7 @@
     private boolean mClearingFocus;
     private int mMaxWidth;
     private boolean mVoiceButtonEnabled;
+    private CharSequence mUserQuery;
 
     private SearchableInfo mSearchable;
     private Bundle mAppSearchData;
@@ -372,6 +373,7 @@
         mQueryTextView.setText(query);
         if (query != null) {
             mQueryTextView.setSelection(query.length());
+            mUserQuery = query;
         }
 
         // If the query is not empty and submit is requested, submit the query
@@ -885,6 +887,7 @@
 
     private void onTextChanged(CharSequence newText) {
         CharSequence text = mQueryTextView.getText();
+        mUserQuery = text;
         boolean hasText = !TextUtils.isEmpty(text);
         if (isSubmitButtonEnabled()) {
             updateSubmitButton(hasText);
@@ -1124,7 +1127,7 @@
         if (data != null) {
             intent.setData(data);
         }
-        intent.putExtra(SearchManager.USER_QUERY, query);
+        intent.putExtra(SearchManager.USER_QUERY, mUserQuery);
         if (query != null) {
             intent.putExtra(SearchManager.QUERY, query);
         }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 18c3b24e..5886c64 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8787,10 +8787,22 @@
         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
             TypedArray styledAttributes = mContext.obtainStyledAttributes(R.styleable.Theme);
 
-            mode.setTitle(mContext.getString(com.android.internal.R.string.textSelectionCABTitle));
+            boolean allowText = getContext().getResources().getBoolean(
+                    com.android.internal.R.bool.allow_action_menu_item_text_with_icon);
+
+            mode.setTitle(allowText ? 
+                    mContext.getString(com.android.internal.R.string.textSelectionCABTitle) : null);
             mode.setSubtitle(null);
 
+            int selectAllIconId = 0; // No icon by default
+            if (!allowText) {
+                // Provide an icon, text will not be displayed on smaller screens.
+                selectAllIconId = styledAttributes.getResourceId(
+                        R.styleable.Theme_actionModeSelectAllDrawable, 0);
+            }
+
             menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
+                    setIcon(selectAllIconId).
                     setAlphabeticShortcut('a').
                     setShowAsAction(
                             MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index beacf75..479788d 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.Button;
@@ -103,6 +104,12 @@
         // TODO Support checkable action items
     }
 
+    private void updateTextButtonVisibility() {
+        boolean visible = !TextUtils.isEmpty(mTextButton.getText());
+        visible = visible && (mImageButton.getDrawable() == null || mItemData.showsTextAsAction());
+        mTextButton.setVisibility(visible ? VISIBLE : GONE);
+    }
+
     public void setIcon(Drawable icon) {
         mImageButton.setImageDrawable(icon);
         if (icon != null) {
@@ -111,9 +118,9 @@
             mImageButton.setVisibility(GONE);
         }
 
-        mTextButton.setVisibility(icon == null || mItemData.showsTextAsAction() ? VISIBLE : GONE);
+        updateTextButtonVisibility();
     }
-    
+
     public boolean hasText() {
         return mTextButton.getVisibility() != GONE;
     }
@@ -128,10 +135,9 @@
         // populate accessibility description with title
         setContentDescription(title);
 
-        if (mImageButton.getDrawable() == null || mItemData.showsTextAsAction()) {
-            mTextButton.setText(mTitle);
-            mTextButton.setVisibility(VISIBLE);
-        }
+        mTextButton.setText(mTitle);
+
+        updateTextButtonVisibility();
     }
 
     public boolean showsIcon() {
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index c82323e..deed1c5 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -15,26 +15,26 @@
  */
 package com.android.internal.widget;
 
-import com.android.internal.R;
-import com.android.internal.view.menu.ActionMenuPresenter;
-import com.android.internal.view.menu.ActionMenuView;
-import com.android.internal.view.menu.MenuBuilder;
-
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.ActionMode;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup.LayoutParams;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.internal.R;
+import com.android.internal.view.menu.ActionMenuPresenter;
+import com.android.internal.view.menu.ActionMenuView;
+import com.android.internal.view.menu.MenuBuilder;
+
 /**
  * @hide
  */
@@ -130,26 +130,23 @@
             mTitleLayout = (LinearLayout) getChildAt(getChildCount() - 1);
             mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
             mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
-            if (mTitle != null) {
-                mTitleView.setText(mTitle);
-                if (mTitleStyleRes != 0) {
-                    mTitleView.setTextAppearance(mContext, mTitleStyleRes);
-                }
+            if (mTitleStyleRes != 0) {
+                mTitleView.setTextAppearance(mContext, mTitleStyleRes);
             }
-            if (mSubtitle != null) {
-                mSubtitleView.setText(mSubtitle);
-                if (mSubtitleStyleRes != 0) {
-                    mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
-                }
-                mSubtitleView.setVisibility(VISIBLE);
+            if (mSubtitleStyleRes != 0) {
+                mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
             }
-        } else {
-            mTitleView.setText(mTitle);
-            mSubtitleView.setText(mSubtitle);
-            mSubtitleView.setVisibility(mSubtitle != null ? VISIBLE : GONE);
-            if (mTitleLayout.getParent() == null) {
-                addView(mTitleLayout);
-            }
+        }
+
+        mTitleView.setText(mTitle);
+        mSubtitleView.setText(mSubtitle);
+
+        final boolean hasTitle = !TextUtils.isEmpty(mTitle);
+        final boolean hasSubtitle = !TextUtils.isEmpty(mSubtitle);
+        mSubtitleView.setVisibility(hasSubtitle ? VISIBLE : GONE);
+        mTitleLayout.setVisibility(hasTitle || hasSubtitle ? VISIBLE : GONE);
+        if (mTitleLayout.getParent() == null) {
+            addView(mTitleLayout);
         }
     }
 
@@ -228,6 +225,7 @@
         mAnimateInOnLayout = false;
     }
 
+    @Override
     public boolean showOverflowMenu() {
         if (mMenuPresenter != null) {
             return mMenuPresenter.showOverflowMenu();
@@ -235,6 +233,7 @@
         return false;
     }
 
+    @Override
     public boolean hideOverflowMenu() {
         if (mMenuPresenter != null) {
             return mMenuPresenter.hideOverflowMenu();
@@ -242,6 +241,7 @@
         return false;
     }
 
+    @Override
     public boolean isOverflowMenuShowing() {
         if (mMenuPresenter != null) {
             return mMenuPresenter.isOverflowMenuShowing();
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 0dc0422..b3666cb 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -30,6 +30,7 @@
 import android.os.SystemClock;
 import android.os.storage.IMountService;
 import android.provider.Settings;
+import android.security.KeyStore;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -47,7 +48,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * Utilities for the lock patten and its settings.
+ * Utilities for the lock pattern and its settings.
  */
 public class LockPatternUtils {
 
@@ -397,6 +398,7 @@
             raf.close();
             DevicePolicyManager dpm = getDevicePolicyManager();
             if (pattern != null) {
+                KeyStore.getInstance().password(patternToString(pattern));
                 setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
                 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
                 dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern
@@ -488,6 +490,9 @@
                 // Update the encryption password.
                 updateEncryptionPassword(password);
 
+                // Update the keystore password
+                KeyStore.getInstance().password(password);
+
                 int computedQuality = computePasswordQuality(password);
                 setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality));
                 if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
@@ -646,7 +651,7 @@
      * @param password the gesture pattern.
      * @return the hash of the pattern in a byte array.
      */
-     public byte[] passwordToHash(String password) {
+    public byte[] passwordToHash(String password) {
         if (password == null) {
             return null;
         }
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index e539cd2..e2832ed 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -42,7 +42,7 @@
 #include <SkiaColorFilter.h>
 #include <Rect.h>
 
-#include "TextLayout.h"
+#include <TextLayout.h>
 
 namespace android {
 
@@ -419,7 +419,7 @@
 
 static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
         jfloat x, jfloat y, int flags, SkPaint* paint) {
-#if 0 // TODO: replace "0" by "RTL_USE_HARFBUZZ" when renderer->drawGlyphs() is implemented
+#if RTL_USE_HARFBUZZ
     sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
             paint, text, 0, count, count, flags);
     if (value == NULL) {
@@ -431,7 +431,8 @@
 #endif
     const jchar* glyphArray = value->getGlyphs();
     int glyphCount = value->getGlyphsCount();
-    renderer->drawGlyphs((const char*) glyphArray, 0, glyphCount << 1, x, y, paint);
+    int bytesCount = glyphCount * sizeof(jchar);
+    renderer->drawText((const char*) glyphArray, bytesCount, glyphCount, x, y, paint);
 #else
     const jchar *workText;
     jchar* buffer = NULL;
@@ -446,7 +447,7 @@
 static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
         jint start, jint count, jint contextCount, jfloat x, jfloat y,
         int flags, SkPaint* paint) {
-#if 0 // TODO: replace "0" by "RTL_USE_HARFBUZZ" when renderer->drawGlyphs() is implemented
+#if RTL_USE_HARFBUZZ
     sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
             paint, text, start, count, contextCount, flags);
     if (value == NULL) {
@@ -458,7 +459,8 @@
 #endif
     const jchar* glyphArray = value->getGlyphs();
     int glyphCount = value->getGlyphsCount();
-    renderer->drawGlyphs((const char*) glyphArray, 0, glyphCount << 1, x, y, paint);
+    int bytesCount = glyphCount * sizeof(jchar);
+    renderer->drawText((const char*) glyphArray, bytesCount, glyphCount, x, y, paint);
 #else
     uint8_t rtl = flags & 0x1;
     if (rtl) {
diff --git a/core/res/res/drawable-hdpi/ic_menu_selectall_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_selectall_holo_dark.png
new file mode 100644
index 0000000..5579443
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_selectall_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_selectall_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_selectall_holo_light.png
new file mode 100644
index 0000000..6674914
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_selectall_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_selectall_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_selectall_holo_dark.png
new file mode 100644
index 0000000..caec299
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_selectall_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_selectall_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_selectall_holo_light.png
new file mode 100644
index 0000000..434f5d1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_selectall_holo_light.png
Binary files differ
diff --git a/core/res/res/layout/am_compat_mode_dialog.xml b/core/res/res/layout/am_compat_mode_dialog.xml
new file mode 100644
index 0000000..a8d39cf
--- /dev/null
+++ b/core/res/res/layout/am_compat_mode_dialog.xml
@@ -0,0 +1,60 @@
+<?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:layout_width="match_parent" android:layout_height="match_parent"
+        android:layout_marginLeft="40dp" android:layout_marginRight="40dp"
+        android:layout_marginTop="15dp" android:layout_marginBottom="15dp"
+        android:orientation="vertical">
+    <LinearLayout
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:orientation="horizontal" android:baselineAligned="true">
+        <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+                android:layout_marginLeft="10dp" android:layout_marginRight="10dp"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textSize="18sp"
+                android:text="@string/screen_compat_mode_scale"
+                />
+        <Switch
+                android:id="@+id/compat_checkbox"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_marginRight="10dp"
+                />
+    </LinearLayout>
+
+    <View android:layout_width="wrap_content" android:layout_height="1dp"
+            android:layout_marginTop="10dp" android:layout_marginBottom="10dp"
+            android:background="@android:drawable/divider_horizontal_dark"
+            />
+
+    <CheckBox android:id="@+id/ask_checkbox"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:text="@string/screen_compat_mode_show"
+            />
+    <TextView
+        android:id="@+id/reask_hint"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:gravity="center"
+        android:visibility="invisible"
+        android:text="@string/screen_compat_mode_hint" />
+</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e325b8d..db76211 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -619,6 +619,8 @@
         <attr name="actionModeCopyDrawable" format="reference" />
         <!-- Drawable to use for the Paste action button in Contextual Action Bar -->
         <attr name="actionModePasteDrawable" format="reference" />
+        <!-- Drawable to use for the Select all action button in Contextual Action Bar -->
+        <attr name="actionModeSelectAllDrawable" format="reference" />
         <!-- Drawable to use for the Share action button in WebView selection action modes -->
         <attr name="actionModeShareDrawable" format="reference" />
         <!-- Drawable to use for the Find action button in WebView selection action modes -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 41a566c..4c3cfc1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1684,6 +1684,7 @@
   <public type="attr" name="layout_rowWeight" />
   <public type="attr" name="layout_columnSpan" />
   <public type="attr" name="layout_columnWeight" />
+  <public type="attr" name="actionModeSelectAllDrawable" />
 
   <public type="attr" name="isAuxiliary" />
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 816546b..b8a4443 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2360,6 +2360,12 @@
     <string name="launch_warning_replace"><xliff:g id="app_name">%1$s</xliff:g> is now running.</string>
     <!-- [CHAR LIMIT=50] Title of the alert when application launches on top of another. -->
     <string name="launch_warning_original"><xliff:g id="app_name">%1$s</xliff:g> was originally launched.</string>
+    <!-- [CHAR LIMIT=50] Compat mode dialog: compat mode switch label. -->
+    <string name="screen_compat_mode_scale">Scale</string>
+    <!-- [CHAR LIMIT=50] Compat mode dialog: compat mode switch label. -->
+    <string name="screen_compat_mode_show">Always show</string>
+    <!-- [CHAR LIMIT=200] Compat mode dialog: hint to re-enable compat mode dialog. -->
+    <string name="screen_compat_mode_hint">Re-enable this with Settings &gt; Applications &gt; Manage applications.</string>
 
     <!-- Text of the alert that is displayed when an application has violated StrictMode. -->
     <string name="smv_application">The application <xliff:g id="application">%1$s</xliff:g>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 0a614b2..4f39da4 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -267,6 +267,7 @@
         <item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_holo_dark</item>
         <item name="actionModeCopyDrawable">@android:drawable/ic_menu_copy_holo_dark</item>
         <item name="actionModePasteDrawable">@android:drawable/ic_menu_paste_holo_dark</item>
+        <item name="actionModeSelectAllDrawable">@android:drawable/ic_menu_selectall_holo_dark</item>
         <item name="actionModeShareDrawable">@android:drawable/ic_menu_share_holo_dark</item>
         <item name="actionModeFindDrawable">@android:drawable/ic_menu_find_holo_dark</item>
         <item name="actionModeWebSearchDrawable">@android:drawable/ic_menu_search</item>
@@ -396,6 +397,7 @@
         <item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_holo_light</item>
         <item name="actionModeCopyDrawable">@android:drawable/ic_menu_copy_holo_light</item>
         <item name="actionModePasteDrawable">@android:drawable/ic_menu_paste_holo_light</item>
+        <item name="actionModeSelectAllDrawable">@android:drawable/ic_menu_selectall_holo_light</item>
         <item name="actionModeShareDrawable">@android:drawable/ic_menu_share_holo_light</item>
         <item name="actionModeFindDrawable">@android:drawable/ic_menu_find_holo_light</item>
         <item name="actionModeWebSearchDrawable">@android:drawable/ic_menu_search_holo_light</item>
@@ -1029,6 +1031,7 @@
         <item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_holo_dark</item>
         <item name="actionModeCopyDrawable">@android:drawable/ic_menu_copy_holo_dark</item>
         <item name="actionModePasteDrawable">@android:drawable/ic_menu_paste_holo_dark</item>
+        <item name="actionModeSelectAllDrawable">@android:drawable/ic_menu_selectall_holo_dark</item>
         <item name="actionModeShareDrawable">@android:drawable/ic_menu_share_holo_dark</item>
         <item name="actionModeFindDrawable">@android:drawable/ic_menu_find_holo_dark</item>
         <item name="actionModeWebSearchDrawable">@android:drawable/ic_menu_search_holo_dark</item>
@@ -1316,6 +1319,7 @@
         <item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_holo_light</item>
         <item name="actionModeCopyDrawable">@android:drawable/ic_menu_copy_holo_light</item>
         <item name="actionModePasteDrawable">@android:drawable/ic_menu_paste_holo_light</item>
+        <item name="actionModeSelectAllDrawable">@android:drawable/ic_menu_selectall_holo_light</item>
         <item name="actionModeShareDrawable">@android:drawable/ic_menu_share_holo_light</item>
         <item name="actionModeFindDrawable">@android:drawable/ic_menu_find_holo_light</item>
         <item name="actionModeWebSearchDrawable">@android:drawable/ic_menu_search_holo_light</item>
diff --git a/core/tests/bluetoothtests/Android.mk b/core/tests/bluetoothtests/Android.mk
new file mode 100644
index 0000000..4a1d18c
--- /dev/null
+++ b/core/tests/bluetoothtests/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+# Include all test java files.
+LOCAL_SRC_FILES := \
+	$(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_PACKAGE_NAME := BluetoothTests
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml
new file mode 100644
index 0000000..96db035
--- /dev/null
+++ b/core/tests/bluetoothtests/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.bluetooth.tests" >
+
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+
+    <application >
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="android.bluetooth.BluetoothTestRunner"
+            android:targetPackage="com.android.bluetooth.tests"
+            android:label="Bluetooth Tests" />
+
+</manifest>
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothRebootStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java
similarity index 100%
rename from core/tests/coretests/src/android/bluetooth/BluetoothRebootStressTest.java
rename to core/tests/bluetoothtests/src/android/bluetooth/BluetoothRebootStressTest.java
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
similarity index 98%
rename from core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
rename to core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
index 7f13791..abd7d9a 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
@@ -120,6 +120,9 @@
      */
     public void testEnablePan() {
         int iterations = BluetoothTestRunner.sEnablePanIterations;
+        if (iterations == 0) {
+            return;
+        }
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         mTestUtils.disable(adapter);
         mTestUtils.enable(adapter);
@@ -170,6 +173,9 @@
      */
     public void testAcceptPair() {
         int iterations = BluetoothTestRunner.sPairIterations;
+        if (iterations == 0) {
+            return;
+        }
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
         mTestUtils.disable(adapter);
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
similarity index 100%
rename from core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
rename to core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
similarity index 100%
rename from core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
rename to core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 6dc5c21..6084dd2 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1230,9 +1230,4 @@
     <instrumentation android:name="android.test.InstrumentationTestRunner"
             android:targetPackage="com.android.frameworks.coretests"
             android:label="Frameworks Core Tests" />
-
-    <instrumentation android:name="android.bluetooth.BluetoothTestRunner"
-            android:targetPackage="com.android.frameworks.coretests"
-            android:label="Bluetooth Tests" />
-
 </manifest>
diff --git a/core/tests/coretests/src/android/bluetooth/AtParserTest.java b/core/tests/coretests/src/android/bluetooth/AtParserTest.java
deleted file mode 100644
index c5aa52b..0000000
--- a/core/tests/coretests/src/android/bluetooth/AtParserTest.java
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.bluetooth.AtCommandHandler;
-import android.bluetooth.AtCommandResult;
-import android.bluetooth.AtParser;
-
-import java.util.*;
-import junit.framework.*;
-
-public class AtParserTest extends TestCase {
-
-    /* An AtCommandHandler instrumented for testing purposes
-     */
-    private class HandlerTest extends AtCommandHandler {
-        boolean mBasicCalled, mActionCalled, mReadCalled, mTestCalled, 
-                mSetCalled;
-        int mBasicReturn, mActionReturn, mReadReturn, mTestReturn, mSetReturn;
-        Object[] mSetArgs;
-        String mBasicArgs;
-
-        HandlerTest() {
-            this(AtCommandResult.ERROR, AtCommandResult.ERROR,
-                 AtCommandResult.ERROR, AtCommandResult.ERROR,
-                 AtCommandResult.ERROR);
-        }
-
-        HandlerTest(int a, int b, int c, int d, int e) {
-            mBasicReturn = a;
-            mActionReturn = b;
-            mReadReturn = c;
-            mSetReturn = d;
-            mTestReturn = e;
-            reset();
-        }
-        public void reset() {
-            mBasicCalled = false;
-            mActionCalled = false;
-            mReadCalled = false;
-            mSetCalled = false;
-            mTestCalled = false;
-            mSetArgs = null;
-            mBasicArgs = null;
-        }
-        public boolean wasCalled() {   // helper
-            return mBasicCalled || mActionCalled || mReadCalled ||
-                    mTestCalled || mSetCalled;
-        }
-        @Override
-        public AtCommandResult handleBasicCommand(String args) {
-            mBasicCalled = true;
-            mBasicArgs = args;
-            return new AtCommandResult(mBasicReturn);
-        }
-        @Override
-        public AtCommandResult handleActionCommand() {
-            mActionCalled = true;
-            return new AtCommandResult(mActionReturn);
-        }
-        @Override
-        public AtCommandResult handleReadCommand() {
-            mReadCalled = true;
-            return new AtCommandResult(mReadReturn);
-        }
-        @Override
-        public AtCommandResult handleSetCommand(Object[] args) {
-            mSetCalled = true;
-            mSetArgs = args;
-            return new AtCommandResult(mSetReturn);
-        }
-        @Override
-        public AtCommandResult handleTestCommand() {
-            mTestCalled = true;
-            return new AtCommandResult(mTestReturn);
-        }
-    }
-
-    private AtParser mParser;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mParser = new AtParser();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-    }
-
-
-    /* Test that the right method is being called
-     */
-/*    public void testBasic1() throws Exception {
-        HandlerTest D = new HandlerTest(0, 1, 1, 1, 1);
-        HandlerTest A = new HandlerTest(0, 1, 1, 1, 1);
-        mParser.register('D', D);
-        mParser.register('A', A);
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("  A T D = ? T 1 2 3  4   ").toStrings()));
-        assertTrue(D.mBasicCalled);
-        assertFalse(D.mActionCalled);
-        assertFalse(D.mTestCalled);
-        assertFalse(D.mSetCalled);
-        assertFalse(D.mReadCalled);
-        assertFalse(A.wasCalled());
-        assertEquals("=?T1234", D.mBasicArgs);
-    }
-*/
-    /* Test some crazy strings
-     *//*
-    public void testBasic2() throws Exception {
-        HandlerTest A = new HandlerTest(0, 1, 1, 1, 1);
-        mParser.register('A', A);
-
-        assertTrue(Arrays.equals(
-                   new String[]{},
-                   mParser.process("     ").toStrings()));
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("  a T a t \"\"  1 2 3 a 4   ")
-                           .toStrings()));
-        assertEquals("T\"\"123A4", A.mBasicArgs);
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("  a T a t  \"foo BaR12Z\" 1 2 3 a 4   ")
-                           .toStrings()));
-        assertEquals("T\"foo BaR12Z\"123A4", A.mBasicArgs);
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("ATA\"").toStrings()));
-        assertEquals("\"\"", A.mBasicArgs);
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("ATA\"a").toStrings()));
-        assertEquals("\"a\"", A.mBasicArgs);
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("ATa\" ").toStrings()));
-        assertEquals("\" \"", A.mBasicArgs);
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("ATA  \"one \" two \"t hr ee ")
-                           .toStrings()));
-        assertEquals("\"one \"TWO\"t hr ee \"", A.mBasicArgs);
-    }*/
-
-    /* Simple extended commands
-     *//*
-    public void testExt1() throws Exception {
-        HandlerTest A = new HandlerTest(1, 0, 0, 0, 0);
-        mParser.register("+A", A);
-
-        assertTrue(Arrays.equals(
-                   new String[]{"ERROR"},
-                   mParser.process("AT+B").toStrings()));
-        assertFalse(A.wasCalled());
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("AT+A").toStrings()));
-        assertTrue(A.mActionCalled);
-        A.mActionCalled = false;
-        assertFalse(A.wasCalled());
-        A.reset();
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("AT+A=").toStrings()));
-        assertTrue(A.mSetCalled);
-        A.mSetCalled = false;
-        assertFalse(A.wasCalled());
-        assertEquals(1, A.mSetArgs.length);
-        A.reset();
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("AT+A=?").toStrings()));
-        assertTrue(A.mTestCalled);
-        A.mTestCalled = false;
-        assertFalse(A.wasCalled());
-        A.reset();
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("AT+A?").toStrings()));
-        assertTrue(A.mReadCalled);
-        A.mReadCalled = false;
-        assertFalse(A.wasCalled());
-        A.reset();
-    }
-*/
-
-
-    /* Test chained commands
-     *//*
-    public void testChain1() throws Exception {
-        HandlerTest A = new HandlerTest(0, 1, 1, 1, 1);
-        HandlerTest B = new HandlerTest(1, 0, 0, 0, 0);
-        HandlerTest C = new HandlerTest(1, 1, 1, 1, 1);
-        mParser.register('A', A);
-        mParser.register("+B", B);
-        mParser.register("+C", C);
-
-        assertTrue(Arrays.equals(
-                   new String[]{"ERROR"},
-                   mParser.process("AT+B;+C").toStrings()));
-        assertTrue(B.mActionCalled);
-        assertTrue(C.mActionCalled);
-        B.reset();
-        C.reset();
-
-        assertTrue(Arrays.equals(
-                   new String[]{"ERROR"},
-                   mParser.process("AT+C;+B").toStrings()));
-        assertFalse(B.wasCalled());
-        assertTrue(C.mActionCalled);
-        B.reset();
-        C.reset();
-    }*/
-
-    /* Test Set command
-     *//*
-    public void testSet1() throws Exception {
-        HandlerTest A = new HandlerTest(1, 1, 1, 0, 1);
-        mParser.register("+AAAA", A);
-        Object[] expectedResult;
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("AT+AAAA=1").toStrings()));
-        expectedResult = new Object[]{(Integer)1};
-        assertTrue(Arrays.equals(expectedResult, A.mSetArgs));
-        A.reset();
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("AT+AAAA=1,2,3").toStrings()));
-        expectedResult = new Object[]{(Integer)1, (Integer)2, (Integer)3};
-        assertTrue(Arrays.equals(expectedResult, A.mSetArgs));
-        A.reset();
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("AT+AAAA=3,0,0,1").toStrings()));
-        expectedResult = new Object[]{(Integer)3, (Integer)0, (Integer)0,
-                                      (Integer)1};
-        assertTrue(Arrays.equals(expectedResult, A.mSetArgs));
-        A.reset();
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("AT+AAAA=\"foo\",1,\"b,ar").toStrings()));
-        expectedResult = new Object[]{"\"foo\"", 1, "\"b,ar\""};
-        assertTrue(Arrays.equals(expectedResult, A.mSetArgs));
-        A.reset();
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("AT+AAAA=").toStrings()));
-        expectedResult = new Object[]{""};
-        assertTrue(Arrays.equals(expectedResult, A.mSetArgs));
-        A.reset();
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("AT+AAAA=,").toStrings()));
-        expectedResult = new Object[]{"", ""};
-        assertTrue(Arrays.equals(expectedResult, A.mSetArgs));
-        A.reset();
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("AT+AAAA=,,,").toStrings()));
-        expectedResult = new Object[]{"", "", "", ""};
-        assertTrue(Arrays.equals(expectedResult, A.mSetArgs));
-        A.reset();
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("AT+AAAA=,1,,\"foo\",").toStrings()));
-        expectedResult = new Object[]{"", 1, "", "\"foo\"", ""};
-        assertEquals(5, A.mSetArgs.length);
-        assertTrue(Arrays.equals(expectedResult, A.mSetArgs));
-        A.reset();
-    }*/
-
-    /* Test repeat command "A/"
-     *//*
-    public void testRepeat() throws Exception {
-        HandlerTest A = new HandlerTest(0, 0, 0, 0, 0);
-        mParser.register('A', A);
-
-        // Try repeated command on fresh parser
-        assertTrue(Arrays.equals(
-                   new String[]{},
-                   mParser.process("A/").toStrings()));
-        assertFalse(A.wasCalled());
-        A.reset();
-
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("ATA").toStrings()));
-        assertTrue(A.mBasicCalled);
-        assertEquals("", A.mBasicArgs);
-        A.reset();
-
-        // Now repeat the command
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("A/").toStrings()));
-        assertTrue(A.mBasicCalled);
-        assertEquals("", A.mBasicArgs);
-        A.reset();
-
-        // Multiple repeats
-        assertTrue(Arrays.equals(
-                   new String[]{"OK"},
-                   mParser.process("A/").toStrings()));
-        assertTrue(A.mBasicCalled);
-        assertEquals("", A.mBasicArgs);
-        A.reset();
-
-    }*/
-}
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index baab2e8..605680a 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -130,7 +130,7 @@
      * sampleRate:         Track sampling rate in Hz.
      * format:             Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
      *                     16 bits per sample).
-     * channels:           Channel mask: see audio_channels_t.
+     * channelMask:        Channel mask: see audio_channels_t.
      * frameCount:         Total size of track PCM buffer in frames. This defines the
      *                     latency of the track.
      * flags:              A bitmask of acoustic values from enum record_flags.  It enables
@@ -151,7 +151,7 @@
                         AudioRecord(int inputSource,
                                     uint32_t sampleRate = 0,
                                     int format          = 0,
-                                    uint32_t channels = AUDIO_CHANNEL_IN_MONO,
+                                    uint32_t channelMask = AUDIO_CHANNEL_IN_MONO,
                                     int frameCount      = 0,
                                     uint32_t flags      = 0,
                                     callback_t cbf = 0,
@@ -177,7 +177,7 @@
             status_t    set(int inputSource     = 0,
                             uint32_t sampleRate = 0,
                             int format          = 0,
-                            uint32_t channels = AUDIO_CHANNEL_IN_MONO,
+                            uint32_t channelMask = AUDIO_CHANNEL_IN_MONO,
                             int frameCount      = 0,
                             uint32_t flags      = 0,
                             callback_t cbf = 0,
@@ -348,8 +348,8 @@
 
             bool processAudioBuffer(const sp<ClientRecordThread>& thread);
             status_t openRecord_l(uint32_t sampleRate,
-                                int format,
-                                int channelCount,
+                                uint32_t format,
+                                uint32_t channelMask,
                                 int frameCount,
                                 uint32_t flags,
                                 audio_io_handle_t input);
@@ -364,10 +364,10 @@
     uint32_t                mFrameCount;
 
     audio_track_cblk_t*     mCblk;
-    uint8_t                 mFormat;
+    uint32_t                mFormat;
     uint8_t                 mChannelCount;
     uint8_t                 mInputSource;
-    uint8_t                 mReserved;
+    uint8_t                 mReserved[2];
     status_t                mStatus;
     uint32_t                mLatency;
 
@@ -382,7 +382,7 @@
     uint32_t                mNewPosition;
     uint32_t                mUpdatePeriod;
     uint32_t                mFlags;
-    uint32_t                mChannels;
+    uint32_t                mChannelMask;
     audio_io_handle_t       mInput;
     int                     mSessionId;
 };
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index de928da..df30e8c 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -69,8 +69,8 @@
             MUTE    = 0x00000001
         };
         uint32_t    flags;
-        int         channelCount;
         int         format;
+        int         channelCount; // will be removed in the future, do not use
         size_t      frameCount;
         size_t      size;
         union {
@@ -129,7 +129,7 @@
      * sampleRate:         Track sampling rate in Hz.
      * format:             Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
      *                     16 bits per sample).
-     * channels:           Channel mask: see audio_channels_t.
+     * channelMask:        Channel mask: see audio_channels_t.
      * frameCount:         Total size of track PCM buffer in frames. This defines the
      *                     latency of the track.
      * flags:              Reserved for future use.
@@ -143,7 +143,7 @@
                         AudioTrack( int streamType,
                                     uint32_t sampleRate  = 0,
                                     int format           = 0,
-                                    int channels         = 0,
+                                    int channelMask      = 0,
                                     int frameCount       = 0,
                                     uint32_t flags       = 0,
                                     callback_t cbf       = 0,
@@ -163,7 +163,7 @@
                         AudioTrack( int streamType,
                                     uint32_t sampleRate = 0,
                                     int format          = 0,
-                                    int channels        = 0,
+                                    int channelMask     = 0,
                                     const sp<IMemory>& sharedBuffer = 0,
                                     uint32_t flags      = 0,
                                     callback_t cbf      = 0,
@@ -187,7 +187,7 @@
             status_t    set(int streamType      =-1,
                             uint32_t sampleRate = 0,
                             int format          = 0,
-                            int channels        = 0,
+                            int channelMask     = 0,
                             int frameCount      = 0,
                             uint32_t flags      = 0,
                             callback_t cbf      = 0,
@@ -438,8 +438,8 @@
             bool processAudioBuffer(const sp<AudioTrackThread>& thread);
             status_t createTrack_l(int streamType,
                                  uint32_t sampleRate,
-                                 int format,
-                                 int channelCount,
+                                 uint32_t format,
+                                 uint32_t channelMask,
                                  int frameCount,
                                  uint32_t flags,
                                  const sp<IMemory>& sharedBuffer,
@@ -459,11 +459,12 @@
     uint32_t                mFrameCount;
 
     audio_track_cblk_t*     mCblk;
+    uint32_t                mFormat;
     uint8_t                 mStreamType;
-    uint8_t                 mFormat;
     uint8_t                 mChannelCount;
     uint8_t                 mMuted;
-    uint32_t                mChannels;
+    uint8_t                 mReserved;
+    uint32_t                mChannelMask;
     status_t                mStatus;
     uint32_t                mLatency;
 
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index d8fdc27..4037c46 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -48,8 +48,8 @@
                                 pid_t pid,
                                 int streamType,
                                 uint32_t sampleRate,
-                                int format,
-                                int channelCount,
+                                uint32_t format,
+                                uint32_t channelMask,
                                 int frameCount,
                                 uint32_t flags,
                                 const sp<IMemory>& sharedBuffer,
@@ -61,8 +61,8 @@
                                 pid_t pid,
                                 int input,
                                 uint32_t sampleRate,
-                                int format,
-                                int channelCount,
+                                uint32_t format,
+                                uint32_t channelMask,
                                 int frameCount,
                                 uint32_t flags,
                                 int *sessionId,
@@ -73,7 +73,7 @@
      */
     virtual     uint32_t    sampleRate(int output) const = 0;
     virtual     int         channelCount(int output) const = 0;
-    virtual     int         format(int output) const = 0;
+    virtual     uint32_t    format(int output) const = 0;
     virtual     size_t      frameCount(int output) const = 0;
     virtual     uint32_t    latency(int output) const = 0;
 
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 1827c3e..072329d 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -82,7 +82,7 @@
                 // 16 bit because data is converted to 16 bit before being stored in buffer
 
                 uint8_t     frameSize;
-                uint8_t     channelCount;
+                uint8_t     pad1;
                 uint16_t    bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger
 
                 uint16_t    waitTimeMs;      // Cumulated wait time
@@ -90,6 +90,7 @@
     volatile    int32_t     flags;
 
                 // Cache line boundary (32 bytes)
+
                             audio_track_cblk_t();
                 uint32_t    stepUser(uint32_t frameCount);
                 bool        stepServer(uint32_t frameCount);
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 7183688..9058cae 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -32,6 +32,8 @@
  * preclude the use of hardware crypto.
  */
 public class KeyStore {
+
+    // ResponseCodes
     public static final int NO_ERROR = 1;
     public static final int LOCKED = 2;
     public static final int UNINITIALIZED = 3;
@@ -43,6 +45,9 @@
     public static final int UNDEFINED_ACTION = 9;
     public static final int WRONG_PASSWORD = 10;
 
+    // States
+    public enum State { UNLOCKED, LOCKED, UNINITIALIZED };
+
     private static final LocalSocketAddress sAddress = new LocalSocketAddress(
             "keystore", LocalSocketAddress.Namespace.RESERVED);
 
@@ -54,31 +59,35 @@
         return new KeyStore();
     }
 
-    public int test() {
+    public State state() {
         execute('t');
-        return mError;
+        switch (mError) {
+            case NO_ERROR: return State.UNLOCKED;
+            case LOCKED: return State.LOCKED;
+            case UNINITIALIZED: return State.UNINITIALIZED;
+            default: throw new AssertionError(mError);
+        }
     }
 
-    public byte[] get(byte[] key) {
+    private byte[] get(byte[] key) {
         ArrayList<byte[]> values = execute('g', key);
         return (values == null || values.isEmpty()) ? null : values.get(0);
     }
 
-    public String get(String key) {
-        byte[] value = get(getBytes(key));
-        return (value == null) ? null : toString(value);
+    public byte[] get(String key) {
+        return get(getBytes(key));
     }
 
-    public boolean put(byte[] key, byte[] value) {
+    private boolean put(byte[] key, byte[] value) {
         execute('i', key, value);
         return mError == NO_ERROR;
     }
 
-    public boolean put(String key, String value) {
-        return put(getBytes(key), getBytes(value));
+    public boolean put(String key, byte[] value) {
+        return put(getBytes(key), value);
     }
 
-    public boolean delete(byte[] key) {
+    private boolean delete(byte[] key) {
         execute('d', key);
         return mError == NO_ERROR;
     }
@@ -87,7 +96,7 @@
         return delete(getBytes(key));
     }
 
-    public boolean contains(byte[] key) {
+    private boolean contains(byte[] key) {
         execute('e', key);
         return mError == NO_ERROR;
     }
@@ -118,19 +127,11 @@
         return mError == NO_ERROR;
     }
 
-    public boolean password(byte[] oldPassword, byte[] newPassword) {
-        execute('p', oldPassword, newPassword);
+    private boolean password(byte[] password) {
+        execute('p', password);
         return mError == NO_ERROR;
     }
 
-    public boolean password(String oldPassword, String newPassword) {
-        return password(getBytes(oldPassword), getBytes(newPassword));
-    }
-
-    public boolean password(byte[] password) {
-        return password(password, password);
-    }
-
     public boolean password(String password) {
         return password(getBytes(password));
     }
@@ -140,7 +141,7 @@
         return mError == NO_ERROR;
     }
 
-    public boolean unlock(byte[] password) {
+    private boolean unlock(byte[] password) {
         execute('u', password);
         return mError == NO_ERROR;
     }
@@ -149,6 +150,11 @@
         return unlock(getBytes(password));
     }
 
+    public boolean isEmpty() {
+        execute('z');
+        return mError == KEY_NOT_FOUND;
+    }
+
     public int getLastError() {
         return mError;
     }
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index 6630a4f..4582aa0 100755
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -20,6 +20,9 @@
 import android.security.KeyStore;
 import android.test.ActivityUnitTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
+import java.nio.charset.Charsets;
+import java.util.Arrays;
+import java.util.HashSet;
 
 /**
  * Junit / Instrumentation test case for KeyStore class
@@ -31,16 +34,15 @@
 @MediumTest
 public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
     private static final String TEST_PASSWD = "12345678";
-    private static final String TEST_EMPTY_PASSWD = "";
-    private static final String TEST_SHORT_PASSWD = "short";
     private static final String TEST_PASSWD2 = "87654321";
     private static final String TEST_KEYNAME = "testkey";
     private static final String TEST_KEYNAME1 = "testkey1";
     private static final String TEST_KEYNAME2 = "testkey2";
-    private static final String TEST_KEYVALUE = "test value";
+    private static final byte[] TEST_KEYVALUE = "test value".getBytes(Charsets.UTF_8);
 
     // "Hello, World" in Chinese
-    private static final String TEST_I18N = "\u4F60\u597D, \u4E16\u754C";
+    private static final String TEST_I18N_KEY = "\u4F60\u597D, \u4E16\u754C";
+    private static final byte[] TEST_I18N_VALUE = TEST_I18N_KEY.getBytes(Charsets.UTF_8);
 
     private KeyStore mKeyStore = null;
 
@@ -51,8 +53,10 @@
     @Override
     protected void setUp() throws Exception {
         mKeyStore = KeyStore.getInstance();
-        if (mKeyStore.test() != KeyStore.UNINITIALIZED) mKeyStore.reset();
-        assertEquals(KeyStore.UNINITIALIZED, mKeyStore.test());
+        if (mKeyStore.state() != KeyStore.State.UNINITIALIZED) {
+            mKeyStore.reset();
+        }
+        assertEquals(KeyStore.State.UNINITIALIZED, mKeyStore.state());
         super.setUp();
     }
 
@@ -62,21 +66,13 @@
         super.tearDown();
     }
 
-    public void testTest() throws Exception {
-        assertEquals(KeyStore.UNINITIALIZED, mKeyStore.test());
+    public void teststate() throws Exception {
+        assertEquals(KeyStore.State.UNINITIALIZED, mKeyStore.state());
     }
 
     public void testPassword() throws Exception {
-        //assertFalse(mKeyStore.password(TEST_EMPTY_PASSWD));
-        //assertFalse(mKeyStore.password(TEST_SHORT_PASSWD));
-
         assertTrue(mKeyStore.password(TEST_PASSWD));
-        assertEquals(KeyStore.NO_ERROR, mKeyStore.test());
-
-        assertFalse(mKeyStore.password(TEST_PASSWD2, TEST_PASSWD2));
-        //assertFalse(mKeyStore.password(TEST_PASSWD, TEST_SHORT_PASSWD));
-
-        assertTrue(mKeyStore.password(TEST_PASSWD, TEST_PASSWD2));
+        assertEquals(KeyStore.State.UNLOCKED, mKeyStore.state());
     }
 
     public void testPut() throws Exception {
@@ -87,11 +83,11 @@
     }
 
     public void testI18n() throws Exception {
-        assertFalse(mKeyStore.put(TEST_I18N, TEST_I18N));
-        assertFalse(mKeyStore.contains(TEST_I18N));
-        mKeyStore.password(TEST_I18N);
-        assertTrue(mKeyStore.put(TEST_I18N, TEST_I18N));
-        assertTrue(mKeyStore.contains(TEST_I18N));
+        assertFalse(mKeyStore.put(TEST_I18N_KEY, TEST_I18N_VALUE));
+        assertFalse(mKeyStore.contains(TEST_I18N_KEY));
+        mKeyStore.password(TEST_I18N_KEY);
+        assertTrue(mKeyStore.put(TEST_I18N_KEY, TEST_I18N_VALUE));
+        assertTrue(mKeyStore.contains(TEST_I18N_KEY));
     }
 
     public void testDelete() throws Exception {
@@ -114,33 +110,46 @@
     }
 
     public void testSaw() throws Exception {
-        String[] results = mKeyStore.saw(TEST_KEYNAME);
-        assertEquals(0, results.length);
+        String[] emptyResult = mKeyStore.saw(TEST_KEYNAME);
+        assertNotNull(emptyResult);
+        assertEquals(0, emptyResult.length);
 
         mKeyStore.password(TEST_PASSWD);
         mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE);
         mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE);
 
-        results = mKeyStore.saw(TEST_KEYNAME);
-        assertEquals(2, results.length);
+        String[] results = mKeyStore.saw(TEST_KEYNAME);
+        assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
+                                               TEST_KEYNAME2.substring(TEST_KEYNAME.length()))),
+                     new HashSet(Arrays.asList(results)));
     }
 
     public void testLock() throws Exception {
         assertFalse(mKeyStore.lock());
 
         mKeyStore.password(TEST_PASSWD);
-        assertEquals(KeyStore.NO_ERROR, mKeyStore.test());
+        assertEquals(KeyStore.State.UNLOCKED, mKeyStore.state());
 
         assertTrue(mKeyStore.lock());
-        assertEquals(KeyStore.LOCKED, mKeyStore.test());
+        assertEquals(KeyStore.State.LOCKED, mKeyStore.state());
     }
 
     public void testUnlock() throws Exception {
         mKeyStore.password(TEST_PASSWD);
-        assertEquals(KeyStore.NO_ERROR, mKeyStore.test());
+        assertEquals(KeyStore.State.UNLOCKED, mKeyStore.state());
         mKeyStore.lock();
 
         assertFalse(mKeyStore.unlock(TEST_PASSWD2));
         assertTrue(mKeyStore.unlock(TEST_PASSWD));
     }
+
+    public void testIsEmpty() throws Exception {
+        assertTrue(mKeyStore.isEmpty());
+        mKeyStore.password(TEST_PASSWD);
+        assertTrue(mKeyStore.isEmpty());
+        mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE);
+        assertFalse(mKeyStore.isEmpty());
+        mKeyStore.reset();
+        assertTrue(mKeyStore.isEmpty());
+    }
 }
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index f8582d8..afab26a 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -1151,6 +1151,7 @@
 
 void DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
         float x, float y, SkPaint* paint) {
+    if (count <= 0) return;
     addOp(DisplayList::DrawText);
     addText(text, bytesCount);
     addInt(count);
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 1ca0a19..9bf3de8 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -35,6 +35,7 @@
 #define DEFAULT_TEXT_CACHE_WIDTH 1024
 #define DEFAULT_TEXT_CACHE_HEIGHT 256
 
+// We should query these values from the GL context
 #define MAX_TEXT_CACHE_WIDTH 2048
 #define MAX_TEXT_CACHE_HEIGHT 2048
 
@@ -58,8 +59,7 @@
     }
 
     for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
-        CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
-        delete glyph;
+        delete mCachedGlyphs.valueAt(i);
     }
 }
 
@@ -134,48 +134,49 @@
 
 }
 
-Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
+Font::CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
     CachedGlyphInfo* cachedGlyph = NULL;
-    ssize_t index = mCachedGlyphs.indexOfKey(utfChar);
+    ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
     if (index >= 0) {
         cachedGlyph = mCachedGlyphs.valueAt(index);
     } else {
-        cachedGlyph = cacheGlyph(paint, utfChar);
+        cachedGlyph = cacheGlyph(paint, textUnit);
     }
 
     // Is the glyph still in texture cache?
     if (!cachedGlyph->mIsValid) {
-        const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
+        const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
         updateGlyphCache(paint, skiaGlyph, cachedGlyph);
     }
 
     return cachedGlyph;
 }
 
-void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
         int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
     if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
-        renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
+        render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
                 bitmapW, bitmapH, NULL);
     } else {
-        renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
+        render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
+                0, 0, NULL);
     }
 
 }
 
-void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
         int numGlyphs, Rect *bounds) {
     if (bounds == NULL) {
         LOGE("No return rectangle provided to measure text");
         return;
     }
     bounds->set(1e6, -1e6, -1e6, 1e6);
-    renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
+    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
 }
 
 #define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
 
-void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
         int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
         uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
     if (numGlyphs == 0 || text == NULL || len == 0) {
@@ -195,14 +196,14 @@
     text += start;
 
     while (glyphsLeft > 0) {
-        int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
+        glyph_t glyph = GET_GLYPH(text);
 
         // Reached the end of the string
-        if (utfChar < 0) {
+        if (IS_END_OF_STRING(glyph)) {
             break;
         }
 
-        CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
+        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
         penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta);
         prevRsbDelta = cachedGlyph->mRsbDelta;
 
@@ -268,11 +269,11 @@
     mState->mUploadTexture = true;
 }
 
-Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
+Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
     CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
     mCachedGlyphs.add(glyph, newGlyph);
 
-    const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
+    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
     newGlyph->mGlyphIndex = skiaGlyph.fID;
     newGlyph->mIsValid = false;
 
@@ -672,7 +673,7 @@
     uint32_t remainingCapacity = getRemainingCacheCapacity();
     uint32_t precacheIdx = 0;
     while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
-        mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
+        mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]);
         remainingCapacity = getRemainingCacheCapacity();
         precacheIdx ++;
     }
@@ -714,7 +715,7 @@
     }
 
     Rect bounds;
-    mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
+    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
     uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
     uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
     uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
@@ -725,7 +726,7 @@
     int penX = radius - bounds.left;
     int penY = radius - bounds.bottom;
 
-    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
+    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
             dataBuffer, paddedWidth, paddedHeight);
     blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
 
@@ -755,7 +756,7 @@
     mDrawn = false;
     mBounds = bounds;
     mClip = clip;
-    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
+    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
     mBounds = NULL;
 
     if (mCurrentQuadIndex != 0) {
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 95f714f..24ed6fa 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -33,8 +33,32 @@
 namespace android {
 namespace uirenderer {
 
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#if RENDER_TEXT_AS_GLYPHS
+    typedef uint16_t glyph_t;
+    #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
+    #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
+    #define IS_END_OF_STRING(glyph) false
+#else
+    typedef SkUnichar glyph_t;
+    #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
+    #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
+    #define IS_END_OF_STRING(glyph) glyph < 0
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Declarations
+///////////////////////////////////////////////////////////////////////////////
+
 class FontRenderer;
 
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
 /**
  * Represents a font, defined by a Skia font id and a font size. A font is used
  * to generate glyphs and cache them in the FontState.
@@ -51,9 +75,9 @@
      * Renders the specified string of text.
      * If bitmap is specified, it will be used as the render target
      */
-    void renderUTF(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
-                     int numGlyphs, int x, int y,
-                     uint8_t *bitmap = NULL, uint32_t bitmapW = 0, uint32_t bitmapH = 0);
+    void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+            int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
+            uint32_t bitmapW = 0, uint32_t bitmapH = 0);
     /**
      * Creates a new font associated with the specified font state.
      */
@@ -69,13 +93,12 @@
         MEASURE,
     };
 
-    void renderUTF(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
-                     int numGlyphs, int x, int y, RenderMode mode,
-                     uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
-                     Rect *bounds);
+    void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+            int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+            uint32_t bitmapW, uint32_t bitmapH, Rect *bounds);
 
-    void measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
-                      int numGlyphs, Rect *bounds);
+    void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+            int numGlyphs, Rect *bounds);
 
     struct CachedGlyphInfo {
         // Has the cache been invalidated?
@@ -107,18 +130,26 @@
     Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
             uint32_t scaleX);
 
-    DefaultKeyedVector<int32_t, CachedGlyphInfo*> mCachedGlyphs;
+    // Cache of glyphs
+    DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
 
     void invalidateTextureCache();
 
-    CachedGlyphInfo* cacheGlyph(SkPaint* paint, int32_t glyph);
+    CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph);
     void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph);
     void measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds);
     void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y);
     void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
-                          uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH);
+            uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH);
 
-    CachedGlyphInfo* getCachedUTFChar(SkPaint* paint, int32_t utfChar);
+    CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit);
+
+    static glyph_t nextGlyph(const uint16_t** srcPtr) {
+        const uint16_t* src = *srcPtr;
+        glyph_t g = *src++;
+        *srcPtr = src;
+        return g;
+    }
 
     FontRenderer* mState;
     uint32_t mFontId;
@@ -128,6 +159,10 @@
     uint32_t mScaleX;
 };
 
+///////////////////////////////////////////////////////////////////////////////
+// Renderer
+///////////////////////////////////////////////////////////////////////////////
+
 class FontRenderer {
 public:
     FontRenderer();
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 6243b01..45f4a42 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2073,11 +2073,6 @@
     drawTextDecorations(text, bytesCount, length, oldX, oldY, paint);
 }
 
-void OpenGLRenderer::drawGlyphs(const char* glyphs, int index, int count, float x, float y,
-        SkPaint* paint) {
-    // TODO
-}
-
 void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
     if (mSnapshot->isIgnored()) return;
 
@@ -2230,14 +2225,19 @@
     // Handle underline and strike-through
     uint32_t flags = paint->getFlags();
     if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+        SkPaint paintCopy(*paint);
+#if RENDER_TEXT_AS_GLYPHS
+        paintCopy.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+#endif
+
         float underlineWidth = length;
         // If length is > 0.0f, we already measured the text for the text alignment
         if (length <= 0.0f) {
-            underlineWidth = paint->measureText(text, bytesCount);
+            underlineWidth = paintCopy.measureText(text, bytesCount);
         }
 
         float offsetX = 0;
-        switch (paint->getTextAlign()) {
+        switch (paintCopy.getTextAlign()) {
             case SkPaint::kCenter_Align:
                 offsetX = underlineWidth * 0.5f;
                 break;
@@ -2249,8 +2249,7 @@
         }
 
         if (underlineWidth > 0.0f) {
-            const float textSize = paint->getTextSize();
-            // TODO: Support stroke width < 1.0f when we have AA lines
+            const float textSize = paintCopy.getTextSize();
             const float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
 
             const float left = x - offsetX;
@@ -2280,10 +2279,9 @@
                 points[currentPoint++] = top;
             }
 
-            SkPaint linesPaint(*paint);
-            linesPaint.setStrokeWidth(strokeWidth);
+            paintCopy.setStrokeWidth(strokeWidth);
 
-            drawLines(&points[0], pointsCount, &linesPaint);
+            drawLines(&points[0], pointsCount, &paintCopy);
         }
     }
 }
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index e2dbba0..549d6e9 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -123,8 +123,6 @@
     virtual void drawPoints(float* points, int count, SkPaint* paint);
     virtual void drawText(const char* text, int bytesCount, int count, float x, float y,
             SkPaint* paint);
-    virtual void drawGlyphs(const char* glyphs, int index, int count, float x, float y,
-            SkPaint* paint);
 
     virtual void resetShader();
     virtual void setupShader(SkiaShader* shader);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 2d8b6f3..7c10518 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -28,6 +28,9 @@
 // If turned on, layers drawn inside FBOs are optimized with regions
 #define RENDER_LAYERS_AS_REGIONS 1
 
+// If turned on, text is interpreted as glyphs instead of UTF-16
+#define RENDER_TEXT_AS_GLYPHS 1
+
 /**
  * Debug level for app developers.
  */
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index d46686d..28dba13 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -73,7 +73,6 @@
         text = str.string();
     }
 
-    // TODO: Should take into account fake bold and text skew
     bool operator<(const ShadowText& rhs) const {
         LTE_INT(len) {
             LTE_INT(radius) {
diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp
index e15875f..f933199f 100644
--- a/libs/utils/BackupHelpers.cpp
+++ b/libs/utils/BackupHelpers.cpp
@@ -503,6 +503,16 @@
         needExtended = true;
     }
 
+    // Non-7bit-clean path also means needing pax extended format
+    if (!needExtended) {
+        for (size_t i = 0; i < filepath.length(); i++) {
+            if ((filepath[i] & 0x80) != 0) {
+                needExtended = true;
+                break;
+            }
+        }
+    }
+
     int err = 0;
     struct stat64 s;
     if (lstat64(filepath.string(), &s) != 0) {
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 446e3df..f6c4cc7 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -88,7 +88,7 @@
         int inputSource,
         uint32_t sampleRate,
         int format,
-        uint32_t channels,
+        uint32_t channelMask,
         int frameCount,
         uint32_t flags,
         callback_t cbf,
@@ -97,7 +97,7 @@
         int sessionId)
     : mStatus(NO_INIT), mSessionId(0)
 {
-    mStatus = set(inputSource, sampleRate, format, channels,
+    mStatus = set(inputSource, sampleRate, format, channelMask,
             frameCount, flags, cbf, user, notificationFrames, sessionId);
 }
 
@@ -121,7 +121,7 @@
         int inputSource,
         uint32_t sampleRate,
         int format,
-        uint32_t channels,
+        uint32_t channelMask,
         int frameCount,
         uint32_t flags,
         callback_t cbf,
@@ -131,7 +131,7 @@
         int sessionId)
 {
 
-    LOGV("set(): sampleRate %d, channels %d, frameCount %d",sampleRate, channels, frameCount);
+    LOGV("set(): sampleRate %d, channelMask %d, frameCount %d",sampleRate, channelMask, frameCount);
 
     AutoMutex lock(mLock);
 
@@ -156,14 +156,14 @@
         return BAD_VALUE;
     }
 
-    if (!audio_is_input_channel(channels)) {
+    if (!audio_is_input_channel(channelMask)) {
         return BAD_VALUE;
     }
 
-    int channelCount = popcount(channels);
+    int channelCount = popcount(channelMask);
 
     audio_io_handle_t input = AudioSystem::getInput(inputSource,
-                                    sampleRate, format, channels, (audio_in_acoustics_t)flags);
+                                    sampleRate, format, channelMask, (audio_in_acoustics_t)flags);
     if (input == 0) {
         LOGE("Could not get audio input for record source %d", inputSource);
         return BAD_VALUE;
@@ -190,7 +190,7 @@
     mSessionId = sessionId;
 
     // create the IAudioRecord
-    status = openRecord_l(sampleRate, format, channelCount,
+    status = openRecord_l(sampleRate, format, channelMask,
                         frameCount, flags, input);
     if (status != NO_ERROR) {
         return status;
@@ -209,7 +209,7 @@
     // Update buffer size in case it has been limited by AudioFlinger during track creation
     mFrameCount = mCblk->frameCount;
     mChannelCount = (uint8_t)channelCount;
-    mChannels = channels;
+    mChannelMask = channelMask;
     mActive = 0;
     mCbf = cbf;
     mNotificationFrames = notificationFrames;
@@ -437,8 +437,8 @@
 // must be called with mLock held
 status_t AudioRecord::openRecord_l(
         uint32_t sampleRate,
-        int format,
-        int channelCount,
+        uint32_t format,
+        uint32_t channelMask,
         int frameCount,
         uint32_t flags,
         audio_io_handle_t input)
@@ -451,7 +451,7 @@
 
     sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), input,
                                                        sampleRate, format,
-                                                       channelCount,
+                                                       channelMask,
                                                        frameCount,
                                                        ((uint16_t)flags) << 16,
                                                        &mSessionId,
@@ -589,7 +589,7 @@
 {
     mInput = AudioSystem::getInput(mInputSource,
                                 mCblk->sampleRate,
-                                mFormat, mChannels,
+                                mFormat, mChannelMask,
                                 (audio_in_acoustics_t)mFlags);
     return mInput;
 }
@@ -756,7 +756,7 @@
         // if the new IAudioRecord is created, openRecord_l() will modify the
         // following member variables: mAudioRecord, mCblkMemory and mCblk.
         // It will also delete the strong references on previous IAudioRecord and IMemory
-        result = openRecord_l(cblk->sampleRate, mFormat, mChannelCount,
+        result = openRecord_l(cblk->sampleRate, mFormat, mChannelMask,
                 mFrameCount, mFlags, getInput_l());
         if (result == NO_ERROR) {
             result = mAudioRecord->start();
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 7520ed9..ea44f87 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -87,7 +87,7 @@
         int streamType,
         uint32_t sampleRate,
         int format,
-        int channels,
+        int channelMask,
         int frameCount,
         uint32_t flags,
         callback_t cbf,
@@ -96,7 +96,7 @@
         int sessionId)
     : mStatus(NO_INIT)
 {
-    mStatus = set(streamType, sampleRate, format, channels,
+    mStatus = set(streamType, sampleRate, format, channelMask,
             frameCount, flags, cbf, user, notificationFrames,
             0, false, sessionId);
 }
@@ -105,7 +105,7 @@
         int streamType,
         uint32_t sampleRate,
         int format,
-        int channels,
+        int channelMask,
         const sp<IMemory>& sharedBuffer,
         uint32_t flags,
         callback_t cbf,
@@ -114,7 +114,7 @@
         int sessionId)
     : mStatus(NO_INIT)
 {
-    mStatus = set(streamType, sampleRate, format, channels,
+    mStatus = set(streamType, sampleRate, format, channelMask,
             0, flags, cbf, user, notificationFrames,
             sharedBuffer, false, sessionId);
 }
@@ -141,7 +141,7 @@
         int streamType,
         uint32_t sampleRate,
         int format,
-        int channels,
+        int channelMask,
         int frameCount,
         uint32_t flags,
         callback_t cbf,
@@ -180,8 +180,8 @@
     if (format == 0) {
         format = AUDIO_FORMAT_PCM_16_BIT;
     }
-    if (channels == 0) {
-        channels = AUDIO_CHANNEL_OUT_STEREO;
+    if (channelMask == 0) {
+        channelMask = AUDIO_CHANNEL_OUT_STEREO;
     }
 
     // validate parameters
@@ -195,15 +195,15 @@
         flags |= AUDIO_POLICY_OUTPUT_FLAG_DIRECT;
     }
 
-    if (!audio_is_output_channel(channels)) {
+    if (!audio_is_output_channel(channelMask)) {
         LOGE("Invalid channel mask");
         return BAD_VALUE;
     }
-    uint32_t channelCount = popcount(channels);
+    uint32_t channelCount = popcount(channelMask);
 
     audio_io_handle_t output = AudioSystem::getOutput(
                                     (audio_stream_type_t)streamType,
-                                    sampleRate,format, channels,
+                                    sampleRate,format, channelMask,
                                     (audio_policy_output_flags_t)flags);
 
     if (output == 0) {
@@ -222,8 +222,8 @@
     // create the IAudioTrack
     status_t status = createTrack_l(streamType,
                                   sampleRate,
-                                  format,
-                                  channelCount,
+                                  (uint32_t)format,
+                                  (uint32_t)channelMask,
                                   frameCount,
                                   flags,
                                   sharedBuffer,
@@ -245,8 +245,8 @@
     mStatus = NO_ERROR;
 
     mStreamType = streamType;
-    mFormat = format;
-    mChannels = channels;
+    mFormat = (uint32_t)format;
+    mChannelMask = (uint32_t)channelMask;
     mChannelCount = channelCount;
     mSharedBuffer = sharedBuffer;
     mMuted = false;
@@ -681,7 +681,7 @@
 audio_io_handle_t AudioTrack::getOutput_l()
 {
     return AudioSystem::getOutput((audio_stream_type_t)mStreamType,
-            mCblk->sampleRate, mFormat, mChannels, (audio_policy_output_flags_t)mFlags);
+            mCblk->sampleRate, mFormat, mChannelMask, (audio_policy_output_flags_t)mFlags);
 }
 
 int AudioTrack::getSessionId()
@@ -705,8 +705,8 @@
 status_t AudioTrack::createTrack_l(
         int streamType,
         uint32_t sampleRate,
-        int format,
-        int channelCount,
+        uint32_t format,
+        uint32_t channelMask,
         int frameCount,
         uint32_t flags,
         const sp<IMemory>& sharedBuffer,
@@ -767,6 +767,7 @@
             }
         } else {
             // Ensure that buffer alignment matches channelcount
+            int channelCount = popcount(channelMask);
             if (((uint32_t)sharedBuffer->pointer() & (channelCount | 1)) != 0) {
                 LOGE("Invalid buffer alignement: address %p, channelCount %d", sharedBuffer->pointer(), channelCount);
                 return BAD_VALUE;
@@ -779,7 +780,7 @@
                                                       streamType,
                                                       sampleRate,
                                                       format,
-                                                      channelCount,
+                                                      channelMask,
                                                       frameCount,
                                                       ((uint16_t)flags) << 16,
                                                       sharedBuffer,
@@ -1164,7 +1165,7 @@
         result = createTrack_l(mStreamType,
                                cblk->sampleRate,
                                mFormat,
-                               mChannelCount,
+                               mChannelMask,
                                mFrameCount,
                                mFlags,
                                mSharedBuffer,
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 158d2f5..4a12962 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -82,8 +82,8 @@
                                 pid_t pid,
                                 int streamType,
                                 uint32_t sampleRate,
-                                int format,
-                                int channelCount,
+                                uint32_t format,
+                                uint32_t channelMask,
                                 int frameCount,
                                 uint32_t flags,
                                 const sp<IMemory>& sharedBuffer,
@@ -98,7 +98,7 @@
         data.writeInt32(streamType);
         data.writeInt32(sampleRate);
         data.writeInt32(format);
-        data.writeInt32(channelCount);
+        data.writeInt32(channelMask);
         data.writeInt32(frameCount);
         data.writeInt32(flags);
         data.writeStrongBinder(sharedBuffer->asBinder());
@@ -129,8 +129,8 @@
                                 pid_t pid,
                                 int input,
                                 uint32_t sampleRate,
-                                int format,
-                                int channelCount,
+                                uint32_t format,
+                                uint32_t channelMask,
                                 int frameCount,
                                 uint32_t flags,
                                 int *sessionId,
@@ -143,7 +143,7 @@
         data.writeInt32(input);
         data.writeInt32(sampleRate);
         data.writeInt32(format);
-        data.writeInt32(channelCount);
+        data.writeInt32(channelMask);
         data.writeInt32(frameCount);
         data.writeInt32(flags);
         int lSessionId = 0;
@@ -186,7 +186,7 @@
         return reply.readInt32();
     }
 
-    virtual int format(int output) const
+    virtual uint32_t format(int output) const
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index d51c946..eae93ff 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1164,7 +1164,7 @@
     mem = new MemoryBase(cache->getHeap(), 0, cache->size());
     *pSampleRate = cache->sampleRate();
     *pNumChannels = cache->channelCount();
-    *pFormat = cache->format();
+    *pFormat = (int)cache->format();
     LOGV("return memory @ %p, sampleRate=%u, channelCount = %d, format = %d", mem->pointer(), *pSampleRate, *pNumChannels, *pFormat);
 
 Exit:
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java
index 4c66a2d..7eb6d22 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java
@@ -112,12 +112,11 @@
 
     private final int NUM_OF_ITERATIONS=20;
 
-    private float calculateTimeTaken(long beginTime, int numIterations)
+    private int calculateTimeTaken(long beginTime, int numIterations)
         throws Exception {
         final long duration2 = SystemClock.uptimeMillis();
         final long durationToCreateMediaItem = (duration2 - beginTime);
-        final float timeTaken1 = (float)durationToCreateMediaItem *
-            1.0f/(float)numIterations;
+        final int timeTaken1 = (int)(durationToCreateMediaItem / numIterations);
         return (timeTaken1);
     }
 
@@ -208,7 +207,7 @@
         final String[] loggingInfo = new String[3];
         final MediaVideoItem[] mediaVideoItem =
             new MediaVideoItem[NUM_OF_ITERATIONS];
-        float timeTaken = 0.0f;
+        int timeTaken = 0;
         long startTime = 0;
 
         /** Time Take for creation of Media Video Item */
@@ -251,7 +250,7 @@
         final String[] loggingInfo = new String[3];
         final MediaImageItem[] mediaImageItem =
             new MediaImageItem[NUM_OF_ITERATIONS];
-        float timeTaken = 0.0f;
+        int timeTaken = 0;
 
         long beginTime = SystemClock.uptimeMillis();
         createImageItems(mediaImageItem, imageItemFileName, renderingMode,
@@ -296,7 +295,7 @@
         final int transitionDuration = 5000;
         final int transitionBehavior = Transition.BEHAVIOR_MIDDLE_FAST;
         final String[] loggingInfo = new String[3];
-        float timeTaken = 0.0f;
+        int timeTaken = 0;
 
         final MediaVideoItem[] mediaVideoItem =
             new MediaVideoItem[(NUM_OF_ITERATIONS *10) + 1];
@@ -514,7 +513,7 @@
         /** 18.Enable Looping for Audio Track.
          * */
         audioTrack.enableLoop();
-        float timeTaken = 0.0f;
+        int timeTaken = 0;
         final long beginTime = SystemClock.uptimeMillis();
             try {
                 mVideoEditor.export(outFilename, outHeight, outBitrate,
@@ -557,7 +556,7 @@
         mediaVideoItem.setExtractBoundaries(videoItemStartTime,
             videoItemEndTime);
 
-        float timeTaken = 0.0f;
+        int timeTaken = 0;
         long beginTime = SystemClock.uptimeMillis();
         for (int i = 0; i < NUM_OF_ITERATIONS; i++) {
             mediaVideoItem.getThumbnail(mediaVideoItem.getWidth() / 2,
@@ -603,7 +602,7 @@
         final OverlayFrame overlayFrame[] = new OverlayFrame[NUM_OF_ITERATIONS];
         final Bitmap mBitmap =  mVideoEditorHelper.getBitmap(overlayFilename,
             640, 480);
-        float timeTaken = 0.0f;
+        int timeTaken = 0;
         long beginTime = SystemClock.uptimeMillis();
         for (int i = 0; i < NUM_OF_ITERATIONS; i++) {
             overlayFrame[i] = new OverlayFrame(mediaVideoItem, "overlay" + i,
@@ -647,7 +646,7 @@
         final int videoProfile = MediaProperties.H264_PROFILE_0_LEVEL_1_3;
         final int width = 1080;
         final int height = MediaProperties.HEIGHT_720;
-        float timeTaken = 0.0f;
+        int timeTaken = 0;
         final String[] loggingInfo = new String[1];
         final MediaVideoItem mediaVideoItem = new MediaVideoItem(mVideoEditor,
             "m0", videoItemFileName1, renderingMode);
@@ -1006,7 +1005,7 @@
         final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
         final int audioVolume = 50;
         final String[] loggingInfo = new String[2];
-        float timeTaken = 0.0f;
+        int timeTaken = 0;
 
         final MediaVideoItem mediaVideoItem = new MediaVideoItem(mVideoEditor,
             "mediaItem1", videoItemFileName1, renderingMode);
@@ -1057,7 +1056,7 @@
         final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
         final String[] loggingInfo = new String[3];
 
-        float timeTaken = 0.0f;
+        int timeTaken = 0;
 
         final MediaImageItem[] mediaImageItem =
             new MediaImageItem[NUM_OF_ITERATIONS];
diff --git a/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml b/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml
index 109cfff..a4564e6 100644
--- a/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml
+++ b/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml
@@ -37,6 +37,7 @@
               android:layout_marginBottom="30dp" />
 
     <Button android:id="@+id/button_allow"
+            android:filterTouchesWhenObscured="true"
             android:text="@string/allow_backup_button_label"
             android:layout_below="@id/package_name"
             android:layout_height="wrap_content"
diff --git a/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml b/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml
index a1f9a4a..ca99ae1 100644
--- a/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml
+++ b/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml
@@ -37,6 +37,7 @@
               android:layout_marginBottom="30dp" />
 
     <Button android:id="@+id/button_allow"
+            android:filterTouchesWhenObscured="true"
             android:text="@string/allow_restore_button_label"
             android:layout_below="@id/package_name"
             android:layout_height="wrap_content"
diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
index 4b42067..52bfc28 100644
--- a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
+++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
@@ -98,6 +98,8 @@
                 break;
 
                 case MSG_RESTORE_PACKAGE: {
+                    String name = (String) msg.obj;
+                    mStatusView.setText(name);
                 }
                 break;
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 45bb2b6..0c4ef7d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -55,6 +55,7 @@
  */
 public class SettingsBackupAgent extends BackupAgentHelper {
     private static final boolean DEBUG = false;
+    private static final boolean DEBUG_BACKUP = DEBUG || true;
 
     private static final String KEY_SYSTEM = "system";
     private static final String KEY_SECURE = "secure";
@@ -75,6 +76,9 @@
     private static final int STATE_WIFI_CONFIG     = 4;
     private static final int STATE_SIZE            = 5; // The number of state items
 
+    // Versioning of the 'full backup' format
+    private static final int FULL_BACKUP_VERSION = 1;
+
     private static String[] sortedSystemKeys = null;
     private static String[] sortedSecureKeys = null;
 
@@ -109,6 +113,8 @@
     private static String mWifiConfigFile;
 
     public void onCreate() {
+        if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked");
+
         mSettingsHelper = new SettingsHelper(this);
         super.onCreate();
 
@@ -151,26 +157,32 @@
             // representation of the backed-up settings.
             String root = getFilesDir().getAbsolutePath();
             File stage = new File(root, STAGE_FILE);
-            FileOutputStream filestream = new FileOutputStream(stage);
-            BufferedOutputStream bufstream = new BufferedOutputStream(filestream);
-            DataOutputStream out = new DataOutputStream(bufstream);
+            try {
+                FileOutputStream filestream = new FileOutputStream(stage);
+                BufferedOutputStream bufstream = new BufferedOutputStream(filestream);
+                DataOutputStream out = new DataOutputStream(bufstream);
 
-            out.writeInt(systemSettingsData.length);
-            out.write(systemSettingsData);
-            out.writeInt(secureSettingsData.length);
-            out.write(secureSettingsData);
-            out.writeInt(locale.length);
-            out.write(locale);
-            out.writeInt(wifiSupplicantData.length);
-            out.write(wifiSupplicantData);
-            out.writeInt(wifiConfigData.length);
-            out.write(wifiConfigData);
+                out.writeInt(FULL_BACKUP_VERSION);
 
-            out.flush();    // also flushes downstream
+                out.writeInt(systemSettingsData.length);
+                out.write(systemSettingsData);
+                out.writeInt(secureSettingsData.length);
+                out.write(secureSettingsData);
+                out.writeInt(locale.length);
+                out.write(locale);
+                out.writeInt(wifiSupplicantData.length);
+                out.write(wifiSupplicantData);
+                out.writeInt(wifiConfigData.length);
+                out.write(wifiConfigData);
 
-            // now we're set to emit the tar stream
-            FullBackup.backupToTar(getPackageName(), FullBackup.DATA_TREE_TOKEN, null,
-                    root, stage.getAbsolutePath(), data);
+                out.flush();    // also flushes downstream
+
+                // now we're set to emit the tar stream
+                FullBackup.backupToTar(getPackageName(), FullBackup.DATA_TREE_TOKEN, null,
+                        root, stage.getAbsolutePath(), data);
+            } finally {
+                stage.delete();
+            }
         }
     }
 
@@ -199,7 +211,7 @@
             } else if (KEY_LOCALE.equals(key)) {
                 byte[] localeData = new byte[size];
                 data.readEntityData(localeData, 0, size);
-                mSettingsHelper.setLocaleData(localeData);
+                mSettingsHelper.setLocaleData(localeData, size);
             } else if (KEY_WIFI_CONFIG.equals(key)) {
                 restoreFileData(mWifiConfigFile, data);
              } else {
@@ -208,6 +220,70 @@
         }
     }
 
+    @Override
+    public void onRestoreFile(ParcelFileDescriptor data, long size,
+            int type, String domain, String relpath, long mode, long mtime)
+            throws IOException {
+        if (DEBUG_BACKUP) Log.d(TAG, "onRestoreFile() invoked");
+        // Our data is actually a blob of flattened settings data identical to that
+        // produced during incremental backups.  Just unpack and apply it all in
+        // turn.
+        FileInputStream instream = new FileInputStream(data.getFileDescriptor());
+        DataInputStream in = new DataInputStream(instream);
+
+        int version = in.readInt();
+        if (DEBUG_BACKUP) Log.d(TAG, "Flattened data version " + version);
+        if (version == FULL_BACKUP_VERSION) {
+            // system settings data first
+            int nBytes = in.readInt();
+            if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of settings data");
+            byte[] buffer = new byte[nBytes];
+            in.read(buffer, 0, nBytes);
+            restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI);
+
+            // secure settings
+            nBytes = in.readInt();
+            if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data");
+            if (nBytes > buffer.length) buffer = new byte[nBytes];
+            in.read(buffer, 0, nBytes);
+            restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI);
+
+            // locale
+            nBytes = in.readInt();
+            if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of locale data");
+            if (nBytes > buffer.length) buffer = new byte[nBytes];
+            in.read(buffer, 0, nBytes);
+            mSettingsHelper.setLocaleData(buffer, nBytes);
+
+            // wifi supplicant
+            nBytes = in.readInt();
+            if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of wifi supplicant data");
+            if (nBytes > buffer.length) buffer = new byte[nBytes];
+            in.read(buffer, 0, nBytes);
+            int retainedWifiState = enableWifi(false);
+            restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, buffer, nBytes);
+            FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
+                    FileUtils.S_IRUSR | FileUtils.S_IWUSR |
+                    FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+                    Process.myUid(), Process.WIFI_UID);
+            // retain the previous WIFI state.
+            enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
+                    retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
+
+            // wifi config
+            nBytes = in.readInt();
+            if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of wifi config data");
+            if (nBytes > buffer.length) buffer = new byte[nBytes];
+            in.read(buffer, 0, nBytes);
+            restoreFileData(mWifiConfigFile, buffer, nBytes);
+
+            if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
+        } else {
+            data.close();
+            throw new IOException("Invalid file schema");
+        }
+    }
+
     private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException {
         long[] stateChecksums = new long[STATE_SIZE];
 
@@ -287,6 +363,17 @@
     }
 
     private void restoreSettings(BackupDataInput data, Uri contentUri) {
+        byte[] settings = new byte[data.getDataSize()];
+        try {
+            data.readEntityData(settings, 0, settings.length);
+        } catch (IOException ioe) {
+            Log.e(TAG, "Couldn't read entity data");
+            return;
+        }
+        restoreSettings(settings, settings.length, contentUri);
+    }
+
+    private void restoreSettings(byte[] settings, int bytes, Uri contentUri) {
         if (DEBUG) Log.i(TAG, "restoreSettings: " + contentUri);
         String[] whitelist = null;
         if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
@@ -296,15 +383,8 @@
         }
 
         ContentValues cv = new ContentValues(2);
-        byte[] settings = new byte[data.getDataSize()];
-        try {
-            data.readEntityData(settings, 0, settings.length);
-        } catch (IOException ioe) {
-            Log.e(TAG, "Couldn't read entity data");
-            return;
-        }
         int pos = 0;
-        while (pos < settings.length) {
+        while (pos < bytes) {
             int length = readInt(settings, pos);
             pos += 4;
             String settingName = length > 0? new String(settings, pos, length) : null;
@@ -451,13 +531,16 @@
     private void restoreFileData(String filename, BackupDataInput data) {
         byte[] bytes = new byte[data.getDataSize()];
         if (bytes.length <= 0) return;
+        restoreFileData(filename, bytes, bytes.length);
+    }
+
+    private void restoreFileData(String filename, byte[] bytes, int size) {
         try {
-            data.readEntityData(bytes, 0, bytes.length);
             File file = new File(filename);
             if (file.exists()) file.delete();
 
             OutputStream os = new BufferedOutputStream(new FileOutputStream(filename, true));
-            os.write(bytes);
+            os.write(bytes, 0, size);
             os.close();
         } catch (IOException ioe) {
             Log.w(TAG, "Couldn't restore " + filename);
@@ -506,15 +589,18 @@
     private void restoreWifiSupplicant(String filename, BackupDataInput data) {
         byte[] bytes = new byte[data.getDataSize()];
         if (bytes.length <= 0) return;
+        restoreWifiSupplicant(filename, bytes, bytes.length);
+    }
+
+    private void restoreWifiSupplicant(String filename, byte[] bytes, int size) {
         try {
-            data.readEntityData(bytes, 0, bytes.length);
             File supplicantFile = new File(FILE_WIFI_SUPPLICANT);
             if (supplicantFile.exists()) supplicantFile.delete();
             copyWifiSupplicantTemplate();
 
             OutputStream os = new BufferedOutputStream(new FileOutputStream(filename, true));
             os.write("\n".getBytes());
-            os.write(bytes);
+            os.write(bytes, 0, size);
             os.close();
         } catch (IOException ioe) {
             Log.w(TAG, "Couldn't restore " + filename);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 0e75fbc..3e7d86a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -147,7 +147,7 @@
      * "ll" is the language code and "cc" is the country code.
      * @param data the locale string in bytes.
      */
-    void setLocaleData(byte[] data) {
+    void setLocaleData(byte[] data, int size) {
         // Check if locale was set by the user:
         Configuration conf = mContext.getResources().getConfiguration();
         Locale loc = conf.locale;
@@ -157,9 +157,9 @@
         if (conf.userSetLocale) return; // Don't change if user set it in the SetupWizard
 
         final String[] availableLocales = mContext.getAssets().getLocales();
-        String localeCode = new String(data);
+        String localeCode = new String(data, 0, size);
         String language = new String(data, 0, 2);
-        String country = data.length > 4 ? new String(data, 3, 2) : "";
+        String country = size > 4 ? new String(data, 3, 2) : "";
         loc = null;
         for (int i = 0; i < availableLocales.length; i++) {
             if (availableLocales[i].equals(localeCode)) {
diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
index ca56c4f..57b6b5e 100644
--- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
@@ -27,6 +27,7 @@
 
 import android.os.CountDownTimer;
 import android.os.SystemClock;
+import android.security.KeyStore;
 import android.telephony.TelephonyManager;
 import android.text.InputType;
 import android.text.method.DigitsKeyListener;
@@ -231,6 +232,7 @@
             mCallback.keyguardDone(true);
             mCallback.reportSuccessfulUnlockAttempt();
             mStatusView.setInstructionText(null);
+            KeyStore.getInstance().password(entry);
         } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
             // to avoid accidental lockout, only count attempts that are long enough to be a
             // real password. This may require some tweaking.
diff --git a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
index 018fe0c..a685497 100644
--- a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
@@ -20,6 +20,7 @@
 import android.content.res.Configuration;
 import android.os.CountDownTimer;
 import android.os.SystemClock;
+import android.security.KeyStore;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -407,6 +408,7 @@
                 mStatusView.updateStatusLines(true);
                 mCallback.keyguardDone(true);
                 mCallback.reportSuccessfulUnlockAttempt();
+                KeyStore.getInstance().password(LockPatternUtils.patternToString(pattern));
             } else {
                 boolean reportFailedAttempt = false;
                 if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 053854f..f806624 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -360,8 +360,8 @@
         pid_t pid,
         int streamType,
         uint32_t sampleRate,
-        int format,
-        int channelCount,
+        uint32_t format,
+        uint32_t channelMask,
         int frameCount,
         uint32_t flags,
         const sp<IMemory>& sharedBuffer,
@@ -429,7 +429,7 @@
         LOGV("createTrack() lSessionId: %d", lSessionId);
 
         track = thread->createTrack_l(client, streamType, sampleRate, format,
-                channelCount, frameCount, sharedBuffer, lSessionId, &lStatus);
+                channelMask, frameCount, sharedBuffer, lSessionId, &lStatus);
 
         // move effect chain to this output thread if an effect on same session was waiting
         // for a track to be created
@@ -477,7 +477,7 @@
     return thread->channelCount();
 }
 
-int AudioFlinger::format(int output) const
+uint32_t AudioFlinger::format(int output) const
 {
     Mutex::Autolock _l(mLock);
     PlaybackThread *thread = checkPlaybackThread_l(output);
@@ -916,7 +916,7 @@
     return (int)mChannelCount;
 }
 
-int AudioFlinger::ThreadBase::format() const
+uint32_t AudioFlinger::ThreadBase::format() const
 {
     return mFormat;
 }
@@ -1002,6 +1002,8 @@
     result.append(buffer);
     snprintf(buffer, SIZE, "Channel Count: %d\n", mChannelCount);
     result.append(buffer);
+    snprintf(buffer, SIZE, "Channel Mask: 0x%08x\n", mChannelMask);
+    result.append(buffer);
     snprintf(buffer, SIZE, "Format: %d\n", mFormat);
     result.append(buffer);
     snprintf(buffer, SIZE, "Frame size: %d\n", mFrameSize);
@@ -1075,7 +1077,7 @@
 
     snprintf(buffer, SIZE, "Output thread %p tracks\n", this);
     result.append(buffer);
-    result.append("   Name  Clien Typ Fmt Chn Session Buf  S M F SRate LeftV RighV  Serv       User       Main buf   Aux Buf\n");
+    result.append("   Name  Clien Typ Fmt Chn mask   Session Buf  S M F SRate LeftV RighV  Serv       User       Main buf   Aux Buf\n");
     for (size_t i = 0; i < mTracks.size(); ++i) {
         sp<Track> track = mTracks[i];
         if (track != 0) {
@@ -1086,7 +1088,7 @@
 
     snprintf(buffer, SIZE, "Output thread %p active tracks\n", this);
     result.append(buffer);
-    result.append("   Name  Clien Typ Fmt Chn Session Buf  S M F SRate LeftV RighV  Serv       User       Main buf   Aux Buf\n");
+    result.append("   Name  Clien Typ Fmt Chn mask   Session Buf  S M F SRate LeftV RighV  Serv       User       Main buf   Aux Buf\n");
     for (size_t i = 0; i < mActiveTracks.size(); ++i) {
         wp<Track> wTrack = mActiveTracks[i];
         if (wTrack != 0) {
@@ -1172,8 +1174,8 @@
         const sp<AudioFlinger::Client>& client,
         int streamType,
         uint32_t sampleRate,
-        int format,
-        int channelCount,
+        uint32_t format,
+        uint32_t channelMask,
         int frameCount,
         const sp<IMemory>& sharedBuffer,
         int sessionId,
@@ -1183,11 +1185,14 @@
     status_t lStatus;
 
     if (mType == DIRECT) {
-        if (sampleRate != mSampleRate || format != mFormat || channelCount != (int)mChannelCount) {
-            LOGE("createTrack_l() Bad parameter:  sampleRate %d format %d, channelCount %d for output %p",
-                 sampleRate, format, channelCount, mOutput);
-            lStatus = BAD_VALUE;
-            goto Exit;
+        if ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM) {
+            if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) {
+                LOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelMask 0x%08x \""
+                        "for output %p with format %d",
+                        sampleRate, format, channelMask, mOutput, mFormat);
+                lStatus = BAD_VALUE;
+                goto Exit;
+            }
         }
     } else {
         // Resampler implementation limits input sampling rate to 2 x output sampling rate.
@@ -1224,7 +1229,7 @@
         }
 
         track = new Track(this, client, streamType, sampleRate, format,
-                channelCount, frameCount, sharedBuffer, sessionId);
+                channelMask, frameCount, sharedBuffer, sessionId);
         if (track->getCblk() == NULL || track->name() < 0) {
             lStatus = NO_MEMORY;
             goto Exit;
@@ -1373,7 +1378,7 @@
     switch (event) {
     case AudioSystem::OUTPUT_OPENED:
     case AudioSystem::OUTPUT_CONFIG_CHANGED:
-        desc.channels = mChannels;
+        desc.channels = mChannelMask;
         desc.samplingRate = mSampleRate;
         desc.format = mFormat;
         desc.frameCount = mFrameCount;
@@ -1393,8 +1398,8 @@
 void AudioFlinger::PlaybackThread::readOutputParameters()
 {
     mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common);
-    mChannels = mOutput->stream->common.get_channels(&mOutput->stream->common);
-    mChannelCount = (uint16_t)popcount(mChannels);
+    mChannelMask = mOutput->stream->common.get_channels(&mOutput->stream->common);
+    mChannelCount = (uint16_t)popcount(mChannelMask);
     mFormat = mOutput->stream->common.get_format(&mOutput->stream->common);
     mFrameSize = (uint16_t)audio_stream_frame_size(&mOutput->stream->common);
     mFrameCount = mOutput->stream->common.get_buffer_size(&mOutput->stream->common) / mFrameSize;
@@ -1804,7 +1809,7 @@
                 AudioMixer::FORMAT, (void *)track->format());
             mAudioMixer->setParameter(
                 AudioMixer::TRACK,
-                AudioMixer::CHANNEL_COUNT, (void *)track->channelCount());
+                AudioMixer::CHANNEL_MASK, (void *)track->channelMask());
             mAudioMixer->setParameter(
                 AudioMixer::RESAMPLE,
                 AudioMixer::SAMPLE_RATE,
@@ -2683,7 +2688,7 @@
                                             this,
                                             mSampleRate,
                                             mFormat,
-                                            mChannelCount,
+                                            mChannelMask,
                                             frameCount);
     if (outputTrack->cblk() != NULL) {
         thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f);
@@ -2751,8 +2756,8 @@
             const wp<ThreadBase>& thread,
             const sp<Client>& client,
             uint32_t sampleRate,
-            int format,
-            int channelCount,
+            uint32_t format,
+            uint32_t channelMask,
             int frameCount,
             uint32_t flags,
             const sp<IMemory>& sharedBuffer,
@@ -2772,6 +2777,7 @@
 
     // LOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
    size_t size = sizeof(audio_track_cblk_t);
+   uint8_t channelCount = popcount(channelMask);
    size_t bufferSize = frameCount*channelCount*sizeof(int16_t);
    if (sharedBuffer == 0) {
        size += bufferSize;
@@ -2786,7 +2792,8 @@
                 // clear all buffers
                 mCblk->frameCount = frameCount;
                 mCblk->sampleRate = sampleRate;
-                mCblk->channelCount = (uint8_t)channelCount;
+                mChannelCount = channelCount;
+                mChannelMask = channelMask;
                 if (sharedBuffer == 0) {
                     mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
                     memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
@@ -2810,7 +2817,8 @@
            // clear all buffers
            mCblk->frameCount = frameCount;
            mCblk->sampleRate = sampleRate;
-           mCblk->channelCount = (uint8_t)channelCount;
+           mChannelCount = channelCount;
+           mChannelMask = channelMask;
            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
@@ -2877,7 +2885,11 @@
 }
 
 int AudioFlinger::ThreadBase::TrackBase::channelCount() const {
-    return (int)mCblk->channelCount;
+    return (const int)mChannelCount;
+}
+
+uint32_t AudioFlinger::ThreadBase::TrackBase::channelMask() const {
+    return mChannelMask;
 }
 
 void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
@@ -2889,9 +2901,9 @@
     if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd ||
         ((unsigned long)bufferStart & (unsigned long)(cblk->frameSize - 1))) {
         LOGE("TrackBase::getBuffer buffer out of range:\n    start: %p, end %p , mBuffer %p mBufferEnd %p\n    \
-                server %d, serverBase %d, user %d, userBase %d, channelCount %d",
+                server %d, serverBase %d, user %d, userBase %d",
                 bufferStart, bufferEnd, mBuffer, mBufferEnd,
-                cblk->server, cblk->serverBase, cblk->user, cblk->userBase, cblk->channelCount);
+                cblk->server, cblk->serverBase, cblk->user, cblk->userBase);
         return 0;
     }
 
@@ -2906,12 +2918,12 @@
             const sp<Client>& client,
             int streamType,
             uint32_t sampleRate,
-            int format,
-            int channelCount,
+            uint32_t format,
+            uint32_t channelMask,
             int frameCount,
             const sp<IMemory>& sharedBuffer,
             int sessionId)
-    :   TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer, sessionId),
+    :   TrackBase(thread, client, sampleRate, format, channelMask, frameCount, 0, sharedBuffer, sessionId),
     mMute(false), mSharedBuffer(sharedBuffer), mName(-1), mMainBuffer(NULL), mAuxBuffer(NULL),
     mAuxEffectId(0), mHasVolumeController(false)
 {
@@ -2931,7 +2943,7 @@
         mStreamType = streamType;
         // NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of
         // 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack
-        mCblk->frameSize = audio_is_linear_pcm(format) ? channelCount * sizeof(int16_t) : sizeof(int8_t);
+        mCblk->frameSize = audio_is_linear_pcm(format) ? mChannelCount * sizeof(int16_t) : sizeof(int8_t);
     }
 }
 
@@ -2979,12 +2991,12 @@
 
 void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
 {
-    snprintf(buffer, size, "   %05d %05d %03u %03u %03u %05u   %04u %1d %1d %1d %05u %05u %05u  0x%08x 0x%08x 0x%08x 0x%08x\n",
+    snprintf(buffer, size, "   %05d %05d %03u %03u 0x%08x %05u   %04u %1d %1d %1d %05u %05u %05u  0x%08x 0x%08x 0x%08x 0x%08x\n",
             mName - AudioMixer::TRACK0,
             (mClient == NULL) ? getpid() : mClient->pid(),
             mStreamType,
             mFormat,
-            mCblk->channelCount,
+            mChannelMask,
             mSessionId,
             mFrameCount,
             mState,
@@ -3219,21 +3231,21 @@
             const wp<ThreadBase>& thread,
             const sp<Client>& client,
             uint32_t sampleRate,
-            int format,
-            int channelCount,
+            uint32_t format,
+            uint32_t channelMask,
             int frameCount,
             uint32_t flags,
             int sessionId)
     :   TrackBase(thread, client, sampleRate, format,
-                  channelCount, frameCount, flags, 0, sessionId),
+                  channelMask, frameCount, flags, 0, sessionId),
         mOverflow(false)
 {
     if (mCblk != NULL) {
        LOGV("RecordTrack constructor, size %d", (int)mBufferEnd - (int)mBuffer);
        if (format == AUDIO_FORMAT_PCM_16_BIT) {
-           mCblk->frameSize = channelCount * sizeof(int16_t);
+           mCblk->frameSize = mChannelCount * sizeof(int16_t);
        } else if (format == AUDIO_FORMAT_PCM_8_BIT) {
-           mCblk->frameSize = channelCount * sizeof(int8_t);
+           mCblk->frameSize = mChannelCount * sizeof(int8_t);
        } else {
            mCblk->frameSize = sizeof(int8_t);
        }
@@ -3313,10 +3325,10 @@
 
 void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
 {
-    snprintf(buffer, size, "   %05d %03u %03u %05d   %04u %01d %05u  %08x %08x\n",
+    snprintf(buffer, size, "   %05d %03u 0x%08x %05d   %04u %01d %05u  %08x %08x\n",
             (mClient == NULL) ? getpid() : mClient->pid(),
             mFormat,
-            mCblk->channelCount,
+            mChannelMask,
             mSessionId,
             mFrameCount,
             mState,
@@ -3332,10 +3344,10 @@
             const wp<ThreadBase>& thread,
             DuplicatingThread *sourceThread,
             uint32_t sampleRate,
-            int format,
-            int channelCount,
+            uint32_t format,
+            uint32_t channelMask,
             int frameCount)
-    :   Track(thread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelCount, frameCount, NULL, 0),
+    :   Track(thread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount, NULL, 0),
     mActive(false), mSourceThread(sourceThread)
 {
 
@@ -3346,8 +3358,10 @@
         mCblk->volume[0] = mCblk->volume[1] = 0x1000;
         mOutBuffer.frameCount = 0;
         playbackThread->mTracks.add(this);
-        LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channelCount %d mBufferEnd %p",
-                mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channelCount, mBufferEnd);
+        LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, " \
+                "mCblk->frameCount %d, mCblk->sampleRate %d, mChannelMask 0x%08x mBufferEnd %p",
+                mCblk, mBuffer, mCblk->buffers,
+                mCblk->frameCount, mCblk->sampleRate, mChannelMask, mBufferEnd);
     } else {
         LOGW("Error creating output track on thread %p", playbackThread);
     }
@@ -3382,7 +3396,7 @@
 {
     Buffer *pInBuffer;
     Buffer inBuffer;
-    uint32_t channelCount = mCblk->channelCount;
+    uint32_t channelCount = mChannelCount;
     bool outputBufferFull = false;
     inBuffer.frameCount = frames;
     inBuffer.i16 = data;
@@ -3667,8 +3681,8 @@
         pid_t pid,
         int input,
         uint32_t sampleRate,
-        int format,
-        int channelCount,
+        uint32_t format,
+        uint32_t channelMask,
         int frameCount,
         uint32_t flags,
         int *sessionId,
@@ -3717,7 +3731,7 @@
         }
         // create new record track. The record track uses one track in mHardwareMixerThread by convention.
         recordTrack = new RecordThread::RecordTrack(thread, client, sampleRate,
-                                                   format, channelCount, frameCount, flags, lSessionId);
+                                                   format, channelMask, frameCount, flags, lSessionId);
     }
     if (recordTrack->getCblk() == NULL) {
         // remove local strong reference to Client before deleting the RecordTrack so that the Client
@@ -4065,7 +4079,7 @@
 
     if (mActiveTrack != 0) {
         result.append("Active Track:\n");
-        result.append("   Clien Fmt Chn Session Buf  S SRate  Serv     User\n");
+        result.append("   Clien Fmt Chn mask   Session Buf  S SRate  Serv     User\n");
         mActiveTrack->dump(buffer, SIZE);
         result.append(buffer);
 
@@ -4219,7 +4233,7 @@
     switch (event) {
     case AudioSystem::INPUT_OPENED:
     case AudioSystem::INPUT_CONFIG_CHANGED:
-        desc.channels = mChannels;
+        desc.channels = mChannelMask;
         desc.samplingRate = mSampleRate;
         desc.format = mFormat;
         desc.frameCount = mFrameCount;
@@ -4242,8 +4256,8 @@
     mResampler = 0;
 
     mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
-    mChannels = mInput->stream->common.get_channels(&mInput->stream->common);
-    mChannelCount = (uint16_t)popcount(mChannels);
+    mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common);
+    mChannelCount = (uint16_t)popcount(mChannelMask);
     mFormat = mInput->stream->common.get_format(&mInput->stream->common);
     mFrameSize = (uint16_t)audio_stream_frame_size(&mInput->stream->common);
     mInputBytes = mInput->stream->common.get_buffer_size(&mInput->stream->common);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 0698dcb..f3371bf 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -76,8 +76,8 @@
                                 pid_t pid,
                                 int streamType,
                                 uint32_t sampleRate,
-                                int format,
-                                int channelCount,
+                                uint32_t format,
+                                uint32_t channelMask,
                                 int frameCount,
                                 uint32_t flags,
                                 const sp<IMemory>& sharedBuffer,
@@ -87,7 +87,7 @@
 
     virtual     uint32_t    sampleRate(int output) const;
     virtual     int         channelCount(int output) const;
-    virtual     int         format(int output) const;
+    virtual     uint32_t    format(int output) const;
     virtual     size_t      frameCount(int output) const;
     virtual     uint32_t    latency(int output) const;
 
@@ -189,8 +189,8 @@
                                 pid_t pid,
                                 int input,
                                 uint32_t sampleRate,
-                                int format,
-                                int channelCount,
+                                uint32_t format,
+                                uint32_t channelMask,
                                 int frameCount,
                                 uint32_t flags,
                                 int *sessionId,
@@ -301,8 +301,8 @@
                                 TrackBase(const wp<ThreadBase>& thread,
                                         const sp<Client>& client,
                                         uint32_t sampleRate,
-                                        int format,
-                                        int channelCount,
+                                        uint32_t format,
+                                        uint32_t channelMask,
                                         int frameCount,
                                         uint32_t flags,
                                         const sp<IMemory>& sharedBuffer,
@@ -329,12 +329,14 @@
             virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0;
             virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
 
-            int format() const {
+            uint32_t format() const {
                 return mFormat;
             }
 
             int channelCount() const ;
 
+            uint32_t channelMask() const;
+
             int sampleRate() const;
 
             void* getBuffer(uint32_t offset, uint32_t frames) const;
@@ -360,9 +362,11 @@
             // we don't really need a lock for these
             int                 mState;
             int                 mClientTid;
-            uint8_t             mFormat;
+            uint32_t            mFormat;
             uint32_t            mFlags;
             int                 mSessionId;
+            uint8_t             mChannelCount;
+            uint32_t            mChannelMask;
         };
 
         class ConfigEvent {
@@ -375,7 +379,7 @@
 
                     uint32_t    sampleRate() const;
                     int         channelCount() const;
-                    int         format() const;
+                    uint32_t    format() const;
                     size_t      frameCount() const;
                     void        wakeUp()    { mWaitWorkCV.broadcast(); }
                     void        exit();
@@ -406,10 +410,10 @@
                     sp<AudioFlinger>        mAudioFlinger;
                     uint32_t                mSampleRate;
                     size_t                  mFrameCount;
-                    uint32_t                mChannels;
+                    uint32_t                mChannelMask;
                     uint16_t                mChannelCount;
                     uint16_t                mFrameSize;
-                    int                     mFormat;
+                    uint32_t                mFormat;
                     Condition               mParamCond;
                     Vector<String8>         mNewParameters;
                     status_t                mParamStatus;
@@ -442,8 +446,8 @@
                                         const sp<Client>& client,
                                         int streamType,
                                         uint32_t sampleRate,
-                                        int format,
-                                        int channelCount,
+                                        uint32_t format,
+                                        uint32_t channelMask,
                                         int frameCount,
                                         const sp<IMemory>& sharedBuffer,
                                         int sessionId);
@@ -530,8 +534,8 @@
                                 OutputTrack(  const wp<ThreadBase>& thread,
                                         DuplicatingThread *sourceThread,
                                         uint32_t sampleRate,
-                                        int format,
-                                        int channelCount,
+                                        uint32_t format,
+                                        uint32_t channelMask,
                                         int frameCount);
                                 ~OutputTrack();
 
@@ -583,8 +587,8 @@
                                     const sp<AudioFlinger::Client>& client,
                                     int streamType,
                                     uint32_t sampleRate,
-                                    int format,
-                                    int channelCount,
+                                    uint32_t format,
+                                    uint32_t channelMask,
                                     int frameCount,
                                     const sp<IMemory>& sharedBuffer,
                                     int sessionId,
@@ -829,8 +833,8 @@
                                 RecordTrack(const wp<ThreadBase>& thread,
                                         const sp<Client>& client,
                                         uint32_t sampleRate,
-                                        int format,
-                                        int channelCount,
+                                        uint32_t format,
+                                        uint32_t channelMask,
                                         int frameCount,
                                         uint32_t flags,
                                         int sessionId);
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 50dcda7..6e9319d 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -26,6 +26,10 @@
 #include <utils/Errors.h>
 #include <utils/Log.h>
 
+#include <cutils/bitops.h>
+
+#include <system/audio.h>
+
 #include "AudioMixer.h"
 
 namespace android {
@@ -61,6 +65,7 @@
         t->channelCount = 2;
         t->enabled = 0;
         t->format = 16;
+        t->channelMask = AUDIO_CHANNEL_OUT_STEREO;
         t->buffer.raw = 0;
         t->bufferProvider = 0;
         t->hook = 0;
@@ -180,13 +185,18 @@
 
     switch (target) {
     case TRACK:
-        if (name == CHANNEL_COUNT) {
-            if ((uint32_t(valueInt) <= MAX_NUM_CHANNELS) && (valueInt)) {
-                if (mState.tracks[ mActiveTrack ].channelCount != valueInt) {
-                    mState.tracks[ mActiveTrack ].channelCount = valueInt;
-                    LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", valueInt);
+        if (name == CHANNEL_MASK) {
+            uint32_t mask = (uint32_t)value;
+            if (mState.tracks[ mActiveTrack ].channelMask != mask) {
+                uint8_t channelCount = popcount(mask);
+                if ((channelCount <= MAX_NUM_CHANNELS) && (channelCount)) {
+                    mState.tracks[ mActiveTrack ].channelMask = mask;
+                    mState.tracks[ mActiveTrack ].channelCount = channelCount;
+                    LOGV("setParameter(TRACK, CHANNEL_MASK, %x)", mask);
                     invalidateState(1<<mActiveTrack);
+                    return NO_ERROR;
                 }
+            } else {
                 return NO_ERROR;
             }
         }
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 88408a7..75c9170 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -61,7 +61,7 @@
 
         // set Parameter names
         // for target TRACK
-        CHANNEL_COUNT   = 0x4000,
+        CHANNEL_MASK    = 0x4000,
         FORMAT          = 0x4001,
         MAIN_BUFFER     = 0x4002,
         AUX_BUFFER      = 0x4003,
@@ -150,6 +150,7 @@
         uint8_t     enabled      : 1;
         uint8_t     reserved0    : 3;
         uint8_t     format;
+        uint32_t    channelMask;
 
         AudioBufferProvider*                bufferProvider;
         mutable AudioBufferProvider::Buffer buffer;
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 455b4c2..cd58b9b9 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -39,6 +39,7 @@
 import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageInstallObserver;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -46,6 +47,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
@@ -74,12 +76,16 @@
 import java.io.EOFException;
 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.InputStream;
 import java.io.PrintWriter;
 import java.io.RandomAccessFile;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -398,6 +404,13 @@
                 break;
             }
 
+            case MSG_RUN_FULL_RESTORE:
+            {
+                FullRestoreParams params = (FullRestoreParams)msg.obj;
+                (new PerformFullRestoreTask(params.fd, params.observer, params.latch)).run();
+                break;
+            }
+
             case MSG_RUN_CLEAR:
             {
                 ClearParams params = (ClearParams)msg.obj;
@@ -1236,7 +1249,7 @@
                     Slog.d(TAG, "awaiting agent for " + app);
 
                     // success; wait for the agent to arrive
-                    // only wait 10 seconds for the clear data to happen
+                    // only wait 10 seconds for the bind to happen
                     long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
                     while (mConnecting && mConnectedAgent == null
                             && (System.currentTimeMillis() < timeoutMark)) {
@@ -1677,6 +1690,7 @@
         public void run() {
             final List<PackageInfo> packagesToBackup;
 
+            Slog.i(TAG, "--- Performing full-dataset restore ---");
             sendStartBackup();
 
             // doAllApps supersedes the package set if any
@@ -1779,7 +1793,9 @@
             // Version 1:
             //     package name
             //     package's versionCode
-            //     boolean: "1" if archive includes .apk, "0" otherwise
+            //     platform versionCode
+            //     getInstallerPackageName() for this package (maybe empty)
+            //     boolean: "1" if archive includes .apk; any other string means not
             //     number of signatures == N
             // N*:    signature byte array in ascii format per Signature.toCharsString()
             StringBuilder builder = new StringBuilder(4096);
@@ -1788,6 +1804,11 @@
             printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
             printer.println(pkg.packageName);
             printer.println(Integer.toString(pkg.versionCode));
+            printer.println(Integer.toString(Build.VERSION.SDK_INT));
+
+            String installerName = mPackageManager.getInstallerPackageName(pkg.packageName);
+            printer.println((installerName != null) ? installerName : "");
+
             printer.println(withApk ? "1" : "0");
             if (pkg.signatures == null) {
                 printer.println("0");
@@ -1861,6 +1882,896 @@
     }
 
 
+    // ----- Full restore from a file/socket -----
+
+    // Description of a file in the restore datastream
+    static class FileMetadata {
+        String packageName;             // name of the owning app
+        String installerPackageName;    // name of the market-type app that installed the owner
+        int type;                       // e.g. FullBackup.TYPE_DIRECTORY
+        String domain;                  // e.g. FullBackup.DATABASE_TREE_TOKEN
+        String path;                    // subpath within the semantic domain
+        long mode;                      // e.g. 0666 (actually int)
+        long mtime;                     // last mod time, UTC time_t (actually int)
+        long size;                      // bytes of content
+    }
+
+    enum RestorePolicy {
+        IGNORE,
+        ACCEPT,
+        ACCEPT_IF_APK
+    }
+
+    class PerformFullRestoreTask implements Runnable {
+        ParcelFileDescriptor mInputFile;
+        IFullBackupRestoreObserver mObserver;
+        AtomicBoolean mLatchObject;
+        IBackupAgent mAgent;
+        String mAgentPackage;
+        ApplicationInfo mTargetApp;
+        ParcelFileDescriptor[] mPipes = null;
+
+        // possible handling states for a given package in the restore dataset
+        final HashMap<String, RestorePolicy> mPackagePolicies
+                = new HashMap<String, RestorePolicy>();
+
+        // installer package names for each encountered app, derived from the manifests
+        final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
+
+        // Signatures for a given package found in its manifest file
+        final HashMap<String, Signature[]> mManifestSignatures
+                = new HashMap<String, Signature[]>();
+
+        // Packages we've already wiped data on when restoring their first file
+        final HashSet<String> mClearedPackages = new HashSet<String>();
+
+        PerformFullRestoreTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
+                AtomicBoolean latch) {
+            mInputFile = fd;
+            mObserver = observer;
+            mLatchObject = latch;
+            mAgent = null;
+            mAgentPackage = null;
+            mTargetApp = null;
+
+            // Which packages we've already wiped data on.  We prepopulate this
+            // with a whitelist of packages known to be unclearable.
+            mClearedPackages.add("android");
+            mClearedPackages.add("com.android.backupconfirm");
+            mClearedPackages.add("com.android.providers.settings");
+        }
+
+        class RestoreFileRunnable implements Runnable {
+            IBackupAgent mAgent;
+            FileMetadata mInfo;
+            ParcelFileDescriptor mSocket;
+            int mToken;
+
+            RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
+                    ParcelFileDescriptor socket, int token) throws IOException {
+                mAgent = agent;
+                mInfo = info;
+                mToken = token;
+
+                // This class is used strictly for process-local binder invocations.  The
+                // semantics of ParcelFileDescriptor differ in this case; in particular, we
+                // do not automatically get a 'dup'ed descriptor that we can can continue
+                // to use asynchronously from the caller.  So, we make sure to dup it ourselves
+                // before proceeding to do the restore.
+                mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
+            }
+
+            @Override
+            public void run() {
+                try {
+                    mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
+                            mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
+                            mToken, mBackupManagerBinder);
+                } catch (RemoteException e) {
+                    // never happens; this is used strictly for local binder calls
+                }
+            }
+        }
+
+        @Override
+        public void run() {
+            Slog.i(TAG, "--- Performing full-dataset restore ---");
+            sendStartRestore();
+
+            try {
+                byte[] buffer = new byte[32 * 1024];
+                FileInputStream instream = new FileInputStream(mInputFile.getFileDescriptor());
+
+                boolean didRestore;
+                do {
+                    didRestore = restoreOneFile(instream, buffer);
+                } while (didRestore);
+
+                if (DEBUG) Slog.v(TAG, "Done consuming input tarfile");
+            } finally {
+                tearDownPipes();
+                tearDownAgent(mTargetApp);
+
+                try {
+                    mInputFile.close();
+                } catch (IOException e) {
+                    /* nothing we can do about this */
+                }
+                synchronized (mCurrentOpLock) {
+                    mCurrentOperations.clear();
+                }
+                synchronized (mLatchObject) {
+                    mLatchObject.set(true);
+                    mLatchObject.notifyAll();
+                }
+                sendEndRestore();
+                mWakelock.release();
+                if (DEBUG) Slog.d(TAG, "Full restore pass complete.");
+            }
+        }
+
+        boolean restoreOneFile(InputStream instream, byte[] buffer) {
+            FileMetadata info;
+            try {
+                info = readTarHeaders(instream);
+                if (info != null) {
+                    if (DEBUG) {
+                        dumpFileMetadata(info);
+                    }
+
+                    final String pkg = info.packageName;
+                    if (!pkg.equals(mAgentPackage)) {
+                        // okay, change in package; set up our various
+                        // bookkeeping if we haven't seen it yet
+                        if (!mPackagePolicies.containsKey(pkg)) {
+                            mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
+                        }
+
+                        // Clean up the previous agent relationship if necessary,
+                        // and let the observer know we're considering a new app.
+                        if (mAgent != null) {
+                            if (DEBUG) Slog.d(TAG, "Saw new package; tearing down old one");
+                            tearDownPipes();
+                            tearDownAgent(mTargetApp);
+                            mTargetApp = null;
+                            mAgentPackage = null;
+                        }
+                    }
+
+                    if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
+                        mPackagePolicies.put(pkg, readAppManifest(info, instream));
+                        mPackageInstallers.put(pkg, info.installerPackageName);
+                        // We've read only the manifest content itself at this point,
+                        // so consume the footer before looping around to the next
+                        // input file
+                        skipTarPadding(info.size, instream);
+                        sendOnRestorePackage(pkg);
+                    } else {
+                        // Non-manifest, so it's actual file data.  Is this a package
+                        // we're ignoring?
+                        boolean okay = true;
+                        RestorePolicy policy = mPackagePolicies.get(pkg);
+                        switch (policy) {
+                            case IGNORE:
+                                okay = false;
+                                break;
+
+                            case ACCEPT_IF_APK:
+                                // If we're in accept-if-apk state, then the first file we
+                                // see MUST be the apk.
+                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
+                                    if (DEBUG) Slog.d(TAG, "APK file; installing");
+                                    // Try to install the app.
+                                    String installerName = mPackageInstallers.get(pkg);
+                                    okay = installApk(info, installerName, instream);
+                                    // good to go; promote to ACCEPT
+                                    mPackagePolicies.put(pkg, (okay)
+                                            ? RestorePolicy.ACCEPT
+                                            : RestorePolicy.IGNORE);
+                                    // At this point we've consumed this file entry
+                                    // ourselves, so just strip the tar footer and
+                                    // go on to the next file in the input stream
+                                    skipTarPadding(info.size, instream);
+                                    return true;
+                                } else {
+                                    // File data before (or without) the apk.  We can't
+                                    // handle it coherently in this case so ignore it.
+                                    mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
+                                    okay = false;
+                                }
+                                break;
+
+                            case ACCEPT:
+                                if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
+                                    if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
+                                    // we can take the data without the apk, so we
+                                    // *want* to do so.  skip the apk by declaring this
+                                    // one file not-okay without changing the restore
+                                    // policy for the package.
+                                    okay = false;
+                                }
+                                break;
+
+                            default:
+                                // Something has gone dreadfully wrong when determining
+                                // the restore policy from the manifest.  Ignore the
+                                // rest of this package's data.
+                                Slog.e(TAG, "Invalid policy from manifest");
+                                okay = false;
+                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
+                                break;
+                        }
+
+                        // If the policy is satisfied, go ahead and set up to pipe the
+                        // data to the agent.
+                        if (DEBUG && okay && mAgent != null) {
+                            Slog.i(TAG, "Reusing existing agent instance");
+                        }
+                        if (okay && mAgent == null) {
+                            if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
+
+                            try {
+                                mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
+
+                                // If we haven't sent any data to this app yet, we probably
+                                // need to clear it first.  Check that.
+                                if (!mClearedPackages.contains(pkg)) {
+                                    // apps with their own full backup agents are
+                                    // responsible for coherently managing a full
+                                    // restore.
+                                    if (mTargetApp.fullBackupAgentName == null) {
+                                        if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
+                                        clearApplicationDataSynchronous(pkg);
+                                    } else {
+                                        if (DEBUG) Slog.d(TAG, "full backup agent ("
+                                                + mTargetApp.fullBackupAgentName + ") => no clear");
+                                    }
+                                    mClearedPackages.add(pkg);
+                                } else {
+                                    if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
+                                }
+
+                                // All set; now set up the IPC and launch the agent
+                                setUpPipes();
+                                mAgent = bindToAgentSynchronous(mTargetApp,
+                                        IApplicationThread.BACKUP_MODE_RESTORE_FULL);
+                                mAgentPackage = pkg;
+                            } catch (IOException e) {
+                                // fall through to error handling
+                            } catch (NameNotFoundException e) {
+                                // fall through to error handling
+                            }
+
+                            if (mAgent == null) {
+                                if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
+                                okay = false;
+                                tearDownPipes();
+                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
+                            }
+                        }
+
+                        // Sanity check: make sure we never give data to the wrong app.  This
+                        // should never happen but a little paranoia here won't go amiss.
+                        if (okay && !pkg.equals(mAgentPackage)) {
+                            Slog.e(TAG, "Restoring data for " + pkg
+                                    + " but agent is for " + mAgentPackage);
+                            okay = false;
+                        }
+
+                        // At this point we have an agent ready to handle the full
+                        // restore data as well as a pipe for sending data to
+                        // that agent.  Tell the agent to start reading from the
+                        // pipe.
+                        if (okay) {
+                            boolean agentSuccess = true;
+                            long toCopy = info.size;
+                            final int token = generateToken();
+                            try {
+                                if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
+                                        + info.path);
+                                prepareOperationTimeout(token,
+                                        TIMEOUT_FULL_BACKUP_INTERVAL);
+                                // fire up the app's agent listening on the socket.  If
+                                // the agent is running in the system process we can't
+                                // just invoke it asynchronously, so we provide a thread
+                                // for it here.
+                                if (mTargetApp.processName.equals("system")) {
+                                    Slog.d(TAG, "system process agent - spinning a thread");
+                                    RestoreFileRunnable runner = new RestoreFileRunnable(
+                                            mAgent, info, mPipes[0], token);
+                                    new Thread(runner).start();
+                                } else {
+                                    mAgent.doRestoreFile(mPipes[0], info.size, info.type,
+                                            info.domain, info.path, info.mode, info.mtime,
+                                            token, mBackupManagerBinder);
+                                }
+                            } catch (IOException e) {
+                                // couldn't dup the socket for a process-local restore
+                                Slog.d(TAG, "Couldn't establish restore");
+                                agentSuccess = false;
+                                okay = false;
+                            } catch (RemoteException e) {
+                                // whoops, remote agent went away.  We'll eat the content
+                                // ourselves, then, and not copy it over.
+                                Slog.e(TAG, "Agent crashed during full restore");
+                                agentSuccess = false;
+                                okay = false;
+                            }
+
+                            // Copy over the data if the agent is still good
+                            if (okay) {
+                                boolean pipeOkay = true;
+                                FileOutputStream pipe = new FileOutputStream(
+                                        mPipes[1].getFileDescriptor());
+                                if (DEBUG) Slog.d(TAG, "Piping data to agent");
+                                while (toCopy > 0) {
+                                    int toRead = (toCopy > buffer.length)
+                                    ? buffer.length : (int)toCopy;
+                                    int nRead = instream.read(buffer, 0, toRead);
+                                    if (nRead <= 0) break;
+                                    toCopy -= nRead;
+
+                                    // send it to the output pipe as long as things
+                                    // are still good
+                                    if (pipeOkay) {
+                                        try {
+                                            pipe.write(buffer, 0, nRead);
+                                        } catch (IOException e) {
+                                            Slog.e(TAG,
+                                                    "Failed to write to restore pipe", e);
+                                            pipeOkay = false;
+                                        }
+                                    }
+                                }
+
+                                // done sending that file!  Now we just need to consume
+                                // the delta from info.size to the end of block.
+                                skipTarPadding(info.size, instream);
+
+                                // and now that we've sent it all, wait for the remote
+                                // side to acknowledge receipt
+                                agentSuccess = waitUntilOperationComplete(token);
+                            }
+
+                            // okay, if the remote end failed at any point, deal with
+                            // it by ignoring the rest of the restore on it
+                            if (!agentSuccess) {
+                                mBackupHandler.removeMessages(MSG_TIMEOUT);
+                                tearDownPipes();
+                                tearDownAgent(mTargetApp);
+                                mAgent = null;
+                                mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
+                            }
+                        }
+
+                        // Problems setting up the agent communication, or an already-
+                        // ignored package: skip to the next tar stream entry by
+                        // reading and discarding this file.
+                        if (!okay) {
+                            if (DEBUG) Slog.d(TAG, "[discarding file content]");
+                            long bytesToConsume = (info.size + 511) & ~511;
+                            while (bytesToConsume > 0) {
+                                int toRead = (bytesToConsume > buffer.length)
+                                ? buffer.length : (int)bytesToConsume;
+                                long nRead = instream.read(buffer, 0, toRead);
+                                if (nRead <= 0) break;
+                                bytesToConsume -= nRead;
+                            }
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                Slog.w(TAG, "io exception on restore socket read", e);
+                // treat as EOF
+                info = null;
+            }
+
+            return (info != null);
+        }
+
+        void setUpPipes() throws IOException {
+            mPipes = ParcelFileDescriptor.createPipe();
+        }
+
+        void tearDownPipes() {
+            if (mPipes != null) {
+                if (mPipes[0] != null) {
+                    try {
+                        mPipes[0].close();
+                        mPipes[0] = null;
+                        mPipes[1].close();
+                        mPipes[1] = null;
+                    } catch (IOException e) {
+                        Slog.w(TAG, "Couldn't close agent pipes", e);
+                    }
+                }
+                mPipes = null;
+            }
+        }
+
+        void tearDownAgent(ApplicationInfo app) {
+            if (mAgent != null) {
+                try {
+                    // unbind and tidy up even on timeout or failure, just in case
+                    mActivityManager.unbindBackupAgent(app);
+
+                    // The agent was running with a stub Application object, so shut it down.
+                    // !!! We hardcode the confirmation UI's package name here rather than use a
+                    //     manifest flag!  TODO something less direct.
+                    if (app.uid != Process.SYSTEM_UID
+                            && !app.packageName.equals("com.android.backupconfirm")) {
+                        if (DEBUG) Slog.d(TAG, "Killing host process");
+                        mActivityManager.killApplicationProcess(app.processName, app.uid);
+                    } else {
+                        if (DEBUG) Slog.d(TAG, "Not killing after full restore");
+                    }
+                } catch (RemoteException e) {
+                    Slog.d(TAG, "Lost app trying to shut down");
+                }
+                mAgent = null;
+            }
+        }
+
+        class RestoreInstallObserver extends IPackageInstallObserver.Stub {
+            final AtomicBoolean mDone = new AtomicBoolean();
+            int mResult;
+
+            public void reset() {
+                synchronized (mDone) {
+                    mDone.set(false);
+                }
+            }
+
+            public void waitForCompletion() {
+                synchronized (mDone) {
+                    while (mDone.get() == false) {
+                        try {
+                            mDone.wait();
+                        } catch (InterruptedException e) { }
+                    }
+                }
+            }
+
+            int getResult() {
+                return mResult;
+            }
+
+            @Override
+            public void packageInstalled(String packageName, int returnCode)
+                    throws RemoteException {
+                synchronized (mDone) {
+                    mResult = returnCode;
+                    mDone.set(true);
+                    mDone.notifyAll();
+                }
+            }
+        }
+        final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
+
+        boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
+            boolean okay = true;
+
+            if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
+
+            // The file content is an .apk file.  Copy it out to a staging location and
+            // attempt to install it.
+            File apkFile = new File(mDataDir, info.packageName);
+            try {
+                FileOutputStream apkStream = new FileOutputStream(apkFile);
+                byte[] buffer = new byte[32 * 1024];
+                long size = info.size;
+                while (size > 0) {
+                    long toRead = (buffer.length < size) ? buffer.length : size;
+                    int didRead = instream.read(buffer, 0, (int)toRead);
+                    apkStream.write(buffer, 0, didRead);
+                    size -= didRead;
+                }
+                apkStream.close();
+
+                // make sure the installer can read it
+                apkFile.setReadable(true, false);
+
+                // Now install it
+                Uri packageUri = Uri.fromFile(apkFile);
+                mInstallObserver.reset();
+                mPackageManager.installPackage(packageUri, mInstallObserver,
+                        PackageManager.INSTALL_REPLACE_EXISTING, installerPackage);
+                mInstallObserver.waitForCompletion();
+
+                if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
+                    // The only time we continue to accept install of data even if the
+                    // apk install failed is if we had already determined that we could
+                    // accept the data regardless.
+                    if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
+                        okay = false;
+                    }
+                }
+            } catch (IOException e) {
+                Slog.e(TAG, "Unable to transcribe restored apk for install");
+                okay = false;
+            } finally {
+                apkFile.delete();
+            }
+
+            return okay;
+        }
+
+        // Given an actual file content size, consume the post-content padding mandated
+        // by the tar format.
+        void skipTarPadding(long size, InputStream instream) throws IOException {
+            long partial = (size + 512) % 512;
+            if (partial > 0) {
+                byte[] buffer = new byte[512];
+                instream.read(buffer, 0, 512 - (int)partial);
+            }
+        }
+
+        // Returns a policy constant; takes a buffer arg to reduce memory churn
+        RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
+                throws IOException {
+            // Fail on suspiciously large manifest files
+            if (info.size > 64 * 1024) {
+                throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
+            }
+            byte[] buffer = new byte[(int) info.size];
+            int nRead = 0;
+            while (nRead < info.size) {
+                nRead += instream.read(buffer, nRead, (int)info.size - nRead);
+            }
+
+            RestorePolicy policy = RestorePolicy.IGNORE;
+            String[] str = new String[1];
+            int offset = 0;
+
+            try {
+                offset = extractLine(buffer, offset, str);
+                int version = Integer.parseInt(str[0]);
+                if (version == BACKUP_MANIFEST_VERSION) {
+                    offset = extractLine(buffer, offset, str);
+                    String manifestPackage = str[0];
+                    // TODO: handle <original-package>
+                    if (manifestPackage.equals(info.packageName)) {
+                        offset = extractLine(buffer, offset, str);
+                        version = Integer.parseInt(str[0]);  // app version
+                        offset = extractLine(buffer, offset, str);
+                        int platformVersion = Integer.parseInt(str[0]);
+                        offset = extractLine(buffer, offset, str);
+                        info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
+                        offset = extractLine(buffer, offset, str);
+                        boolean hasApk = str[0].equals("1");
+                        offset = extractLine(buffer, offset, str);
+                        int numSigs = Integer.parseInt(str[0]);
+                        Signature[] sigs = null;
+                        if (numSigs > 0) {
+                            sigs = new Signature[numSigs];
+                            for (int i = 0; i < numSigs; i++) {
+                                offset = extractLine(buffer, offset, str);
+                                sigs[i] = new Signature(str[0]);
+                            }
+
+                            // Okay, got the manifest info we need...
+                            try {
+                                // Verify signatures against any installed version; if they
+                                // don't match, then we fall though and ignore the data.  The
+                                // signatureMatch() method explicitly ignores the signature
+                                // check for packages installed on the system partition, because
+                                // such packages are signed with the platform cert instead of
+                                // the app developer's cert, so they're different on every
+                                // device.
+                                PackageInfo pkgInfo = mPackageManager.getPackageInfo(
+                                        info.packageName, PackageManager.GET_SIGNATURES);
+                                if (signaturesMatch(sigs, pkgInfo)) {
+                                    if (pkgInfo.versionCode >= version) {
+                                        Slog.i(TAG, "Sig + version match; taking data");
+                                        policy = RestorePolicy.ACCEPT;
+                                    } else {
+                                        // The data is from a newer version of the app than
+                                        // is presently installed.  That means we can only
+                                        // use it if the matching apk is also supplied.
+                                        Slog.d(TAG, "Data version " + version
+                                                + " is newer than installed version "
+                                                + pkgInfo.versionCode + " - requiring apk");
+                                        policy = RestorePolicy.ACCEPT_IF_APK;
+                                    }
+                                }
+                            } catch (NameNotFoundException e) {
+                                // Okay, the target app isn't installed.  We can process
+                                // the restore properly only if the dataset provides the
+                                // apk file and we can successfully install it.
+                                if (DEBUG) Slog.i(TAG, "Package " + info.packageName
+                                        + " not installed; requiring apk in dataset");
+                                policy = RestorePolicy.ACCEPT_IF_APK;
+                            }
+
+                            if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
+                                Slog.i(TAG, "Cannot restore package " + info.packageName
+                                        + " without the matching .apk");
+                            }
+                        } else {
+                            Slog.i(TAG, "Missing signature on backed-up package "
+                                    + info.packageName);
+                        }
+                    } else {
+                        Slog.i(TAG, "Expected package " + info.packageName
+                                + " but restore manifest claims " + manifestPackage);
+                    }
+                } else {
+                    Slog.i(TAG, "Unknown restore manifest version " + version
+                            + " for package " + info.packageName);
+                }
+            } catch (NumberFormatException e) {
+                Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
+            }
+
+            return policy;
+        }
+
+        // Builds a line from a byte buffer starting at 'offset', and returns
+        // the index of the next unconsumed data in the buffer.
+        int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
+            final int end = buffer.length;
+            if (offset >= end) throw new IOException("Incomplete data");
+
+            int pos;
+            for (pos = offset; pos < end; pos++) {
+                byte c = buffer[pos];
+                // at LF we declare end of line, and return the next char as the
+                // starting point for the next time through
+                if (c == '\n') {
+                    break;
+                }
+            }
+            outStr[0] = new String(buffer, offset, pos - offset);
+            pos++;  // may be pointing an extra byte past the end but that's okay
+            return pos;
+        }
+
+        void dumpFileMetadata(FileMetadata info) {
+            if (DEBUG) {
+                StringBuilder b = new StringBuilder(128);
+
+                // mode string
+                b.append((info.type == FullBackup.TYPE_DIRECTORY) ? 'd' : '-');
+                b.append(((info.mode & 0400) != 0) ? 'r' : '-');
+                b.append(((info.mode & 0200) != 0) ? 'w' : '-');
+                b.append(((info.mode & 0100) != 0) ? 'x' : '-');
+                b.append(((info.mode & 0040) != 0) ? 'r' : '-');
+                b.append(((info.mode & 0020) != 0) ? 'w' : '-');
+                b.append(((info.mode & 0010) != 0) ? 'x' : '-');
+                b.append(((info.mode & 0004) != 0) ? 'r' : '-');
+                b.append(((info.mode & 0002) != 0) ? 'w' : '-');
+                b.append(((info.mode & 0001) != 0) ? 'x' : '-');
+                b.append(String.format(" %9d ", info.size));
+
+                Date stamp = new Date(info.mtime);
+                b.append(new SimpleDateFormat("MMM dd kk:mm:ss ").format(stamp));
+
+                b.append(info.packageName);
+                b.append(" :: ");
+                b.append(info.domain);
+                b.append(" :: ");
+                b.append(info.path);
+
+                Slog.i(TAG, b.toString());
+            }
+        }
+        // Consume a tar file header block [sequence] and accumulate the relevant metadata
+        FileMetadata readTarHeaders(InputStream instream) throws IOException {
+            byte[] block = new byte[512];
+            FileMetadata info = null;
+
+            boolean gotHeader = readTarHeader(instream, block);
+            if (gotHeader) {
+                // okay, presume we're okay, and extract the various metadata
+                info = new FileMetadata();
+                info.size = extractRadix(block, 124, 12, 8);
+                info.mtime = extractRadix(block, 136, 12, 8);
+                info.mode = extractRadix(block, 100, 8, 8);
+
+                info.path = extractString(block, 345, 155); // prefix
+                String path = extractString(block, 0, 100);
+                if (path.length() > 0) {
+                    if (info.path.length() > 0) info.path += '/';
+                    info.path += path;
+                }
+
+                // tar link indicator field: 1 byte at offset 156 in the header.
+                int typeChar = block[156];
+                if (typeChar == 'x') {
+                    // pax extended header, so we need to read that
+                    gotHeader = readPaxExtendedHeader(instream, info);
+                    if (gotHeader) {
+                        // and after a pax extended header comes another real header -- read
+                        // that to find the real file type
+                        gotHeader = readTarHeader(instream, block);
+                    }
+                    if (!gotHeader) throw new IOException("Bad or missing pax header");
+
+                    typeChar = block[156];
+                }
+
+                switch (typeChar) {
+                    case '0': info.type = FullBackup.TYPE_FILE; break;
+                    case '5': info.type = FullBackup.TYPE_DIRECTORY; break;
+                    case 0: {
+                        // presume EOF
+                        return null;
+                    }
+                    default: {
+                        Slog.e(TAG, "Unknown tar entity type: " + typeChar);
+                        throw new IOException("Unknown entity type " + typeChar);
+                    }
+                }
+
+                // Parse out the path
+                //
+                // first: apps/shared/unrecognized
+                if (FullBackup.SHARED_PREFIX.regionMatches(0,
+                        info.path, 0, FullBackup.SHARED_PREFIX.length())) {
+                    // File in shared storage.  !!! TODO: implement this.
+                    info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
+                    info.domain = FullBackup.SHARED_STORAGE_TOKEN;
+                } else if (FullBackup.APPS_PREFIX.regionMatches(0,
+                        info.path, 0, FullBackup.APPS_PREFIX.length())) {
+                    // App content!  Parse out the package name and domain
+
+                    // strip the apps/ prefix
+                    info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
+
+                    // extract the package name
+                    int slash = info.path.indexOf('/');
+                    if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
+                    info.packageName = info.path.substring(0, slash);
+                    info.path = info.path.substring(slash+1);
+
+                    // if it's a manifest we're done, otherwise parse out the domains
+                    if (!info.path.equals(BACKUP_MANIFEST_FILENAME)) {
+                        slash = info.path.indexOf('/');
+                        if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
+                        info.domain = info.path.substring(0, slash);
+                        // validate that it's one of the domains we understand
+                        if (!info.domain.equals(FullBackup.APK_TREE_TOKEN)
+                                && !info.domain.equals(FullBackup.DATA_TREE_TOKEN)
+                                && !info.domain.equals(FullBackup.DATABASE_TREE_TOKEN)
+                                && !info.domain.equals(FullBackup.ROOT_TREE_TOKEN)
+                                && !info.domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)
+                                && !info.domain.equals(FullBackup.OBB_TREE_TOKEN)
+                                && !info.domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
+                            throw new IOException("Unrecognized domain " + info.domain);
+                        }
+
+                        info.path = info.path.substring(slash + 1);
+                    }
+                }
+            }
+            return info;
+        }
+
+        boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
+            int nRead = instream.read(block, 0, 512);
+            if (nRead > 0 && nRead != 512) {
+                // if we read only a partial block, then things are
+                // clearly screwed up.  terminate the restore.
+                throw new IOException("Partial header block: " + nRead);
+            }
+            return (nRead > 0);
+        }
+
+        // overwrites 'info' fields based on the pax extended header
+        boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
+                throws IOException {
+            // We should never see a pax extended header larger than this
+            if (info.size > 32*1024) {
+                Slog.w(TAG, "Suspiciously large pax header size " + info.size
+                        + " - aborting");
+                throw new IOException("Sanity failure: pax header size " + info.size);
+            }
+
+            // read whole blocks, not just the content size
+            int numBlocks = (int)((info.size + 511) >> 9);
+            byte[] data = new byte[numBlocks * 512];
+            int nRead = instream.read(data);
+            if (nRead != data.length) {
+                return false;
+            }
+
+            final int contentSize = (int) info.size;
+            int offset = 0;
+            do {
+                // extract the line at 'offset'
+                int eol = offset+1;
+                while (eol < contentSize && data[eol] != ' ') eol++;
+                if (eol >= contentSize) {
+                    // error: we just hit EOD looking for the end of the size field
+                    throw new IOException("Invalid pax data");
+                }
+                // eol points to the space between the count and the key
+                int linelen = (int) extractRadix(data, offset, eol - offset, 10);
+                int key = eol + 1;  // start of key=value
+                eol = offset + linelen - 1; // trailing LF
+                int value;
+                for (value = key+1; data[value] != '=' && value <= eol; value++);
+                if (value > eol) {
+                    throw new IOException("Invalid pax declaration");
+                }
+
+                // pax requires that key/value strings be in UTF-8
+                String keyStr = new String(data, key, value-key, "UTF-8");
+                // -1 to strip the trailing LF
+                String valStr = new String(data, value+1, eol-value-1, "UTF-8");
+
+                if ("path".equals(keyStr)) {
+                    info.path = valStr;
+                } else if ("size".equals(keyStr)) {
+                    info.size = Long.parseLong(valStr);
+                } else {
+                    if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
+                }
+
+                offset += linelen;
+            } while (offset < contentSize);
+
+            return true;
+        }
+
+        long extractRadix(byte[] data, int offset, int maxChars, int radix)
+                throws IOException {
+            long value = 0;
+            final int end = offset + maxChars;
+            for (int i = offset; i < end; i++) {
+                final byte b = data[i];
+                if (b == 0 || b == ' ') break;
+                if (b < '0' || b > ('0' + radix - 1)) {
+                    throw new IOException("Invalid number in header");
+                }
+                value = radix * value + (b - '0');
+            }
+            return value;
+        }
+
+        String extractString(byte[] data, int offset, int maxChars) throws IOException {
+            final int end = offset + maxChars;
+            int eos = offset;
+            // tar string fields can end with either NUL or SPC
+            while (eos < end && data[eos] != 0 && data[eos] != ' ') eos++;
+            return new String(data, offset, eos-offset, "US-ASCII");
+        }
+
+        void sendStartRestore() {
+            if (mObserver != null) {
+                try {
+                    mObserver.onStartRestore();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "full restore observer went away: startRestore");
+                    mObserver = null;
+                }
+            }
+        }
+
+        void sendOnRestorePackage(String name) {
+            if (mObserver != null) {
+                try {
+                    // TODO: use a more user-friendly name string
+                    mObserver.onRestorePackage(name);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "full restore observer went away: restorePackage");
+                    mObserver = null;
+                }
+            }
+        }
+
+        void sendEndRestore() {
+            if (mObserver != null) {
+                try {
+                    mObserver.onEndRestore();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "full restore observer went away: endRestore");
+                    mObserver = null;
+                }
+            }
+        }
+    }
+
     // ----- Restore handling -----
 
     private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
@@ -2583,41 +3494,95 @@
                 mFullConfirmations.put(token, params);
             }
 
-            // start up the confirmation UI, making sure the screen lights up
-            if (DEBUG) Slog.d(TAG, "Starting confirmation UI, token=" + token);
-            try {
-                Intent confIntent = new Intent(FullBackup.FULL_BACKUP_INTENT_ACTION);
-                confIntent.setClassName("com.android.backupconfirm",
-                        "com.android.backupconfirm.BackupRestoreConfirmation");
-                confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
-                confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                mContext.startActivity(confIntent);
-            } catch (ActivityNotFoundException e) {
-                Slog.e(TAG, "Unable to launch full backup confirmation", e);
+            // start up the confirmation UI
+            if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
+            if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
+                Slog.e(TAG, "Unable to launch full backup confirmation");
                 mFullConfirmations.delete(token);
                 return;
             }
+
+            // make sure the screen is lit for the user interaction
             mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
 
             // start the confirmation countdown
-            if (DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
-                    + TIMEOUT_FULL_CONFIRMATION + " millis");
-            Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
-                    token, 0, params);
-            mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
+            startConfirmationTimeout(token, params);
 
             // wait for the backup to be performed
             if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
             waitForCompletion(params);
-            if (DEBUG) Slog.d(TAG, "...Full backup operation complete!");
         } finally {
-            Binder.restoreCallingIdentity(oldId);
             try {
                 fd.close();
             } catch (IOException e) {
                 // just eat it
             }
+            Binder.restoreCallingIdentity(oldId);
         }
+        if (DEBUG) Slog.d(TAG, "Full backup done; returning to caller");
+    }
+
+    public void fullRestore(ParcelFileDescriptor fd) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
+        Slog.i(TAG, "Beginning full restore...");
+
+        long oldId = Binder.clearCallingIdentity();
+
+        try {
+            FullRestoreParams params = new FullRestoreParams(fd);
+            final int token = generateToken();
+            synchronized (mFullConfirmations) {
+                mFullConfirmations.put(token, params);
+            }
+
+            // start up the confirmation UI
+            if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
+            if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
+                Slog.e(TAG, "Unable to launch full restore confirmation");
+                mFullConfirmations.delete(token);
+                return;
+            }
+
+            // make sure the screen is lit for the user interaction
+            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
+
+            // start the confirmation countdown
+            startConfirmationTimeout(token, params);
+
+            // wait for the restore to be performed
+            if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
+            waitForCompletion(params);
+        } finally {
+            try {
+                fd.close();
+            } catch (IOException e) {
+                Slog.w(TAG, "Error trying to close fd after full restore: " + e);
+            }
+            Binder.restoreCallingIdentity(oldId);
+            Slog.i(TAG, "Full restore completed");
+        }
+    }
+
+    boolean startConfirmationUi(int token, String action) {
+        try {
+            Intent confIntent = new Intent(action);
+            confIntent.setClassName("com.android.backupconfirm",
+                    "com.android.backupconfirm.BackupRestoreConfirmation");
+            confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
+            confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            mContext.startActivity(confIntent);
+        } catch (ActivityNotFoundException e) {
+            return false;
+        }
+        return true;
+    }
+
+    void startConfirmationTimeout(int token, FullParams params) {
+        if (DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
+                + TIMEOUT_FULL_CONFIRMATION + " millis");
+        Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
+                token, 0, params);
+        mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
     }
 
     void waitForCompletion(FullParams params) {
@@ -2661,9 +3626,10 @@
                     if (allow) {
                         params.observer = observer;
                         final int verb = params instanceof FullBackupParams
-                        ? MSG_RUN_FULL_BACKUP
+                                ? MSG_RUN_FULL_BACKUP
                                 : MSG_RUN_FULL_RESTORE;
 
+                        if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
                         mWakelock.acquire();
                         Message msg = mBackupHandler.obtainMessage(verb, params);
                         mBackupHandler.sendMessage(msg);
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index a564c2d..dd76eb8 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,6 +16,11 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.UPDATE_DEVICE_STATS;
+import static android.net.ConnectivityManager.isNetworkTypeValid;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
+
 import android.bluetooth.BluetoothTetheringDataTracker;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -26,11 +31,13 @@
 import android.net.DummyDataStateTracker;
 import android.net.EthernetDataTracker;
 import android.net.IConnectivityManager;
-import android.net.LinkAddress;
+import android.net.INetworkPolicyListener;
+import android.net.INetworkPolicyManager;
 import android.net.LinkProperties;
 import android.net.MobileDataStateTracker;
 import android.net.NetworkConfig;
 import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkStateTracker;
 import android.net.NetworkUtils;
 import android.net.Proxy;
@@ -54,6 +61,7 @@
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Slog;
+import android.util.SparseIntArray;
 
 import com.android.internal.telephony.Phone;
 import com.android.server.connectivity.Tethering;
@@ -62,13 +70,12 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.InetAddress;
-import java.net.Inet4Address;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.GregorianCalendar;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * @hide
@@ -78,6 +85,8 @@
     private static final boolean DBG = true;
     private static final String TAG = "ConnectivityService";
 
+    private static final boolean LOGD_RULES = false;
+
     // how long to wait before switching back to a radio's default network
     private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000;
     // system property that can override the above value
@@ -91,6 +100,9 @@
     private Tethering mTethering;
     private boolean mTetheringConfigValid = false;
 
+    /** Currently active network rules by UID. */
+    private SparseIntArray mUidRules = new SparseIntArray();
+
     /**
      * Sometimes we want to refer to the individual network state
      * trackers separately, and sometimes we just want to treat them
@@ -128,6 +140,7 @@
     private AtomicBoolean mBackgroundDataEnabled = new AtomicBoolean(true);
 
     private INetworkManagementService mNetd;
+    private INetworkPolicyManager mPolicyManager;
 
     private static final int ENABLED  = 1;
     private static final int DISABLED = 0;
@@ -250,14 +263,8 @@
     }
     RadioAttributes[] mRadioAttributes;
 
-    public static synchronized ConnectivityService getInstance(Context context) {
-        if (sServiceInstance == null) {
-            sServiceInstance = new ConnectivityService(context);
-        }
-        return sServiceInstance;
-    }
-
-    private ConnectivityService(Context context) {
+    public ConnectivityService(
+            Context context, INetworkManagementService netd, INetworkPolicyManager policyManager) {
         if (DBG) log("ConnectivityService starting up");
 
         HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
@@ -290,9 +297,19 @@
             loge("Error setting defaultDns using " + dns);
         }
 
-        mContext = context;
+        mContext = checkNotNull(context, "missing Context");
+        mNetd = checkNotNull(netd, "missing INetworkManagementService");
+        mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
 
-        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        try {
+            mPolicyManager.registerListener(mPolicyListener);
+        } catch (RemoteException e) {
+            // ouch, no rules updates means some processes may never get network
+            Slog.e(TAG, "unable to register INetworkPolicyListener", e);
+        }
+
+        final PowerManager powerManager = (PowerManager) context.getSystemService(
+                Context.POWER_SERVICE);
         mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
         mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_networkTransitionTimeout);
@@ -536,32 +553,92 @@
     }
 
     /**
+     * Check if UID is blocked from using the given {@link NetworkInfo}.
+     */
+    private boolean isNetworkBlocked(NetworkInfo info, int uid) {
+        synchronized (mUidRules) {
+            return isNetworkBlockedLocked(info, uid);
+        }
+    }
+
+    /**
+     * Check if UID is blocked from using the given {@link NetworkInfo}.
+     */
+    private boolean isNetworkBlockedLocked(NetworkInfo info, int uid) {
+        // TODO: expand definition of "paid" network to cover tethered or paid
+        // hotspot use cases.
+        final boolean networkIsPaid = info.getType() != ConnectivityManager.TYPE_WIFI;
+        final int uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+
+        if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) {
+            return true;
+        }
+
+        // no restrictive rules; network is visible
+        return false;
+    }
+
+    /**
      * Return NetworkInfo for the active (i.e., connected) network interface.
      * It is assumed that at most one network is active at a time. If more
      * than one is active, it is indeterminate which will be returned.
      * @return the info for the active network, or {@code null} if none is
      * active
      */
+    @Override
     public NetworkInfo getActiveNetworkInfo() {
-        return getNetworkInfo(mActiveDefaultNetwork);
+        enforceAccessPermission();
+        final int uid = Binder.getCallingUid();
+        return getNetworkInfo(mActiveDefaultNetwork, uid);
     }
 
+    @Override
+    public NetworkInfo getActiveNetworkInfoForUid(int uid) {
+        enforceConnectivityInternalPermission();
+        return getNetworkInfo(mActiveDefaultNetwork, uid);
+    }
+
+    @Override
     public NetworkInfo getNetworkInfo(int networkType) {
         enforceAccessPermission();
-        if (ConnectivityManager.isNetworkTypeValid(networkType)) {
-            NetworkStateTracker t = mNetTrackers[networkType];
-            if (t != null)
-                return t.getNetworkInfo();
-        }
-        return null;
+        final int uid = Binder.getCallingUid();
+        return getNetworkInfo(networkType, uid);
     }
 
+    private NetworkInfo getNetworkInfo(int networkType, int uid) {
+        NetworkInfo info = null;
+        if (isNetworkTypeValid(networkType)) {
+            final NetworkStateTracker tracker = mNetTrackers[networkType];
+            if (tracker != null) {
+                info = tracker.getNetworkInfo();
+                if (isNetworkBlocked(info, uid)) {
+                    // network is blocked; clone and override state
+                    info = new NetworkInfo(info);
+                    info.setDetailedState(DetailedState.BLOCKED, null, null);
+                }
+            }
+        }
+        return info;
+    }
+
+    @Override
     public NetworkInfo[] getAllNetworkInfo() {
         enforceAccessPermission();
-        NetworkInfo[] result = new NetworkInfo[mNetworksDefined];
+        final int uid = Binder.getCallingUid();
+        final NetworkInfo[] result = new NetworkInfo[mNetworksDefined];
         int i = 0;
-        for (NetworkStateTracker t : mNetTrackers) {
-            if(t != null) result[i++] = t.getNetworkInfo();
+        synchronized (mUidRules) {
+            for (NetworkStateTracker tracker : mNetTrackers) {
+                if (tracker != null) {
+                    NetworkInfo info = tracker.getNetworkInfo();
+                    if (isNetworkBlockedLocked(info, uid)) {
+                        // network is blocked; clone and override state
+                        info = new NetworkInfo(info);
+                        info.setDetailedState(DetailedState.BLOCKED, null, null);
+                    }
+                    result[i++] = info;
+                }
+            }
         }
         return result;
     }
@@ -574,15 +651,19 @@
      * @return the ip properties for the active network, or {@code null} if
      * none is active
      */
+    @Override
     public LinkProperties getActiveLinkProperties() {
         return getLinkProperties(mActiveDefaultNetwork);
     }
 
+    @Override
     public LinkProperties getLinkProperties(int networkType) {
         enforceAccessPermission();
-        if (ConnectivityManager.isNetworkTypeValid(networkType)) {
-            NetworkStateTracker t = mNetTrackers[networkType];
-            if (t != null) return t.getLinkProperties();
+        if (isNetworkTypeValid(networkType)) {
+            final NetworkStateTracker tracker = mNetTrackers[networkType];
+            if (tracker != null) {
+                return tracker.getLinkProperties();
+            }
         }
         return null;
     }
@@ -1027,6 +1108,30 @@
         }
     }
 
+    private INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
+        @Override
+        public void onRulesChanged(int uid, int uidRules) {
+            // only someone like NPMS should only be calling us
+            // TODO: create permission for modifying data policy
+            mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
+            if (LOGD_RULES) {
+                Slog.d(TAG, "onRulesChanged(uid=" + uid + ", uidRules=" + uidRules + ")");
+            }
+
+            synchronized (mUidRules) {
+                // skip update when we've already applied rules
+                final int oldRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+                if (oldRules == uidRules) return;
+
+                mUidRules.put(uid, uidRules);
+            }
+
+            // TODO: dispatch into NMS to push rules towards kernel module
+            // TODO: notify UID when it has requested targeted updates
+        }
+    };
+
     /**
      * @see ConnectivityManager#setMobileDataEnabled(boolean)
      */
@@ -1284,9 +1389,6 @@
     }
 
     void systemReady() {
-        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        mNetd = INetworkManagementService.Stub.asInterface(b);
-
         synchronized(this) {
             mSystemReady = true;
             if (mInitialBroadcast != null) {
@@ -2255,4 +2357,11 @@
         }
         return networkType;
     }
+
+    private static <T> T checkNotNull(T value, String message) {
+        if (value == null) {
+            throw new NullPointerException(message);
+        }
+        return value;
+    }
 }
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 92d76be..78636f3 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -1925,9 +1925,8 @@
             // Check for permissions if a particular caller is specified
             if (who != null) {
                 // When checking for a single caller, status is based on caller's request
-                ActiveAdmin ap = getActiveAdminForCallerLocked(who,
-                        DeviceAdminInfo.USES_ENCRYPTED_STORAGE);
-                return ap.encryptionRequested;
+                ActiveAdmin ap = getActiveAdminUncheckedLocked(who);
+                return ap != null ? ap.encryptionRequested : false;
             }
 
             // If no particular caller is specified, return the aggregate set of requests.
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index 54555bb..99c8af6 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -37,16 +37,25 @@
 public class SystemBackupAgent extends BackupAgentHelper {
     private static final String TAG = "SystemBackupAgent";
 
-    // These paths must match what the WallpaperManagerService uses
+    // These paths must match what the WallpaperManagerService uses.  The leaf *_FILENAME
+    // are also used in the full-backup file format, so must not change unless steps are
+    // taken to support the legacy backed-up datasets.
+    private static final String WALLPAPER_IMAGE_FILENAME = "wallpaper";
+    private static final String WALLPAPER_INFO_FILENAME = "wallpaper_info.xml";
+
     private static final String WALLPAPER_IMAGE_DIR = "/data/data/com.android.settings/files";
-    private static final String WALLPAPER_IMAGE = WALLPAPER_IMAGE_DIR + "/wallpaper";
+    private static final String WALLPAPER_IMAGE = WALLPAPER_IMAGE_DIR + "/" + WALLPAPER_IMAGE_FILENAME;
+
     private static final String WALLPAPER_INFO_DIR = "/data/system";
-    private static final String WALLPAPER_INFO = WALLPAPER_INFO_DIR + "/wallpaper_info.xml";
+    private static final String WALLPAPER_INFO = WALLPAPER_INFO_DIR + "/" +  WALLPAPER_INFO_FILENAME;
+
 
     @Override
     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
             ParcelFileDescriptor newState) throws IOException {
         if (oldState == null) {
+            // Ah, it's a full backup dataset, being restored piecemeal.  Just
+            // pop over to the full restore handling and we're done.
             runFullBackup(data);
             return;
         }
@@ -66,11 +75,18 @@
     }
 
     private void runFullBackup(BackupDataOutput output) {
-        // Back up the data files directly
-        FullBackup.backupToTar(getPackageName(), null, null,
-                WALLPAPER_IMAGE_DIR, WALLPAPER_IMAGE, output);
-        FullBackup.backupToTar(getPackageName(), null, null,
+        fullWallpaperBackup(output);
+    }
+
+    private void fullWallpaperBackup(BackupDataOutput output) {
+        // Back up the data files directly.  We do them in this specific order --
+        // info file followed by image -- because then we need take no special
+        // steps during restore; the restore will happen properly when the individual
+        // files are restored piecemeal.
+        FullBackup.backupToTar(getPackageName(), FullBackup.ROOT_TREE_TOKEN, null,
                 WALLPAPER_INFO_DIR, WALLPAPER_INFO, output);
+        FullBackup.backupToTar(getPackageName(), FullBackup.ROOT_TREE_TOKEN, null,
+                WALLPAPER_IMAGE_DIR, WALLPAPER_IMAGE, output);
     }
 
     @Override
@@ -96,4 +112,46 @@
             (new File(WALLPAPER_INFO)).delete();
         }
     }
+
+    @Override
+    public void onRestoreFile(ParcelFileDescriptor data, long size,
+            int type, String domain, String path, long mode, long mtime)
+            throws IOException {
+        Slog.i(TAG, "Restoring file domain=" + domain + " path=" + path);
+
+        // Bits to indicate postprocessing we may need to perform
+        boolean restoredWallpaper = false;
+
+        File outFile = null;
+        // Various domain+files we understand a priori
+        if (domain.equals(FullBackup.ROOT_TREE_TOKEN)) {
+            if (path.equals(WALLPAPER_INFO_FILENAME)) {
+                outFile = new File(WALLPAPER_INFO);
+                restoredWallpaper = true;
+            } else if (path.equals(WALLPAPER_IMAGE_FILENAME)) {
+                outFile = new File(WALLPAPER_IMAGE);
+                restoredWallpaper = true;
+            }
+        }
+
+        try {
+            if (outFile == null) {
+                Slog.w(TAG, "Skipping unrecognized system file: [ " + domain + " : " + path + " ]");
+            }
+            FullBackup.restoreToFile(data, size, type, mode, mtime, outFile);
+
+            if (restoredWallpaper) {
+                WallpaperManagerService wallpaper =
+                        (WallpaperManagerService)ServiceManager.getService(
+                        Context.WALLPAPER_SERVICE);
+                wallpaper.settingsRestored();
+            }
+        } catch (IOException e) {
+            if (restoredWallpaper) {
+                // Make sure we wind up in a good state
+                (new File(WALLPAPER_IMAGE)).delete();
+                (new File(WALLPAPER_INFO)).delete();
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5355d44..4cd601f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -16,19 +16,6 @@
 
 package com.android.server;
 
-import com.android.server.accessibility.AccessibilityManagerService;
-import com.android.server.am.ActivityManagerService;
-import com.android.server.net.NetworkPolicyManagerService;
-import com.android.server.pm.PackageManagerService;
-import com.android.server.usb.UsbService;
-import com.android.server.wm.WindowManagerService;
-import com.android.internal.app.ShutdownThread;
-import com.android.internal.os.BinderInternal;
-import com.android.internal.os.SamplingProfilerIntegration;
-
-import dalvik.system.VMRuntime;
-import dalvik.system.Zygote;
-
 import android.accounts.AccountManagerService;
 import android.app.ActivityManagerNative;
 import android.bluetooth.BluetoothAdapter;
@@ -41,25 +28,34 @@
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.media.AudioService;
-import android.os.Build;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.provider.Contacts.People;
 import android.provider.Settings;
 import android.server.BluetoothA2dpService;
 import android.server.BluetoothService;
 import android.server.search.SearchManagerService;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
-import android.util.Log;
 import android.util.Slog;
-import android.view.Display;
 import android.view.WindowManager;
 
+import com.android.internal.app.ShutdownThread;
+import com.android.internal.os.BinderInternal;
+import com.android.internal.os.SamplingProfilerIntegration;
+import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.net.NetworkPolicyManagerService;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.usb.UsbService;
+import com.android.server.wm.WindowManagerService;
+
+import dalvik.system.VMRuntime;
+import dalvik.system.Zygote;
+
 import java.io.File;
 import java.util.Timer;
 import java.util.TimerTask;
@@ -120,6 +116,7 @@
         LightsService lights = null;
         PowerManagerService power = null;
         BatteryService battery = null;
+        NetworkManagementService networkManagement = null;
         NetworkPolicyManagerService networkPolicy = null;
         ConnectivityService connectivity = null;
         IPackageManager pm = null;
@@ -294,16 +291,15 @@
 
             try {
                 Slog.i(TAG, "NetworkManagement Service");
-                ServiceManager.addService(
-                        Context.NETWORKMANAGEMENT_SERVICE,
-                        NetworkManagementService.create(context));
+                networkManagement = NetworkManagementService.create(context);
+                ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting NetworkManagement Service", e);
             }
 
             try {
                 Slog.i(TAG, "Connectivity Service");
-                connectivity = ConnectivityService.getInstance(context);
+                connectivity = new ConnectivityService(context, networkManagement, networkPolicy);
                 ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting Connectivity Service", e);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index cf5592c..b463e56 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -968,10 +968,12 @@
     static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
     static final int CLEAR_DNS_CACHE = 28;
     static final int UPDATE_HTTP_PROXY = 29;
-    static final int DISPATCH_FOREGROUND_ACTIVITIES_CHANGED = 30;
-    static final int DISPATCH_PROCESS_DIED = 31;
+    static final int SHOW_COMPAT_MODE_DIALOG_MSG = 30;
+    static final int DISPATCH_FOREGROUND_ACTIVITIES_CHANGED = 31;
+    static final int DISPATCH_PROCESS_DIED = 32;
 
     AlertDialog mUidAlert;
+    CompatModeDialog mCompatModeDialog;
 
     final Handler mHandler = new Handler() {
         //public Handler() {
@@ -1270,6 +1272,34 @@
                     sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
                 }
             } break;
+            case SHOW_COMPAT_MODE_DIALOG_MSG: {
+                synchronized (ActivityManagerService.this) {
+                    ActivityRecord ar = (ActivityRecord)msg.obj;
+                    if (mCompatModeDialog != null) {
+                        if (mCompatModeDialog.mAppInfo.packageName.equals(
+                                ar.info.applicationInfo.packageName)) {
+                            return;
+                        }
+                        mCompatModeDialog.dismiss();
+                        mCompatModeDialog = null;
+                    }
+                    if (ar != null) {
+                        if (mCompatModePackages.getPackageAskCompatModeLocked(
+                                ar.packageName)) {
+                            int mode = mCompatModePackages.computeCompatModeLocked(
+                                    ar.info.applicationInfo);
+                            if (mode == ActivityManager.COMPAT_MODE_DISABLED
+                                    || mode == ActivityManager.COMPAT_MODE_ENABLED) {
+                                mCompatModeDialog = new CompatModeDialog(
+                                        ActivityManagerService.this, mContext,
+                                        ar.info.applicationInfo);
+                                mCompatModeDialog.show();
+                            }
+                        }
+                    }
+                }
+                break;
+            }
             case DISPATCH_FOREGROUND_ACTIVITIES_CHANGED: {
                 final ProcessRecord app = (ProcessRecord) msg.obj;
                 final boolean foregroundActivities = msg.arg1 != 0;
@@ -2112,6 +2142,18 @@
         }
     }
 
+    public boolean getPackageAskScreenCompat(String packageName) {
+        synchronized (this) {
+            return mCompatModePackages.getPackageAskCompatModeLocked(packageName);
+        }
+    }
+
+    public void setPackageAskScreenCompat(String packageName, boolean ask) {
+        synchronized (this) {
+            mCompatModePackages.setPackageAskCompatModeLocked(packageName, ask);
+        }
+    }
+
     void reportResumedActivityLocked(ActivityRecord r) {
         //Slog.i(TAG, "**** REPORT RESUME: " + r);
         
@@ -3634,6 +3676,7 @@
             boolean isRestrictedBackupMode = false;
             if (mBackupTarget != null && mBackupAppName.equals(processName)) {
                 isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)
+                        || (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
                         || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
             }
             
@@ -7948,8 +7991,14 @@
         if (dumpAll) {
             pw.println("  mConfigWillChange: " + mMainStack.mConfigWillChange);
             if (mCompatModePackages.getPackages().size() > 0) {
-                pw.print("  mScreenCompatPackages=");
-                pw.println(mCompatModePackages.getPackages());
+                pw.println("  mScreenCompatPackages:");
+                for (Map.Entry<String, Integer> entry
+                        : mCompatModePackages.getPackages().entrySet()) {
+                    String pkg = entry.getKey();
+                    int mode = entry.getValue();
+                    pw.print("    "); pw.print(pkg); pw.print(": ");
+                            pw.print(mode); pw.println();
+                }
             }
         }
         pw.println("  mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown);
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index d5ac19e..af69307 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -485,6 +485,13 @@
         return null;
     }
 
+    final void showAskCompatModeDialogLocked(ActivityRecord r) {
+        Message msg = Message.obtain();
+        msg.what = ActivityManagerService.SHOW_COMPAT_MODE_DIALOG_MSG;
+        msg.obj = r.task.askedCompatMode ? null : r;
+        mService.mHandler.sendMessage(msg);
+    }
+
     final boolean realStartActivityLocked(ActivityRecord r,
             ProcessRecord app, boolean andResume, boolean checkConfig)
             throws RemoteException {
@@ -541,6 +548,7 @@
             mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
             r.sleeping = false;
             r.forceNewConfig = false;
+            showAskCompatModeDialogLocked(r);
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
                     System.identityHashCode(r),
                     r.info, mService.compatibilityInfoForPackageLocked(r.info.applicationInfo),
@@ -1430,6 +1438,7 @@
                         next.task.taskId, next.shortComponentName);
                 
                 next.sleeping = false;
+                showAskCompatModeDialogLocked(next);
                 next.app.thread.scheduleResumeActivity(next,
                         mService.isNextTransitionForward());
                 
diff --git a/services/java/com/android/server/am/BackupRecord.java b/services/java/com/android/server/am/BackupRecord.java
index 6590b91..7e73106 100644
--- a/services/java/com/android/server/am/BackupRecord.java
+++ b/services/java/com/android/server/am/BackupRecord.java
@@ -26,6 +26,7 @@
     public static final int BACKUP_NORMAL = 0;
     public static final int BACKUP_FULL = 1;
     public static final int RESTORE = 2;
+    public static final int RESTORE_FULL = 3;
     
     final BatteryStatsImpl.Uid.Pkg.Serv stats;
     String stringName;                     // cached toString() output
diff --git a/services/java/com/android/server/am/CompatModeDialog.java b/services/java/com/android/server/am/CompatModeDialog.java
new file mode 100644
index 0000000..0442bda
--- /dev/null
+++ b/services/java/com/android/server/am/CompatModeDialog.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.app.ActivityManager;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.view.Gravity;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.Switch;
+
+public class CompatModeDialog extends Dialog {
+    final ActivityManagerService mService;
+    final ApplicationInfo mAppInfo;
+
+    final Switch mCompatEnabled;
+    final CheckBox mAlwaysShow;
+    final View mHint;
+
+    public CompatModeDialog(ActivityManagerService service, Context context,
+            ApplicationInfo appInfo) {
+        super(context, com.android.internal.R.style.Theme_Holo_Dialog_MinWidth);
+        setCancelable(true);
+        setCanceledOnTouchOutside(true);
+        getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+        getWindow().setType(WindowManager.LayoutParams.TYPE_PHONE);
+        getWindow().setGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL);
+        mService = service;
+        mAppInfo = appInfo;
+
+        setContentView(com.android.internal.R.layout.am_compat_mode_dialog);
+        mCompatEnabled = (Switch)findViewById(com.android.internal.R.id.compat_checkbox);
+        mCompatEnabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                synchronized (mService) {
+                    mService.mCompatModePackages.setPackageScreenCompatModeLocked(
+                            mAppInfo.packageName,
+                            mCompatEnabled.isChecked() ? ActivityManager.COMPAT_MODE_ENABLED
+                                    : ActivityManager.COMPAT_MODE_DISABLED);
+                    updateControls();
+                }
+            }
+        });
+        mAlwaysShow = (CheckBox)findViewById(com.android.internal.R.id.ask_checkbox);
+        mAlwaysShow.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                synchronized (mService) {
+                    mService.mCompatModePackages.setPackageAskCompatModeLocked(
+                            mAppInfo.packageName, mAlwaysShow.isChecked());
+                    updateControls();
+                }
+            }
+        });
+        mHint = findViewById(com.android.internal.R.id.reask_hint);
+
+        updateControls();
+    }
+
+    void updateControls() {
+        synchronized (mService) {
+            int mode = mService.mCompatModePackages.computeCompatModeLocked(mAppInfo);
+            mCompatEnabled.setChecked(mode == ActivityManager.COMPAT_MODE_ENABLED);
+            boolean ask = mService.mCompatModePackages.getPackageAskCompatModeLocked(
+                    mAppInfo.packageName);
+            mAlwaysShow.setChecked(ask);
+            mHint.setVisibility(ask ? View.INVISIBLE : View.VISIBLE);
+        }
+    }
+}
diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java
index 1faf8da..1277bca 100644
--- a/services/java/com/android/server/am/CompatModePackages.java
+++ b/services/java/com/android/server/am/CompatModePackages.java
@@ -3,8 +3,10 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Map;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -31,7 +33,12 @@
     private final ActivityManagerService mService;
     private final AtomicFile mFile;
 
-    private final HashSet<String> mPackages = new HashSet<String>();
+    // Compatibility state: no longer ask user to select the mode.
+    public static final int COMPAT_FLAG_DONT_ASK = 1<<0;
+    // Compatibility state: compatibility mode is enabled.
+    public static final int COMPAT_FLAG_ENABLED = 1<<1;
+
+    private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
 
     private static final int MSG_WRITE = 1;
 
@@ -71,7 +78,15 @@
                             if ("pkg".equals(tagName)) {
                                 String pkg = parser.getAttributeValue(null, "name");
                                 if (pkg != null) {
-                                    mPackages.add(pkg);
+                                    String mode = parser.getAttributeValue(null, "mode");
+                                    int modeInt = 0;
+                                    if (mode != null) {
+                                        try {
+                                            modeInt = Integer.parseInt(mode);
+                                        } catch (NumberFormatException e) {
+                                        }
+                                    }
+                                    mPackages.put(pkg, modeInt);
                                 }
                             }
                         }
@@ -93,17 +108,22 @@
         }
     }
 
-    public HashSet<String> getPackages() {
+    public HashMap<String, Integer> getPackages() {
         return mPackages;
     }
 
+    private int getPackageFlags(String packageName) {
+        Integer flags = mPackages.get(packageName);
+        return flags != null ? flags : 0;
+    }
+
     public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
         return new CompatibilityInfo(ai, mService.mConfiguration.screenLayout,
-                mPackages.contains(ai.packageName));
+                (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0);
     }
 
-    private int computeCompatModeLocked(ApplicationInfo ai) {
-        boolean enabled = mPackages.contains(ai.packageName);
+    public int computeCompatModeLocked(ApplicationInfo ai) {
+        boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0;
         CompatibilityInfo info = new CompatibilityInfo(ai,
                 mService.mConfiguration.screenLayout, enabled);
         if (info.alwaysSupportsScreen()) {
@@ -116,6 +136,40 @@
                 : ActivityManager.COMPAT_MODE_DISABLED;
     }
 
+    public boolean getFrontActivityAskCompatModeLocked() {
+        ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
+        if (r == null) {
+            return false;
+        }
+        return getPackageAskCompatModeLocked(r.packageName);
+    }
+
+    public boolean getPackageAskCompatModeLocked(String packageName) {
+        return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0;
+    }
+
+    public void setFrontActivityAskCompatModeLocked(boolean ask) {
+        ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
+        if (r != null) {
+            setPackageAskCompatModeLocked(r.packageName, ask);
+        }
+    }
+
+    public void setPackageAskCompatModeLocked(String packageName, boolean ask) {
+        int curFlags = getPackageFlags(packageName);
+        int newFlags = ask ? (curFlags&~COMPAT_FLAG_DONT_ASK) : (curFlags|COMPAT_FLAG_DONT_ASK);
+        if (curFlags != newFlags) {
+            if (newFlags != 0) {
+                mPackages.put(packageName, newFlags);
+            } else {
+                mPackages.remove(packageName);
+            }
+            mHandler.removeMessages(MSG_WRITE);
+            Message msg = mHandler.obtainMessage(MSG_WRITE);
+            mHandler.sendMessageDelayed(msg, 10000);
+        }
+    }
+
     public int getFrontActivityScreenCompatModeLocked() {
         ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null);
         if (r == null) {
@@ -161,7 +215,8 @@
     private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) {
         final String packageName = ai.packageName;
 
-        boolean changed = false;
+        int curFlags = getPackageFlags(packageName);
+
         boolean enable;
         switch (mode) {
             case ActivityManager.COMPAT_MODE_DISABLED:
@@ -171,24 +226,26 @@
                 enable = true;
                 break;
             case ActivityManager.COMPAT_MODE_TOGGLE:
-                enable = !mPackages.contains(packageName);
+                enable = (curFlags&COMPAT_FLAG_ENABLED) == 0;
                 break;
             default:
                 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring");
                 return;
         }
+
+        int newFlags = curFlags;
         if (enable) {
-            if (!mPackages.contains(packageName)) {
-                changed = true;
-                mPackages.add(packageName);
-            }
+            newFlags |= COMPAT_FLAG_ENABLED;
         } else {
-            if (mPackages.contains(packageName)) {
-                changed = true;
+            newFlags &= ~COMPAT_FLAG_ENABLED;
+        }
+
+        if (newFlags != curFlags) {
+            if (newFlags != 0) {
+                mPackages.put(packageName, newFlags);
+            } else {
                 mPackages.remove(packageName);
             }
-        }
-        if (changed) {
             CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
             if (ci.alwaysSupportsScreen()) {
                 Slog.w(TAG, "Ignoring compat mode change of " + packageName
@@ -241,9 +298,9 @@
     }
 
     void saveCompatModes() {
-        HashSet<String> pkgs;
+        HashMap<String, Integer> pkgs;
         synchronized (mService) {
-            pkgs = new HashSet<String>(mPackages);
+            pkgs = new HashMap<String, Integer>(mPackages);
         }
 
         FileOutputStream fos = null;
@@ -258,9 +315,14 @@
 
             final IPackageManager pm = AppGlobals.getPackageManager();
             final int screenLayout = mService.mConfiguration.screenLayout;
-            final Iterator<String> it = pkgs.iterator();
+            final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator();
             while (it.hasNext()) {
-                String pkg = it.next();
+                Map.Entry<String, Integer> entry = it.next();
+                String pkg = entry.getKey();
+                int mode = entry.getValue();
+                if (mode == 0) {
+                    continue;
+                }
                 ApplicationInfo ai = null;
                 try {
                     ai = pm.getApplicationInfo(pkg, 0);
@@ -278,6 +340,7 @@
                 }
                 out.startTag(null, "pkg");
                 out.attribute(null, "name", pkg);
+                out.attribute(null, "mode", Integer.toString(mode));
                 out.endTag(null, "pkg");
             }
 
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index e8c87e1..e61a7f4 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -34,6 +34,7 @@
     long lastActiveTime;    // Last time this task was active, including sleep.
     boolean rootWasReset;   // True if the intent at the root of the task had
                             // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
+    boolean askedCompatMode;// Have asked the user about compat mode for this task.
 
     String stringName;      // caching of toString() result.
     
@@ -112,6 +113,9 @@
             pw.print(prefix); pw.print("realActivity=");
             pw.println(realActivity.flattenToShortString());
         }
+        if (!askedCompatMode) {
+            pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode);
+        }
         pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
                 pw.print(" (inactive for ");
                 pw.print((getInactiveDuration()/1000)); pw.println("s)");
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index d083d01..1ae8284 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -16,11 +16,15 @@
 
 package com.android.server.net;
 
+import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.MANAGE_APP_TOKENS;
 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
-import static android.net.NetworkPolicyManager.POLICY_REJECT_BACKGROUND;
-import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
+import static android.net.NetworkPolicyManager.dumpPolicy;
+import static android.net.NetworkPolicyManager.dumpRules;
 
 import android.app.IActivityManager;
 import android.app.IProcessObserver;
@@ -28,8 +32,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.os.IPowerManager;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Slog;
@@ -37,9 +44,16 @@
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 /**
  * Service that maintains low-level network policy rules and collects usage
  * statistics to drive those rules.
+ * <p>
+ * Derives active rules by combining a given policy with other system status,
+ * and delivers to listeners, such as {@link ConnectivityManager}, for
+ * enforcement.
  */
 public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
     private static final String TAG = "NetworkPolicy";
@@ -51,19 +65,27 @@
 
     private Object mRulesLock = new Object();
 
-    private boolean mScreenOn = false;
+    private boolean mScreenOn;
 
     /** Current network policy for each UID. */
     private SparseIntArray mUidPolicy = new SparseIntArray();
+    /** Current derived network rules for each UID. */
+    private SparseIntArray mUidRules = new SparseIntArray();
 
     /** Foreground at both UID and PID granularity. */
     private SparseBooleanArray mUidForeground = new SparseBooleanArray();
     private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
             SparseBooleanArray>();
 
+    private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<
+            INetworkPolicyListener>();
+
     // TODO: periodically poll network stats and write to disk
     // TODO: save/restore policy information from disk
 
+    // TODO: keep whitelist of system-critical services that should never have
+    // rules enforced, such as system, phone, and radio UIDs.
+
     public NetworkPolicyManagerService(
             Context context, IActivityManager activityManager, IPowerManager powerManager) {
         mContext = checkNotNull(context, "missing context");
@@ -158,12 +180,18 @@
 
     @Override
     public void setUidPolicy(int uid, int policy) {
+        // TODO: create permission for modifying data policy
         mContext.enforceCallingOrSelfPermission(
                 UPDATE_DEVICE_STATS, "requires UPDATE_DEVICE_STATS permission");
 
+        final int oldPolicy;
         synchronized (mRulesLock) {
+            oldPolicy = getUidPolicy(uid);
             mUidPolicy.put(uid, policy);
+            updateRulesForUidL(uid);
         }
+
+        // TODO: consider dispatching BACKGROUND_DATA_SETTING broadcast
     }
 
     @Override
@@ -173,6 +201,86 @@
         }
     }
 
+    @Override
+    public void registerListener(INetworkPolicyListener listener) {
+        mListeners.register(listener);
+
+        synchronized (mRulesLock) {
+            // dispatch any existing rules to new listeners
+            final int size = mUidRules.size();
+            for (int i = 0; i < size; i++) {
+                final int uid = mUidRules.keyAt(i);
+                final int uidRules = mUidRules.valueAt(i);
+                if (uidRules != RULE_ALLOW_ALL) {
+                    try {
+                        listener.onRulesChanged(uid, uidRules);
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void unregisterListener(INetworkPolicyListener listener) {
+        mListeners.unregister(listener);
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+        mContext.enforceCallingOrSelfPermission(DUMP, "requires DUMP permission");
+
+        synchronized (mRulesLock) {
+            fout.println("Policy status for known UIDs:");
+
+            final SparseBooleanArray knownUids = new SparseBooleanArray();
+            collectKeys(mUidPolicy, knownUids);
+            collectKeys(mUidForeground, knownUids);
+            collectKeys(mUidRules, knownUids);
+
+            final int size = knownUids.size();
+            for (int i = 0; i < size; i++) {
+                final int uid = knownUids.keyAt(i);
+                fout.print("  UID=");
+                fout.print(uid);
+
+                fout.print(" policy=");
+                final int policyIndex = mUidPolicy.indexOfKey(uid);
+                if (policyIndex < 0) {
+                    fout.print("UNKNOWN");
+                } else {
+                    dumpPolicy(fout, mUidPolicy.valueAt(policyIndex));
+                }
+
+                fout.print(" foreground=");
+                final int foregroundIndex = mUidPidForeground.indexOfKey(uid);
+                if (foregroundIndex < 0) {
+                    fout.print("UNKNOWN");
+                } else {
+                    dumpSparseBooleanArray(fout, mUidPidForeground.valueAt(foregroundIndex));
+                }
+
+                fout.print(" rules=");
+                final int rulesIndex = mUidRules.indexOfKey(uid);
+                if (rulesIndex < 0) {
+                    fout.print("UNKNOWN");
+                } else {
+                    dumpRules(fout, mUidRules.valueAt(rulesIndex));
+                }
+
+                fout.println();
+            }
+        }
+    }
+
+    @Override
+    public boolean isUidForeground(int uid) {
+        synchronized (mRulesLock) {
+            // only really in foreground when screen is also on
+            return mUidForeground.get(uid, false) && mScreenOn;
+        }
+    }
+
     /**
      * Foreground for PID changed; recompute foreground at UID level. If
      * changed, will trigger {@link #updateRulesForUidL(int)}.
@@ -223,22 +331,33 @@
     }
 
     private void updateRulesForUidL(int uid) {
-        // only really in foreground when screen on
-        final boolean uidForeground = mUidForeground.get(uid, false) && mScreenOn;
         final int uidPolicy = getUidPolicy(uid);
+        final boolean uidForeground = isUidForeground(uid);
 
-        if (LOGD) {
-            Log.d(TAG, "updateRulesForUid(uid=" + uid + ") found foreground=" + uidForeground
-                    + " and policy=" + uidPolicy);
+        // derive active rules based on policy and active state
+        int uidRules = RULE_ALLOW_ALL;
+        if (!uidForeground && (uidPolicy & POLICY_REJECT_PAID_BACKGROUND) != 0) {
+            // uid in background, and policy says to block paid data
+            uidRules = RULE_REJECT_PAID;
         }
 
-        if (!uidForeground && (uidPolicy & POLICY_REJECT_BACKGROUND) != 0) {
-            // TODO: build updated rules and push to NMS
-        } else if ((uidPolicy & POLICY_REJECT_PAID) != 0) {
-            // TODO: build updated rules and push to NMS
-        } else {
-            // TODO: build updated rules and push to NMS
+        // TODO: only dispatch when rules actually change
+
+        // record rule locally to dispatch to new listeners
+        mUidRules.put(uid, uidRules);
+
+        // dispatch changed rule to existing listeners
+        final int length = mListeners.beginBroadcast();
+        for (int i = 0; i < length; i++) {
+            final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+            if (listener != null) {
+                try {
+                    listener.onRulesChanged(uid, uidRules);
+                } catch (RemoteException e) {
+                }
+            }
         }
+        mListeners.finishBroadcast();
     }
 
     private static <T> T checkNotNull(T value, String message) {
@@ -247,4 +366,28 @@
         }
         return value;
     }
+    
+    private static void collectKeys(SparseIntArray source, SparseBooleanArray target) {
+        final int size = source.size();
+        for (int i = 0; i < size; i++) {
+            target.put(source.keyAt(i), true);
+        }
+    }
+
+    private static void collectKeys(SparseBooleanArray source, SparseBooleanArray target) {
+        final int size = source.size();
+        for (int i = 0; i < size; i++) {
+            target.put(source.keyAt(i), true);
+        }
+    }
+
+    private static void dumpSparseBooleanArray(PrintWriter fout, SparseBooleanArray value) {
+        fout.print("[");
+        final int size = value.size();
+        for (int i = 0; i < size; i++) {
+            fout.print(value.keyAt(i) + "=" + value.valueAt(i));
+            if (i < size - 1) fout.print(",");
+        }
+        fout.print("]");
+    }
 }
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index f8d1426..295d569 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -24,7 +24,9 @@
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-    
+    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
+    <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
+
     <application>
         <uses-library android:name="android.test.runner" />
 
diff --git a/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java b/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
new file mode 100644
index 0000000..fe88793
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.AbstractFuture;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Future;
+
+/**
+ * {@link ContextWrapper} that can attach listeners for upcoming
+ * {@link Context#sendBroadcast(Intent)}.
+ */
+public class BroadcastInterceptingContext extends ContextWrapper {
+    private static final String TAG = "WatchingContext";
+
+    private final List<BroadcastInterceptor> mInterceptors = Lists.newArrayList();
+
+    public class BroadcastInterceptor extends AbstractFuture<Intent> {
+        private final BroadcastReceiver mReceiver;
+        private final IntentFilter mFilter;
+
+        public BroadcastInterceptor(BroadcastReceiver receiver, IntentFilter filter) {
+            mReceiver = receiver;
+            mFilter = filter;
+        }
+
+        public boolean dispatchBroadcast(Intent intent) {
+            if (mFilter.match(getContentResolver(), intent, false, TAG) > 0) {
+                if (mReceiver != null) {
+                    final Context context = BroadcastInterceptingContext.this;
+                    mReceiver.onReceive(context, intent);
+                    return false;
+                } else {
+                    set(intent);
+                    return true;
+                }
+            } else {
+                return false;
+            }
+        }
+    }
+
+    public BroadcastInterceptingContext(Context base) {
+        super(base);
+    }
+
+    public Future<Intent> nextBroadcastIntent(String action) {
+        return nextBroadcastIntent(new IntentFilter(action));
+    }
+
+    public Future<Intent> nextBroadcastIntent(IntentFilter filter) {
+        final BroadcastInterceptor interceptor = new BroadcastInterceptor(null, filter);
+        synchronized (mInterceptors) {
+            mInterceptors.add(interceptor);
+        }
+        return interceptor;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        synchronized (mInterceptors) {
+            mInterceptors.add(new BroadcastInterceptor(receiver, filter));
+        }
+        return null;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler) {
+        return registerReceiver(receiver, filter);
+    }
+
+    @Override
+    public void unregisterReceiver(BroadcastReceiver receiver) {
+        synchronized (mInterceptors) {
+            final Iterator<BroadcastInterceptor> i = mInterceptors.iterator();
+            while (i.hasNext()) {
+                final BroadcastInterceptor interceptor = i.next();
+                if (receiver.equals(interceptor.mReceiver)) {
+                    i.remove();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent) {
+        synchronized (mInterceptors) {
+            final Iterator<BroadcastInterceptor> i = mInterceptors.iterator();
+            while (i.hasNext()) {
+                final BroadcastInterceptor interceptor = i.next();
+                if (interceptor.dispatchBroadcast(intent)) {
+                    i.remove();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void sendStickyBroadcast(Intent intent) {
+        sendBroadcast(intent);
+    }
+
+    @Override
+    public void removeStickyBroadcast(Intent intent) {
+        // ignored
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
new file mode 100644
index 0000000..cf1171f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+
+import android.app.IActivityManager;
+import android.app.IProcessObserver;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.INetworkPolicyListener;
+import android.os.Binder;
+import android.os.IPowerManager;
+import android.test.AndroidTestCase;
+import android.test.mock.MockPackageManager;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
+
+import com.android.server.net.NetworkPolicyManagerService;
+
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+
+import java.util.concurrent.Future;
+
+/**
+ * Tests for {@link NetworkPolicyManagerService}.
+ */
+@LargeTest
+public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
+    private static final String TAG = "NetworkPolicyManagerServiceTest";
+
+    private BroadcastInterceptingContext mServiceContext;
+
+    private IActivityManager mActivityManager;
+    private IPowerManager mPowerManager;
+    private INetworkPolicyListener mPolicyListener;
+
+    private NetworkPolicyManagerService mService;
+    private IProcessObserver mProcessObserver;
+
+    private Binder mStubBinder = new Binder();
+
+    private static final int UID_A = 800;
+    private static final int UID_B = 801;
+
+    private static final int PID_1 = 400;
+    private static final int PID_2 = 401;
+    private static final int PID_3 = 402;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        // intercept various broadcasts, and pretend that uids have packages
+        mServiceContext = new BroadcastInterceptingContext(getContext()) {
+            @Override
+            public PackageManager getPackageManager() {
+                return new MockPackageManager() {
+                    @Override
+                    public String[] getPackagesForUid(int uid) {
+                        return new String[] { "com.example" };
+                    }
+                };
+            }
+        };
+
+        mActivityManager = createMock(IActivityManager.class);
+        mPowerManager = createMock(IPowerManager.class);
+        mPolicyListener = createMock(INetworkPolicyListener.class);
+
+        mService = new NetworkPolicyManagerService(
+                mServiceContext, mActivityManager, mPowerManager);
+
+        // RemoteCallbackList needs a binder to use as key
+        expect(mPolicyListener.asBinder()).andReturn(mStubBinder).atLeastOnce();
+        replay();
+        mService.registerListener(mPolicyListener);
+        verifyAndReset();
+
+        // catch the registered IProcessObserver during systemReady()
+        final Capture<IProcessObserver> processObserver = new Capture<IProcessObserver>();
+        mActivityManager.registerProcessObserver(capture(processObserver));
+        expectLastCall().atLeastOnce();
+
+        // expect to answer screen status during systemReady()
+        expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
+
+        replay();
+        mService.systemReady();
+        verifyAndReset();
+
+        mProcessObserver = processObserver.getValue();
+
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mServiceContext = null;
+
+        mActivityManager = null;
+        mPowerManager = null;
+        mPolicyListener = null;
+
+        mService = null;
+        mProcessObserver = null;
+
+        super.tearDown();
+    }
+
+    @Suppress
+    public void testPolicyChangeTriggersBroadcast() throws Exception {
+        mService.setUidPolicy(UID_A, POLICY_NONE);
+
+        // change background policy and expect broadcast
+        final Future<Intent> backgroundChanged = mServiceContext.nextBroadcastIntent(
+                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
+
+        mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
+
+        backgroundChanged.get();
+    }
+
+    public void testPidForegroundCombined() throws Exception {
+        // push all uid into background
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
+        mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, false);
+        assertFalse(mService.isUidForeground(UID_A));
+        assertFalse(mService.isUidForeground(UID_B));
+
+        // push one of the shared pids into foreground
+        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
+        assertTrue(mService.isUidForeground(UID_A));
+        assertFalse(mService.isUidForeground(UID_B));
+
+        // and swap another uid into foreground
+        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
+        mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, true);
+        assertFalse(mService.isUidForeground(UID_A));
+        assertTrue(mService.isUidForeground(UID_B));
+
+        // push both pid into foreground
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
+        assertTrue(mService.isUidForeground(UID_A));
+
+        // pull one out, should still be foreground
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        assertTrue(mService.isUidForeground(UID_A));
+
+        // pull final pid out, should now be background
+        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
+        assertFalse(mService.isUidForeground(UID_A));
+    }
+
+    public void testScreenChangesRules() throws Exception {
+        // push strict policy for foreground uid, verify ALLOW rule
+        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+        mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
+        verifyAndReset();
+
+        // now turn screen off and verify REJECT rule
+        expect(mPowerManager.isScreenOn()).andReturn(false).atLeastOnce();
+        expectRulesChanged(UID_A, RULE_REJECT_PAID);
+        replay();
+        mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
+        verifyAndReset();
+
+        // and turn screen back on, verify ALLOW rule restored
+        expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
+        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
+        verifyAndReset();
+    }
+
+    public void testPolicyNone() throws Exception {
+        // POLICY_NONE should RULE_ALLOW in foreground
+        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mService.setUidPolicy(UID_A, POLICY_NONE);
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+        verifyAndReset();
+
+        // POLICY_NONE should RULE_ALLOW in background
+        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        verifyAndReset();
+    }
+
+    public void testPolicyReject() throws Exception {
+        // POLICY_REJECT should RULE_ALLOW in background
+        expectRulesChanged(UID_A, RULE_REJECT_PAID);
+        replay();
+        mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
+        verifyAndReset();
+
+        // POLICY_REJECT should RULE_ALLOW in foreground
+        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+        verifyAndReset();
+
+        // POLICY_REJECT should RULE_REJECT in background
+        expectRulesChanged(UID_A, RULE_REJECT_PAID);
+        replay();
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        verifyAndReset();
+    }
+
+    public void testPolicyRejectAddRemove() throws Exception {
+        // POLICY_NONE should have RULE_ALLOW in background
+        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mService.setUidPolicy(UID_A, POLICY_NONE);
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        verifyAndReset();
+
+        // adding POLICY_REJECT should cause RULE_REJECT
+        expectRulesChanged(UID_A, RULE_REJECT_PAID);
+        replay();
+        mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
+        verifyAndReset();
+
+        // removing POLICY_REJECT should return us to RULE_ALLOW
+        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mService.setUidPolicy(UID_A, POLICY_NONE);
+        verifyAndReset();
+    }
+
+    private void expectRulesChanged(int uid, int policy) throws Exception {
+        mPolicyListener.onRulesChanged(eq(uid), eq(policy));
+        expectLastCall().atLeastOnce();
+    }
+
+    private void replay() {
+        EasyMock.replay(mActivityManager, mPowerManager, mPolicyListener);
+    }
+
+    private void verifyAndReset() {
+        EasyMock.verify(mActivityManager, mPowerManager, mPolicyListener);
+        EasyMock.reset(mActivityManager, mPowerManager, mPolicyListener);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
index ca33d32..d1ee4f6 100644
--- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
@@ -25,14 +25,9 @@
 import static org.easymock.EasyMock.reset;
 import static org.easymock.EasyMock.verify;
 
-import com.google.common.collect.Lists;
-import com.google.common.util.concurrent.AbstractFuture;
-
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.net.INetworkManagementEventObserver;
 import android.net.NetworkStats;
 import android.net.ThrottleManager;
@@ -48,8 +43,6 @@
 import android.util.Log;
 import android.util.TrustedTime;
 
-import java.util.Iterator;
-import java.util.List;
 import java.util.concurrent.Future;
 
 /**
@@ -66,7 +59,7 @@
 
     private static final String TEST_IFACE = "test0";
 
-    private WatchingContext mWatchingContext;
+    private BroadcastInterceptingContext mWatchingContext;
     private INetworkManagementService mMockNMService;
     private TrustedTime mMockTime;
 
@@ -76,7 +69,7 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mWatchingContext = new WatchingContext(getContext());
+        mWatchingContext = new BroadcastInterceptingContext(getContext());
 
         mMockNMService = createMock(INetworkManagementService.class);
         mMockTime = createMock(TrustedTime.class);
@@ -354,69 +347,4 @@
 
         pollAction.get();
     }
-
-
-    /**
-     * {@link ContextWrapper} that can attach listeners for upcoming
-     * {@link Context#sendBroadcast(Intent)}.
-     */
-    private static class WatchingContext extends ContextWrapper {
-        private List<LocalBroadcastReceiver> mReceivers = Lists.newArrayList();
-
-        public class LocalBroadcastReceiver extends AbstractFuture<Intent> {
-            private IntentFilter mFilter;
-
-            public LocalBroadcastReceiver(IntentFilter filter) {
-                mFilter = filter;
-            }
-
-            public boolean dispatchBroadcast(Intent intent) {
-                if (mFilter.match(getContentResolver(), intent, false, TAG) > 0) {
-                    set(intent);
-                    return true;
-                } else {
-                    return false;
-                }
-            }
-        }
-
-        public WatchingContext(Context base) {
-            super(base);
-        }
-
-        public Future<Intent> nextBroadcastIntent(String action) {
-            return nextBroadcastIntent(new IntentFilter(action));
-        }
-
-        public Future<Intent> nextBroadcastIntent(IntentFilter filter) {
-            final LocalBroadcastReceiver receiver = new LocalBroadcastReceiver(filter);
-            synchronized (mReceivers) {
-                mReceivers.add(receiver);
-            }
-            return receiver;
-        }
-
-        @Override
-        public void sendBroadcast(Intent intent) {
-            synchronized (mReceivers) {
-                final Iterator<LocalBroadcastReceiver> i = mReceivers.iterator();
-                while (i.hasNext()) {
-                    final LocalBroadcastReceiver receiver = i.next();
-                    if (receiver.dispatchBroadcast(intent)) {
-                        i.remove();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void sendStickyBroadcast(Intent intent) {
-            sendBroadcast(intent);
-        }
-
-        @Override
-        public void removeStickyBroadcast(Intent intent) {
-            // ignored
-        }
-    }
 }
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 3636baa..e8d4f98 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -62,6 +62,7 @@
  */
 public abstract class DataConnection extends StateMachine {
     protected static final boolean DBG = true;
+    protected static final boolean VDBG = false;
 
     protected static Object mCountLock = new Object();
     protected static int mCount;
@@ -290,7 +291,7 @@
             lastFailTime = timeStamp;
             AsyncResult.forMessage(connectionCompletedMsg, cause, new Exception());
         }
-        if (DBG) log("notifyConnection at " + timeStamp + " cause=" + cause);
+        if (DBG) log("notifyConnectionCompleted at " + timeStamp + " cause=" + cause);
 
         connectionCompletedMsg.sendToTarget();
     }
@@ -301,12 +302,14 @@
      * @param dp is the DisconnectParams.
      */
     private void notifyDisconnectCompleted(DisconnectParams dp) {
-        if (DBG) log("NotifyDisconnectCompleted");
+        if (VDBG) log("NotifyDisconnectCompleted");
 
         if (dp.onCompletedMsg != null) {
             Message msg = dp.onCompletedMsg;
-            log(String.format("msg=%s msg.obj=%s", msg.toString(),
+            if (VDBG) {
+                log(String.format("msg=%s msg.obj=%s", msg.toString(),
                     ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
+            }
             AsyncResult.forMessage(msg);
             msg.sendToTarget();
         }
@@ -472,7 +475,7 @@
             result = DataCallState.SetupResult.ERR_RilError;
             result.mFailCause = FailCause.fromInt(response.status);
         } else {
-            log("onSetupConnectionCompleted received DataCallState: " + response);
+            if (DBG) log("onSetupConnectionCompleted received DataCallState: " + response);
             cid = response.cid;
             // set link properties based on data call response
             result = setLinkProperties(response, mLinkProperties);
@@ -558,53 +561,53 @@
             switch (msg.what) {
                 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
                     if (mAc != null) {
-                        log("Disconnecting to previous connection mAc=" + mAc);
+                        if (VDBG) log("Disconnecting to previous connection mAc=" + mAc);
                         mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
                                 AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
                     } else {
                         mAc = new AsyncChannel();
                         mAc.connected(null, getHandler(), msg.replyTo);
-                        log("DcDefaultState: FULL_CONNECTION reply connected");
+                        if (VDBG) log("DcDefaultState: FULL_CONNECTION reply connected");
                         mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
                                 AsyncChannel.STATUS_SUCCESSFUL, mId, "hi");
                     }
                     break;
                 }
                 case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
-                    log("CMD_CHANNEL_DISCONNECT");
+                    if (VDBG) log("CMD_CHANNEL_DISCONNECT");
                     mAc.disconnect();
                     break;
                 }
                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
-                    log("CMD_CHANNEL_DISCONNECTED");
+                    if (VDBG) log("CMD_CHANNEL_DISCONNECTED");
                     mAc = null;
                     break;
                 }
                 case DataConnectionAc.REQ_IS_INACTIVE: {
                     boolean val = getCurrentState() == mInactiveState;
-                    log("REQ_IS_INACTIVE  isInactive=" + val);
+                    if (VDBG) log("REQ_IS_INACTIVE  isInactive=" + val);
                     mAc.replyToMessage(msg, DataConnectionAc.RSP_IS_INACTIVE, val ? 1 : 0);
                     break;
                 }
                 case DataConnectionAc.REQ_GET_CID: {
-                    log("REQ_GET_CID  cid=" + cid);
+                    if (VDBG) log("REQ_GET_CID  cid=" + cid);
                     mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_CID, cid);
                     break;
                 }
                 case DataConnectionAc.REQ_GET_APNSETTING: {
-                    log("REQ_GET_APNSETTING  apnSetting=" + mApn);
+                    if (VDBG) log("REQ_GET_APNSETTING  apnSetting=" + mApn);
                     mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNSETTING, mApn);
                     break;
                 }
                 case DataConnectionAc.REQ_GET_LINK_PROPERTIES: {
                     LinkProperties lp = new LinkProperties(mLinkProperties);
-                    log("REQ_GET_LINK_PROPERTIES linkProperties" + lp);
+                    if (VDBG) log("REQ_GET_LINK_PROPERTIES linkProperties" + lp);
                     mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_PROPERTIES, lp);
                     break;
                 }
                 case DataConnectionAc.REQ_SET_LINK_PROPERTIES_HTTP_PROXY: {
                     ProxyProperties proxy = (ProxyProperties) msg.obj;
-                    log("REQ_SET_LINK_PROPERTIES_HTTP_PROXY proxy=" + proxy);
+                    if (VDBG) log("REQ_SET_LINK_PROPERTIES_HTTP_PROXY proxy=" + proxy);
                     mLinkProperties.setHttpProxy(proxy);
                     mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_LINK_PROPERTIES_HTTP_PROXY);
                     break;
@@ -612,7 +615,7 @@
                 case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: {
                     DataCallState newState = (DataCallState) msg.obj;
                     DataConnectionAc.LinkPropertyChangeAction action = updateLinkProperty(newState);
-                    if (DBG) {
+                    if (VDBG) {
                         log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE action="
                             + action + " newState=" + newState);
                     }
@@ -623,18 +626,18 @@
                 }
                 case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: {
                     LinkCapabilities lc = new LinkCapabilities(mCapabilities);
-                    log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc);
+                    if (VDBG) log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc);
                     mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_CAPABILITIES, lc);
                     break;
                 }
                 case DataConnectionAc.REQ_RESET:
-                    if (DBG) log("DcDefaultState: msg.what=REQ_RESET");
+                    if (VDBG) log("DcDefaultState: msg.what=REQ_RESET");
                     clearSettings();
                     mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
                     transitionTo(mInactiveState);
                     break;
                 case DataConnectionAc.REQ_GET_REFCOUNT: {
-                    log("REQ_GET_REFCOUNT  refCount=" + mRefCount);
+                    if (VDBG) log("REQ_GET_REFCOUNT  refCount=" + mRefCount);
                     mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_REFCOUNT, mRefCount);
                     break;
                 }
@@ -666,7 +669,8 @@
 
                 default:
                     if (DBG) {
-                        log("DcDefaultState: shouldn't happen but ignore msg.what=" + msg.what);
+                        log("DcDefaultState: shouldn't happen but ignore msg.what=0x" +
+                                Integer.toHexString(msg.what));
                     }
                     break;
             }
@@ -685,13 +689,13 @@
         private DisconnectParams mDisconnectParams = null;
 
         public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
-            log("DcInactiveState: setEnterNoticationParams cp,cause");
+            if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause");
             mConnectionParams = cp;
             mFailCause = cause;
         }
 
         public void setEnterNotificationParams(DisconnectParams dp) {
-          log("DcInactiveState: setEnterNoticationParams dp");
+            if (VDBG) log("DcInactiveState: setEnterNoticationParams dp");
             mDisconnectParams = dp;
         }
 
@@ -707,11 +711,11 @@
              * call to isInactive.
              */
             if ((mConnectionParams != null) && (mFailCause != null)) {
-                log("DcInactiveState: enter notifyConnectCompleted");
+                if (VDBG) log("DcInactiveState: enter notifyConnectCompleted");
                 notifyConnectCompleted(mConnectionParams, mFailCause);
             }
             if (mDisconnectParams != null) {
-              log("DcInactiveState: enter notifyDisconnectCompleted");
+                if (VDBG) log("DcInactiveState: enter notifyDisconnectCompleted");
                 notifyDisconnectCompleted(mDisconnectParams);
             }
         }
@@ -751,7 +755,10 @@
                     break;
 
                 default:
-                    if (DBG) log("DcInactiveState nothandled msg.what=" + msg.what);
+                    if (VDBG) {
+                        log("DcInactiveState nothandled msg.what=0x" +
+                                Integer.toHexString(msg.what));
+                    }
                     retVal = NOT_HANDLED;
                     break;
             }
@@ -856,7 +863,10 @@
                     break;
 
                 default:
-                    if (DBG) log("DcActivatingState not handled msg.what=" + msg.what);
+                    if (VDBG) {
+                        log("DcActivatingState not handled msg.what=0x" +
+                                Integer.toHexString(msg.what));
+                    }
                     retVal = NOT_HANDLED;
                     break;
             }
@@ -873,7 +883,7 @@
         private FailCause mFailCause = null;
 
         public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
-            log("DcInactiveState: setEnterNoticationParams cp,cause");
+            if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause");
             mConnectionParams = cp;
             mFailCause = cause;
         }
@@ -887,7 +897,7 @@
              * call to isActive.
              */
             if ((mConnectionParams != null) && (mFailCause != null)) {
-                log("DcActiveState: enter notifyConnectCompleted");
+                if (VDBG) log("DcActiveState: enter notifyConnectCompleted");
                 notifyConnectCompleted(mConnectionParams, mFailCause);
             }
         }
@@ -930,7 +940,10 @@
                     break;
 
                 default:
-                    if (DBG) log("DcActiveState nothandled msg.what=" + msg.what);
+                    if (VDBG) {
+                        log("DcActiveState not handled msg.what=0x" +
+                                Integer.toHexString(msg.what));
+                    }
                     retVal = NOT_HANDLED;
                     break;
             }
@@ -972,7 +985,10 @@
                     break;
 
                 default:
-                    if (DBG) log("DcDisconnectingState not handled msg.what=" + msg.what);
+                    if (VDBG) {
+                        log("DcDisconnectingState not handled msg.what=0x" +
+                                Integer.toHexString(msg.what));
+                    }
                     retVal = NOT_HANDLED;
                     break;
             }
@@ -1014,9 +1030,9 @@
                     break;
 
                 default:
-                    if (DBG) {
-                        log("DcDisconnectionErrorCreatingConnection not handled msg.what="
-                                + msg.what);
+                    if (VDBG) {
+                        log("DcDisconnectionErrorCreatingConnection not handled msg.what=0x"
+                                + Integer.toHexString(msg.what));
                     }
                     retVal = NOT_HANDLED;
                     break;
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
index a0d9b0f..62b90ae 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
@@ -28,7 +28,7 @@
  * AsyncChannel to a DataConnection
  */
 public class DataConnectionAc extends AsyncChannel {
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
     private String mLogTag;
 
     public DataConnection dataConnection;
@@ -367,7 +367,7 @@
         if ((response != null) && (response.what == RSP_RESET)) {
             if (DBG) log("restSync ok");
         } else {
-            if (DBG) log("restSync error response=" + response);
+            log("restSync error response=" + response);
         }
     }
 
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 0621cfd..2c04b30 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -2888,7 +2888,7 @@
 
         String s = p.readString();
 
-        if (RILJ_LOGD) riljLog("< iccIO: "
+        if (RILJ_LOGV) riljLog("< iccIO: "
                 + " 0x" + Integer.toHexString(sw1)
                 + " 0x" + Integer.toHexString(sw2) + " "
                 + s);
@@ -3051,7 +3051,7 @@
     responseSetupDataCall(Parcel p) {
         int ver = p.readInt();
         int num = p.readInt();
-        if (RILJ_LOGD) riljLog("responseSetupDataCall ver=" + ver + " num=" + num);
+        if (RILJ_LOGV) riljLog("responseSetupDataCall ver=" + ver + " num=" + num);
 
         DataCallState dataCall;
 
diff --git a/tests/BiDiTests/AndroidManifest.xml b/tests/BiDiTests/AndroidManifest.xml
index e328b5b..b3d8893 100644
--- a/tests/BiDiTests/AndroidManifest.xml
+++ b/tests/BiDiTests/AndroidManifest.xml
@@ -19,7 +19,7 @@
     android:versionCode="1"
     android:versionName="1.0">
 
-    <application android:label="BiDiTests">
+    <application android:label="BiDiTests" android:hardwareAccelerated="true">
 
         <activity android:name=".BiDiTestActivity"
             android:windowSoftInputMode="stateAlwaysHidden">
@@ -73,4 +73,4 @@
 
     </application>
 
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/tests/BiDiTests/res/layout/basic.xml b/tests/BiDiTests/res/layout/basic.xml
index c4807ff..f254e3c 100644
--- a/tests/BiDiTests/res/layout/basic.xml
+++ b/tests/BiDiTests/res/layout/basic.xml
@@ -27,22 +27,21 @@
        <Button android:id="@+id/button"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
-               android:onClick="onButtonClick"
                android:text="@string/button_text"
                android:textSize="32dip"
         />
 
         <TextView android:id="@+id/textview"
-                  android:layout_height="wrap_content"
-                  android:layout_width="wrap_content"
-                  android:textSize="32dip"
-                  android:text="@string/textview_text"
+              android:layout_height="wrap_content"
+              android:layout_width="wrap_content"
+              android:textSize="32dip"
+              android:text="@string/textview_text"
         />
 
         <EditText android:id="@+id/edittext"
-                  android:layout_height="wrap_content"
-                  android:layout_width="match_parent"
-                  android:textSize="32dip"
+              android:layout_height="wrap_content"
+              android:layout_width="match_parent"
+              android:textSize="32dip"
         />
 
     </LinearLayout>