Merge "Use unchecked admin lookup for getStorageEncryption()"
diff --git a/api/current.txt b/api/current.txt
index 932c551..9e87e9c 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
@@ -273,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
@@ -531,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
@@ -660,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
@@ -677,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
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/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/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/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/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/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/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/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/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 262e5ce..b463e56 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3676,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);
             }
             
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/tests/BiDiTests/AndroidManifest.xml b/tests/BiDiTests/AndroidManifest.xml
index 8a77519..e54194c 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">
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>