Merge "Squashed commit of the following:"
diff --git a/Android.mk b/Android.mk
index 2329be2..9bd30fe 100644
--- a/Android.mk
+++ b/Android.mk
@@ -82,6 +82,7 @@
 	core/java/android/app/IWallpaperManagerCallback.aidl \
 	core/java/android/app/admin/IDevicePolicyManager.aidl \
 	core/java/android/app/backup/IBackupManager.aidl \
+	core/java/android/app/backup/IFullBackupRestoreObserver.aidl \
 	core/java/android/app/backup/IRestoreObserver.aidl \
 	core/java/android/app/backup/IRestoreSession.aidl \
 	core/java/android/bluetooth/IBluetooth.aidl \
diff --git a/api/current.txt b/api/current.txt
index 2eda26f..12fa844 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -445,6 +445,7 @@
     field public static final int fromXScale = 16843202; // 0x10101c2
     field public static final int fromYDelta = 16843208; // 0x10101c8
     field public static final int fromYScale = 16843204; // 0x10101c4
+    field public static final int fullBackupAgent = 16843628; // 0x101036c
     field public static final int fullBright = 16842954; // 0x10100ca
     field public static final int fullDark = 16842950; // 0x10100c6
     field public static final int functionalTest = 16842787; // 0x1010023
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index ac0e410..38d0d2a 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -150,20 +150,13 @@
     }
 
     private void doBackup() {
-        boolean isFull = false;
         String pkg = nextArg();
-        if ("-f".equals(pkg)) {
-            isFull = true;
-            pkg = nextArg();
-        }
-
-        if (pkg == null || pkg.startsWith("-")) {
+        if (pkg == null) {
             showUsage();
             return;
         }
 
         try {
-            // !!! TODO: handle full backup
             mBmgr.dataChanged(pkg);
         } catch (RemoteException e) {
             System.err.println(e.toString());
diff --git a/cmds/bu/Android.mk b/cmds/bu/Android.mk
new file mode 100644
index 0000000..4fd5fec
--- /dev/null
+++ b/cmds/bu/Android.mk
@@ -0,0 +1,18 @@
+# Copyright 2011 The Android Open Source Project
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_MODULE := bu
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := bu
+LOCAL_SRC_FILES := bu
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
+
+
diff --git a/cmds/bu/NOTICE b/cmds/bu/NOTICE
new file mode 100644
index 0000000..becc120
--- /dev/null
+++ b/cmds/bu/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2011, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/cmds/bu/bu b/cmds/bu/bu
new file mode 100755
index 0000000..e8dbc31
--- /dev/null
+++ b/cmds/bu/bu
@@ -0,0 +1,6 @@
+# Script to start "bu" on the device
+#
+base=/system
+export CLASSPATH=$base/framework/bu.jar
+exec app_process $base/bin com.android.commands.bu.Backup "$@"
+
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
new file mode 100644
index 0000000..2b8d6aa
--- /dev/null
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.bu;
+
+import android.app.backup.IBackupManager;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.ArrayList;
+
+public final class Backup {
+    static final String TAG = "Backup";
+
+    private static String[] mArgs;
+    private int mNextArg;
+
+    public static void main(String[] args) {
+        mArgs = args;
+        try {
+            new Backup().run();
+        } catch (Exception e) {
+            Log.e(TAG, "Error running backup", e);
+        }
+        Log.d(TAG, "Finished.");
+    }
+
+    public void run() {
+        IBackupManager bmgr = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
+        if (bmgr == null) {
+            System.err.println("ERROR: could not contact backup manager");
+            return;
+        }
+
+        ArrayList<String> packages = new ArrayList<String>();
+        boolean saveApks = false;
+        boolean saveShared = false;
+        boolean doEverything = false;
+
+        String arg;
+        while ((arg = nextArg()) != null) {
+            
+            if (arg.startsWith("-")) {
+                if ("-apk".equals(arg)) {
+                    saveApks = true;
+                } else if ("-noapk".equals(arg)) {
+                    saveApks = false;
+                } else if ("-shared".equals(arg)) {
+                    saveShared = true;
+                } else if ("-noshared".equals(arg)) {
+                    saveShared = false;
+                } else if ("-all".equals(arg)) {
+                    doEverything = true;
+                } else {
+                    System.err.println("ERROR: unknown flag " + arg);
+                    return;
+                }
+            } else {
+                // Not a flag; treat as a package name
+                packages.add(arg);
+            }
+        }
+
+        if (doEverything && packages.size() > 0) {
+            System.err.println("ERROR: -all used with specific package set");
+            return;
+        }
+
+        if (!doEverything && packages.size() == 0) {
+            System.err.println("ERROR: no packages supplied and -all not used");
+            return;
+        }
+
+        try {
+            ParcelFileDescriptor fd = ParcelFileDescriptor.dup(FileDescriptor.out);
+            String[] packArray = new String[packages.size()];
+            bmgr.fullBackup(fd, saveApks, saveShared, doEverything, packages.toArray(packArray));
+        } catch (IOException e) {
+            System.err.println("ERROR: cannot dup System.out");
+        } catch (RemoteException e) {
+            System.err.println("ERROR: unable to invoke backup manager service");
+        }
+    }
+
+    private String nextArg() {
+        if (mNextArg >= mArgs.length) {
+            return null;
+        }
+        String arg = mArgs[mNextArg];
+        mNextArg++;
+        return arg;
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f0e7e98..85e59b3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -129,7 +129,7 @@
     /** @hide */
     public static final boolean DEBUG_BROADCAST = false;
     private static final boolean DEBUG_RESULTS = false;
-    private static final boolean DEBUG_BACKUP = false;
+    private static final boolean DEBUG_BACKUP = true;
     private static final boolean DEBUG_CONFIGURATION = false;
     private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
     private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
@@ -1979,24 +1979,26 @@
 
         BackupAgent agent = null;
         String classname = data.appInfo.backupAgentName;
-        if (classname == null) {
-            if (data.backupMode == IApplicationThread.BACKUP_MODE_INCREMENTAL) {
-                Slog.e(TAG, "Attempted incremental backup but no defined agent for "
-                        + packageName);
-                return;
+
+        if (data.backupMode == IApplicationThread.BACKUP_MODE_FULL) {
+            classname = "android.app.backup.FullBackupAgent";
+            if ((data.appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                // system packages can supply their own full-backup agent
+                if (data.appInfo.fullBackupAgentName != null) {
+                    classname = data.appInfo.fullBackupAgentName;
+                }
             }
-            classname = "android.app.FullBackupAgent";
         }
+
         try {
             IBinder binder = null;
             try {
+                if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
+
                 java.lang.ClassLoader cl = packageInfo.getClassLoader();
-                agent = (BackupAgent) cl.loadClass(data.appInfo.backupAgentName).newInstance();
+                agent = (BackupAgent) cl.loadClass(classname).newInstance();
 
                 // set up the agent's context
-                if (DEBUG_BACKUP) Slog.v(TAG, "Initializing BackupAgent "
-                        + data.appInfo.backupAgentName);
-
                 ContextImpl context = new ContextImpl();
                 context.init(packageInfo, null, this);
                 context.setOuterContext(agent);
@@ -2023,7 +2025,7 @@
             }
         } catch (Exception e) {
             throw new RuntimeException("Unable to create BackupAgent "
-                    + data.appInfo.backupAgentName + ": " + e.toString(), e);
+                    + classname + ": " + e.toString(), e);
         }
     }
 
diff --git a/core/java/android/app/FullBackupAgent.java b/core/java/android/app/FullBackupAgent.java
deleted file mode 100644
index acd20bd..0000000
--- a/core/java/android/app/FullBackupAgent.java
+++ /dev/null
@@ -1,76 +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.
- */
-
-package android.app;
-
-import android.app.backup.BackupAgent;
-import android.app.backup.BackupDataInput;
-import android.app.backup.BackupDataOutput;
-import android.app.backup.FileBackupHelper;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.LinkedList;
-
-/**
- * Backs up an application's entire /data/data/&lt;package&gt;/... file system.  This
- * class is used by the desktop full backup mechanism and is not intended for direct
- * use by applications.
- * 
- * {@hide}
- */
-
-public class FullBackupAgent extends BackupAgent {
-    // !!! TODO: turn off debugging
-    private static final String TAG = "FullBackupAgent";
-    private static final boolean DEBUG = true;
-
-    @Override
-    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-            ParcelFileDescriptor newState) {
-        LinkedList<File> dirsToScan = new LinkedList<File>();
-        ArrayList<String> allFiles = new ArrayList<String>();
-
-        // build the list of files in the app's /data/data tree
-        dirsToScan.add(getFilesDir());
-        if (DEBUG) Log.v(TAG, "Backing up dir tree @ " + getFilesDir().getAbsolutePath() + " :");
-        while (dirsToScan.size() > 0) {
-            File dir = dirsToScan.removeFirst();
-            File[] contents = dir.listFiles();
-            if (contents != null) {
-                for (File f : contents) {
-                    if (f.isDirectory()) {
-                        dirsToScan.add(f);
-                    } else if (f.isFile()) {
-                        if (DEBUG) Log.v(TAG, "    " + f.getAbsolutePath());
-                        allFiles.add(f.getAbsolutePath());
-                    }
-                }
-            }
-        }
-
-        // That's the file set; now back it all up
-        FileBackupHelper helper = new FileBackupHelper(this,
-                allFiles.toArray(new String[allFiles.size()]));
-        helper.performBackup(oldState, data, newState);
-    }
-
-    @Override
-    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) {
-    }
-}
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index fed2bc5..52fc623 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -51,6 +51,7 @@
     void doBackup(in ParcelFileDescriptor oldState,
             in ParcelFileDescriptor data,
             in ParcelFileDescriptor newState,
+            boolean storeApk,
             int token, IBackupManager callbackBinder);
 
     /**
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index cb4e0e78..dc60e24 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -85,7 +85,7 @@
  */
 public abstract class BackupAgent extends ContextWrapper {
     private static final String TAG = "BackupAgent";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     public BackupAgent() {
         super(null);
@@ -172,11 +172,18 @@
      * @param newState An open, read/write ParcelFileDescriptor pointing to an
      *            empty file. The application should record the final backup
      *            state here after restoring its data from the <code>data</code> stream.
+     *            When a full-backup dataset is being restored, this will be <code>null</code>.
      */
     public abstract void onRestore(BackupDataInput data, int appVersionCode,
             ParcelFileDescriptor newState)
             throws IOException;
 
+    /**
+     * 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 -----
 
@@ -199,12 +206,18 @@
         public void doBackup(ParcelFileDescriptor oldState,
                 ParcelFileDescriptor data,
                 ParcelFileDescriptor newState,
+                boolean storeApk,
                 int token, IBackupManager callbackBinder) throws RemoteException {
             // Ensure that we're running with the app's normal permission level
             long ident = Binder.clearCallingIdentity();
 
             if (DEBUG) Log.v(TAG, "doBackup() invoked");
             BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
+
+            if (storeApk) {
+                onSaveApk(output);
+            }
+
             try {
                 BackupAgent.this.onBackup(oldState, output, newState);
             } catch (IOException ex) {
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
new file mode 100644
index 0000000..9850566
--- /dev/null
+++ b/core/java/android/app/backup/FullBackup.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.backup;
+
+/**
+ * Global constant definitions et cetera related to the full-backup-to-fd
+ * binary format.
+ *
+ * @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";
+
+    public static String FULL_BACKUP_INTENT_ACTION = "fullback";
+    public static String FULL_RESTORE_INTENT_ACTION = "fullrest";
+    public static String CONF_TOKEN_INTENT_EXTRA = "conftoken";
+
+    static public native int backupToTar(String packageName, String domain,
+            String linkdomain, String rootpath, String path, BackupDataOutput output);
+}
diff --git a/core/java/android/app/backup/FullBackupAgent.java b/core/java/android/app/backup/FullBackupAgent.java
new file mode 100644
index 0000000..f0a1f2a
--- /dev/null
+++ b/core/java/android/app/backup/FullBackupAgent.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.backup;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import libcore.io.Libcore;
+import libcore.io.ErrnoException;
+import libcore.io.OsConstants;
+import libcore.io.StructStat;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.LinkedList;
+
+/**
+ * Backs up an application's entire /data/data/&lt;package&gt;/... file system.  This
+ * class is used by the desktop full backup mechanism and is not intended for direct
+ * use by applications.
+ * 
+ * {@hide}
+ */
+
+public class FullBackupAgent extends BackupAgent {
+    // !!! TODO: turn off debugging
+    private static final String TAG = "FullBackupAgent";
+    private static final boolean DEBUG = true;
+
+    PackageManager mPm;
+
+    private String mMainDir;
+    private String mFilesDir;
+    private String mDatabaseDir;
+    private String mSharedPrefsDir;
+    private String mCacheDir;
+    private String mLibDir;
+
+    @Override
+    public void onCreate() {
+        mPm = getPackageManager();
+        try {
+            ApplicationInfo appInfo = mPm.getApplicationInfo(getPackageName(), 0);
+            mMainDir = new File(appInfo.dataDir).getAbsolutePath();
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Unable to find package " + getPackageName());
+            throw new RuntimeException(e);
+        }
+
+        mFilesDir = getFilesDir().getAbsolutePath();
+        mDatabaseDir = getDatabasePath("foo").getParentFile().getAbsolutePath();
+        mSharedPrefsDir = getSharedPrefsFile("foo").getParentFile().getAbsolutePath();
+        mCacheDir = getCacheDir().getAbsolutePath();
+
+        ApplicationInfo app = getApplicationInfo();
+        mLibDir = (app.nativeLibraryDir != null)
+                ? new File(app.nativeLibraryDir).getAbsolutePath()
+                : null;
+    }
+
+    @Override
+    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+            ParcelFileDescriptor newState) {
+        // Filters, the scan queue, and the set of resulting entities
+        HashSet<String> filterSet = new HashSet<String>();
+
+        // Okay, start with the app's root tree, but exclude all of the canonical subdirs
+        if (mLibDir != null) {
+            filterSet.add(mLibDir);
+        }
+        filterSet.add(mCacheDir);
+        filterSet.add(mDatabaseDir);
+        filterSet.add(mSharedPrefsDir);
+        filterSet.add(mFilesDir);
+        processTree(FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data);
+
+        // Now do the same for the files dir, db dir, and shared prefs dir
+        filterSet.add(mMainDir);
+        filterSet.remove(mFilesDir);
+        processTree(FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data);
+
+        filterSet.add(mFilesDir);
+        filterSet.remove(mDatabaseDir);
+        processTree(FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data);
+
+        filterSet.add(mDatabaseDir);
+        filterSet.remove(mSharedPrefsDir);
+        processTree(FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data);
+    }
+
+    private void processTree(String domain, String rootPath,
+            HashSet<String> excludes, BackupDataOutput data) {
+        // Scan the dir tree (if it actually exists) and process each entry we find
+        File rootFile = new File(rootPath);
+        if (rootFile.exists()) {
+            LinkedList<File> scanQueue = new LinkedList<File>();
+            scanQueue.add(rootFile);
+
+            while (scanQueue.size() > 0) {
+                File file = scanQueue.remove(0);
+                String filePath = file.getAbsolutePath();
+
+                // prune this subtree?
+                if (excludes.contains(filePath)) {
+                    continue;
+                }
+
+                // If it's a directory, enqueue its contents for scanning.
+                try {
+                    StructStat stat = Libcore.os.lstat(filePath);
+                    if (OsConstants.S_ISLNK(stat.st_mode)) {
+                        if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file);
+                        continue;
+                    } else if (OsConstants.S_ISDIR(stat.st_mode)) {
+                        File[] contents = file.listFiles();
+                        if (contents != null) {
+                            for (File entry : contents) {
+                                scanQueue.add(0, entry);
+                            }
+                        }
+                    }
+                } catch (ErrnoException e) {
+                    if (DEBUG) Log.w(TAG, "Error scanning file " + file + " : " + e);
+                    continue;
+                }
+
+                // Finally, back this file up before proceeding
+                FullBackup.backupToTar(getPackageName(), domain, null, rootPath, filePath, data);
+            }
+        }
+    }
+
+    @Override
+    void onSaveApk(BackupDataOutput data) {
+        ApplicationInfo app = getApplicationInfo();
+        if (DEBUG) Log.i(TAG, "APK flags: system=" + ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0)
+                + " updated=" + ((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)
+                + " locked=" + ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) );
+        if (DEBUG) Log.i(TAG, "codepath: " + getPackageCodePath());
+
+        // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
+        final String pkgName = getPackageName();
+        final String apkDir = new File(getPackageCodePath()).getParent();
+        FullBackup.backupToTar(pkgName, FullBackup.APK_TREE_TOKEN, null,
+                apkDir, getPackageCodePath(), data);
+
+        // Save associated .obb content if it exists and we did save the apk
+        // check for .obb and save those too
+        final File obbDir = Environment.getExternalStorageAppObbDirectory(pkgName);
+        if (obbDir != null) {
+            if (DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
+            File[] obbFiles = obbDir.listFiles();
+            if (obbFiles != null) {
+                final String obbDirName = obbDir.getAbsolutePath();
+                for (File obb : obbFiles) {
+                    FullBackup.backupToTar(pkgName, FullBackup.OBB_TREE_TOKEN, null,
+                            obbDirName, obb.getAbsolutePath(), data);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) {
+    }
+}
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index b315b3a..94e31a8 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -16,7 +16,9 @@
 
 package android.app.backup;
 
+import android.app.backup.IFullBackupRestoreObserver;
 import android.app.backup.IRestoreSession;
+import android.os.ParcelFileDescriptor;
 import android.content.Intent;
 
 /**
@@ -121,6 +123,42 @@
     void backupNow();
 
     /**
+     * Write a full backup of the given package to the supplied file descriptor.
+     * The fd may be a socket or other non-seekable destination.  If no package names
+     * are supplied, then every application on the device will be backed up to the output.
+     *
+     * <p>This method is <i>synchronous</i> -- it does not return until the backup has
+     * completed.
+     *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     *
+     * @param fd The file descriptor to which a 'tar' file stream is to be written
+     * @param includeApks If <code>true</code>, the resulting tar stream will include the
+     *     application .apk files themselves as well as their data.
+     * @param includeShared If <code>true</code>, the resulting tar stream will include
+     *     the contents of the device's shared storage (SD card or equivalent).
+     * @param allApps If <code>true</code>, the resulting tar stream will include all
+     *     installed applications' data, not just those named in the <code>packageNames</code>
+     *     parameter.
+     * @param packageNames The package names of the apps whose data (and optionally .apk files)
+     *     are to be backed up.  The <code>allApps</code> parameter supersedes this.
+     */
+    void fullBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
+            boolean allApps, in String[] packageNames);
+
+    /**
+     * 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
+     * the same time, the UI supplies a callback Binder for progress notifications during
+     * the operation.
+     *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     */
+    void acknowledgeFullBackupOrRestore(int token, boolean allow,
+            IFullBackupRestoreObserver observer);
+
+    /**
      * Identify the currently selected transport.  Callers must hold the
      * android.permission.BACKUP permission to use this method.
      */
diff --git a/core/java/android/app/backup/IFullBackupRestoreObserver.aidl b/core/java/android/app/backup/IFullBackupRestoreObserver.aidl
new file mode 100644
index 0000000..3e0b73d
--- /dev/null
+++ b/core/java/android/app/backup/IFullBackupRestoreObserver.aidl
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.backup;
+
+/**
+ * Observer of a full backup or restore process.  The observer is told "interesting"
+ * information about an ongoing full backup or restore action.
+ *
+ * {@hide}
+ */
+
+oneway interface IFullBackupRestoreObserver {
+    /**
+     * Notification: a full backup operation has begun.
+     */
+    void onStartBackup();
+
+    /**
+     * Notification: the system has begun backing up the given package.
+     *
+     * @param name The name of the application being saved.  This will typically be a
+     *     user-meaningful name such as "Browser" rather than a package name such as
+     *     "com.android.browser", though this is not guaranteed.
+     */
+    void onBackupPackage(String name);
+
+    /**
+     * Notification: the full backup operation has ended.
+     */
+    void onEndBackup();
+
+    /**
+     * Notification: a restore-from-full-backup operation has begun.
+     */
+    void onStartRestore();
+
+    /**
+     * Notification: the system has begun restore of the given package.
+     *
+     * @param name The name of the application being saved.  This will typically be a
+     *     user-meaningful name such as "Browser" rather than a package name such as
+     *     "com.android.browser", though this is not guaranteed.
+     */
+    void onRestorePackage(String name);
+
+    /**
+     * Notification: the restore-from-full-backup operation has ended.
+     */
+    void onEndRestore();
+
+    /**
+     * The user's window of opportunity for confirming the operation has timed out.
+     */
+    void onTimeout();
+}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 92b2c3b..4b38d48 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -89,7 +89,16 @@
      * <p>If android:allowBackup is set to false, this attribute is ignored.
      */
     public String backupAgentName;
-    
+
+    /**
+     * Class implementing the package's *full* backup functionality.  This
+     * is not usable except by system-installed packages.  It can be the same
+     * as the backupAgent.
+     *
+     * @hide
+     */
+    public String fullBackupAgentName;
+
     /**
      * Value for {@link #flags}: if set, this application is installed in the
      * device's system image.
@@ -505,6 +514,7 @@
         dest.writeInt(installLocation);
         dest.writeString(manageSpaceActivityName);
         dest.writeString(backupAgentName);
+        dest.writeString(fullBackupAgentName);
         dest.writeInt(descriptionRes);
     }
 
@@ -538,6 +548,7 @@
         installLocation = source.readInt();
         manageSpaceActivityName = source.readString();
         backupAgentName = source.readString();
+        fullBackupAgentName = source.readString();
         descriptionRes = source.readInt();
     }
 
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 564f4f4..b8cb165 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1506,7 +1506,17 @@
                 }
             }
         }
-        
+
+        name = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestApplication_fullBackupAgent, 0);
+        if (name != null) {
+            ai.fullBackupAgentName = buildClassName(pkgName, name, outError);
+            if (true) {
+                Log.v(TAG, "android:fullBackupAgent=" + ai.fullBackupAgentName
+                        + " from " + pkgName + "+" + name);
+            }
+        }
+
         TypedValue v = sa.peekValue(
                 com.android.internal.R.styleable.AndroidManifestApplication_label);
         if (v != null && (ai.labelRes=v.resourceId) == 0) {
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 823d10f..fbe5379 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -225,4 +225,50 @@
         }
         return addRoute(interfaceName, dstStr, prefixLength, gwStr) == 0;
     }
+
+    /**
+     * Get InetAddress masked with prefixLength.  Will never return null.
+     * @param IP address which will be masked with specified prefixLength
+     * @param prefixLength the prefixLength used to mask the IP
+     */
+    public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
+        if (address == null) {
+            throw new RuntimeException("getNetworkPart doesn't accept null address");
+        }
+
+        byte[] array = address.getAddress();
+
+        if (prefixLength < 0 || prefixLength > array.length * 8) {
+            throw new RuntimeException("getNetworkPart - bad prefixLength");
+        }
+
+        int offset = prefixLength / 8;
+        int reminder = prefixLength % 8;
+        byte mask = (byte)(0xFF << (8 - reminder));
+
+        if (offset < array.length) array[offset] = (byte)(array[offset] & mask);
+
+        offset++;
+
+        for (; offset < array.length; offset++) {
+            array[offset] = 0;
+        }
+
+        InetAddress netPart = null;
+        try {
+            netPart = InetAddress.getByAddress(array);
+        } catch (UnknownHostException e) {
+            throw new RuntimeException("getNetworkPart error - " + e.toString());
+        }
+        return netPart;
+    }
+
+    /**
+     * Check if IP address type is consistent between two InetAddress.
+     * @return true if both are the same type.  False otherwise.
+     */
+    public static boolean addressTypeMatches(InetAddress left, InetAddress right) {
+        return (((left instanceof Inet4Address) && (right instanceof Inet4Address)) ||
+                ((left instanceof Inet6Address) && (right instanceof Inet6Address)));
+    }
 }
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 5b10531..9c4e48b 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -23,6 +23,9 @@
 import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
+
+import java.util.Collection;
+
 /**
  * A simple container for route information.
  *
@@ -44,39 +47,30 @@
     public RouteInfo(LinkAddress destination, InetAddress gateway) {
         if (destination == null) {
             try {
-                if ((gateway != null) && (gateway instanceof Inet4Address)) {
-                    destination = new LinkAddress(InetAddress.getByName("0.0.0.0"), 32);
+                if ((gateway != null) || (gateway instanceof Inet4Address)) {
+                    destination = new LinkAddress(Inet4Address.ANY, 0);
                 } else {
-                    destination = new LinkAddress(InetAddress.getByName("::0"), 128);
+                    destination = new LinkAddress(Inet6Address.ANY, 0);
                 }
             } catch (Exception e) {}
         }
-        mDestination = destination;
+        mDestination = new LinkAddress(NetworkUtils.getNetworkPart(destination.getAddress(),
+                destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength());
         mGateway = gateway;
         mIsDefault = isDefault();
     }
 
     public RouteInfo(InetAddress gateway) {
-        LinkAddress destination = null;
-        try {
-            if ((gateway != null) && (gateway instanceof Inet4Address)) {
-                destination = new LinkAddress(InetAddress.getByName("0.0.0.0"), 32);
-            } else {
-                destination = new LinkAddress(InetAddress.getByName("::0"), 128);
-            }
-        } catch (Exception e) {}
-        mDestination = destination;
-        mGateway = gateway;
-        mIsDefault = isDefault();
+        this(null, gateway);
     }
 
     private boolean isDefault() {
         boolean val = false;
         if (mGateway != null) {
             if (mGateway instanceof Inet4Address) {
-                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 32);
+                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
             } else {
-                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 128);
+                val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
             }
         }
         return val;
@@ -159,4 +153,43 @@
             return new RouteInfo[size];
         }
     };
+
+    private boolean matches(InetAddress destination) {
+        if (destination == null) return false;
+
+        // if the destination is present and the route is default.
+        // return true
+        if (isDefault()) return true;
+
+        // match the route destination and destination with prefix length
+        InetAddress dstNet = NetworkUtils.getNetworkPart(destination,
+                mDestination.getNetworkPrefixLength());
+
+        return mDestination.getAddress().equals(dstNet);
+    }
+
+    /**
+     * Find the route from a Collection of routes that best matches a given address.
+     * May return null if no routes are applicable.
+     * @param routes a Collection of RouteInfos to chose from
+     * @param dest the InetAddress your trying to get to
+     * @return the RouteInfo from the Collection that best fits the given address
+     */
+    public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
+        if ((routes == null) || (dest == null)) return null;
+
+        RouteInfo bestRoute = null;
+        // pick a longest prefix match under same address type
+        for (RouteInfo route : routes) {
+            if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
+                if ((bestRoute != null) &&
+                        (bestRoute.mDestination.getNetworkPrefixLength() >=
+                        route.mDestination.getNetworkPrefixLength())) {
+                    continue;
+                }
+                if (route.matches(dest)) bestRoute = route;
+            }
+        }
+        return bestRoute;
+    }
 }
diff --git a/core/java/android/provider/BrowserContract.java b/core/java/android/provider/BrowserContract.java
index 03bc41a..d678205 100644
--- a/core/java/android/provider/BrowserContract.java
+++ b/core/java/android/provider/BrowserContract.java
@@ -332,6 +332,13 @@
          * <P>Type: TEXT</P>
          */
         public static final String ACCOUNT_TYPE = "account_type";
+
+        /**
+         * The ID of the account's root folder. This will be the ID of the folder
+         * returned when querying {@link Bookmarks#CONTENT_URI_DEFAULT_FOLDER}.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String ROOT_ID = "root_id";
     }
 
     /**
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 2eeb7a4..4141879 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -110,13 +110,13 @@
          * The account that was used to sync the entry to the device.
          * <P>Type: TEXT</P>
          */
-        public static final String _SYNC_ACCOUNT = "_sync_account";
+        public static final String ACCOUNT_NAME = "account_name";
 
         /**
          * The type of the account that was used to sync the entry to the device.
          * <P>Type: TEXT</P>
          */
-        public static final String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
+        public static final String ACCOUNT_TYPE = "account_type";
 
         /**
          * The unique ID for a row assigned by the sync source. NULL if the row has never been synced.
@@ -146,30 +146,11 @@
          * Used to indicate that local, unsynced, changes are present.
          * <P>Type: INTEGER (long)</P>
          */
-        public static final String _SYNC_DIRTY = "_sync_dirty";
+        public static final String DIRTY = "dirty";
 
     }
 
     /**
-     * Columns from the Account information used by Calendars and Events tables.
-     */
-    public interface AccountColumns {
-        /**
-         * The name of the account instance to which this row belongs, which when paired with
-         * {@link #ACCOUNT_TYPE} identifies a specific account.
-         * <P>Type: TEXT</P>
-         */
-        public static final String ACCOUNT_NAME = "account_name";
-
-        /**
-         * The type of account to which this row belongs, which when paired with
-         * {@link #ACCOUNT_NAME} identifies a specific account.
-         * <P>Type: TEXT</P>
-         */
-        public static final String ACCOUNT_TYPE = "account_type";
-    }
-
-    /**
      * Columns from the Calendars table that other tables join into themselves.
      */
     public interface CalendarsColumns {
@@ -177,7 +158,7 @@
          * The color of the calendar
          * <P>Type: INTEGER (color value)</P>
          */
-        public static final String COLOR = "color";
+        public static final String CALENDAR_COLOR = "calendar_color";
 
         /**
          * The level of access that the user has for the calendar
@@ -211,7 +192,7 @@
          * The timezone the calendar's events occurs in
          * <P>Type: TEXT</P>
          */
-        public static final String TIMEZONE = "timezone";
+        public static final String CALENDAR_TIMEZONE = "calendar_timezone";
 
         /**
          * If this calendar is in the list of calendars that are selected for
@@ -275,13 +256,13 @@
                 ContentValues cv = new ContentValues();
                 cv.put(_ID, calendarId);
 
-                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ACCOUNT);
-                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ACCOUNT_TYPE);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ACCOUNT_NAME);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ACCOUNT_TYPE);
 
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_TIME);
-                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _SYNC_DIRTY);
+                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
 
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC2);
@@ -293,12 +274,13 @@
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.NAME);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
                         Calendars.DISPLAY_NAME);
-                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, Calendars.COLOR);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, Calendars.CALENDAR_COLOR);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, VISIBLE);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SYNC_EVENTS);
-                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.LOCATION);
-                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, TIMEZONE);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
+                        Calendars.CALENDAR_LOCATION);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CALENDAR_TIMEZONE);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
                         Calendars.OWNER_ACCOUNT);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
@@ -325,11 +307,12 @@
     /**
      * Contains a list of available calendars.
      */
-    public static class Calendars implements BaseColumns, SyncColumns, AccountColumns,
+    public static class Calendars implements BaseColumns, SyncColumns,
             CalendarsColumns
     {
-        private static final String WHERE_DELETE_FOR_ACCOUNT = Calendars._SYNC_ACCOUNT + "=?"
-                + " AND " + Calendars._SYNC_ACCOUNT_TYPE + "=?";
+        private static final String WHERE_DELETE_FOR_ACCOUNT = Calendars.ACCOUNT_NAME + "=?"
+                + " AND "
+                + Calendars.ACCOUNT_TYPE + "=?";
 
         public static final Cursor query(ContentResolver cr, String[] projection,
                                        String where, String orderBy)
@@ -414,7 +397,7 @@
          * The location the of the events in the calendar
          * <P>Type: TEXT</P>
          */
-        public static final String LOCATION = "location";
+        public static final String CALENDAR_LOCATION = "calendar_location";
 
         /**
          * The owner account for this calendar, based on the calendar feed.
@@ -443,23 +426,31 @@
         public static final String MAX_REMINDERS = "maxReminders";
 
         /**
+         * The maximum number of reminders allowed for an event.
+         * <P>
+         * Type: INTEGER
+         * </P>
+         */
+        public static final String ALLOWED_REMINDERS = "allowedReminders";
+
+        /**
          * These fields are only writable by a sync adapter. To modify them the
          * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and
          * _SYNC_ACCOUNT_TYPE in the query parameters.
          */
         public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
-            _SYNC_ACCOUNT,
-            _SYNC_ACCOUNT_TYPE,
+            ACCOUNT_NAME,
+            ACCOUNT_TYPE,
             _SYNC_ID,
             _SYNC_TIME,
             _SYNC_VERSION,
-            _SYNC_DIRTY,
+            DIRTY,
             OWNER_ACCOUNT,
             MAX_REMINDERS,
             CAN_MODIFY_TIME_ZONE,
             CAN_ORGANIZER_RESPOND,
-            LOCATION,
-            TIMEZONE,
+            CALENDAR_LOCATION,
+            CALENDAR_TIMEZONE,
             ACCESS_LEVEL,
             DELETED,
             SYNC1,
@@ -604,7 +595,7 @@
          * This column is available for use by sync adapters
          * <P>Type: TEXT</P>
          */
-        public static final String SYNC_ADAPTER_DATA = "syncAdapterData";
+        public static final String SYNC_DATA1 = "sync_data1";
 
         /**
          * The comments feed uri.
@@ -652,7 +643,7 @@
          * The timezone for the event, allDay events will have a local tz instead of UTC
          * <P>Type: TEXT
          */
-        public static final String EVENT_TIMEZONE2 = "eventTimezone2";
+        public static final String EVENT_END_TIMEZONE = "eventEndTimezone";
 
         /**
          * Whether the event lasts all day or not
@@ -661,25 +652,49 @@
         public static final String ALL_DAY = "allDay";
 
         /**
-         * Visibility for the event.
+         * Defines how the event shows up for others when the calendar is
+         * shared.
          * <P>Type: INTEGER</P>
          */
-        public static final String VISIBILITY = "visibility";
-
-        public static final int VISIBILITY_DEFAULT = 0;
-        public static final int VISIBILITY_CONFIDENTIAL = 1;
-        public static final int VISIBILITY_PRIVATE = 2;
-        public static final int VISIBILITY_PUBLIC = 3;
+        public static final String ACCESS_LEVEL = "accessLevel";
 
         /**
-         * Transparency for the event -- does the event consume time on the calendar?
+         * Default access is controlled by the server and will be treated as
+         * public on the device.
+         */
+        public static final int ACCESS_DEFAULT = 0;
+        /**
+         * Confidential is not used by the app.
+         */
+        public static final int ACCESS_CONFIDENTIAL = 1;
+        /**
+         * Private assumes the event appears as a free/busy slot with no
+         * details.
+         */
+        public static final int ACCESS_PRIVATE = 2;
+        /**
+         * Public assumes the contents are visible to anyone with access to the
+         * calendar.
+         */
+        public static final int ACCESS_PUBLIC = 3;
+
+        /**
+         * If this event counts as busy time or is still free time that can be
+         * scheduled over.
          * <P>Type: INTEGER</P>
          */
-        public static final String TRANSPARENCY = "transparency";
+        public static final String AVAILABILITY = "availability";
 
-        public static final int TRANSPARENCY_OPAQUE = 0;
-
-        public static final int TRANSPARENCY_TRANSPARENT = 1;
+        /**
+         * Indicates that this event takes up time and will conflict with other
+         * events.
+         */
+        public static final int AVAILABILITY_BUSY = 0;
+        /**
+         * Indicates that this event is free time and will not conflict with
+         * other events.
+         */
+        public static final int AVAILABILITY_FREE = 1;
 
         /**
          * Whether the event has an alarm or not
@@ -723,7 +738,7 @@
          * an exception.
          * <P>Type: TEXT</P>
          */
-        public static final String ORIGINAL_EVENT = "originalEvent";
+        public static final String ORIGINAL_SYNC_ID = "original_sync_id";
 
         /**
          * The original instance time of the recurring event for which this
@@ -801,10 +816,10 @@
     }
 
     /**
-     * Contains one entry per calendar event. Recurring events show up as a single entry.
+     * Contains one entry per calendar event. Recurring events show up as a
+     * single entry.
      */
-    public static final class EventsEntity implements BaseColumns, SyncColumns, AccountColumns,
-            EventsColumns {
+    public static final class EventsEntity implements BaseColumns, SyncColumns, EventsColumns {
         /**
          * The content:// style URL for this table
          */
@@ -885,8 +900,8 @@
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, DURATION);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_TIMEZONE);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ALL_DAY);
-                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, VISIBILITY);
-                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, TRANSPARENCY);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, AVAILABILITY);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, HAS_ALARM);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
                         HAS_EXTENDED_PROPERTIES);
@@ -894,7 +909,7 @@
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, RDATE);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXRULE);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXDATE);
-                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_EVENT);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_SYNC_ID);
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv,
                         ORIGINAL_INSTANCE_TIME);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ORIGINAL_ALL_DAY);
@@ -907,12 +922,12 @@
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORGANIZER);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_DATA);
-                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _SYNC_DIRTY);
+                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
-                        Events.SYNC_ADAPTER_DATA);
+                        Events.SYNC_DATA1);
 
                 Entity entity = new Entity(cv);
                 Cursor subCursor;
@@ -1003,11 +1018,10 @@
     /**
      * Contains one entry per calendar event. Recurring events show up as a single entry.
      */
-    public static final class Events implements BaseColumns, SyncColumns, AccountColumns,
-            EventsColumns {
+    public static final class Events implements BaseColumns, SyncColumns, EventsColumns {
 
         private static final String[] FETCH_ENTRY_COLUMNS =
-                new String[] { Events._SYNC_ACCOUNT, Events._SYNC_ID };
+                new String[] { Events.ACCOUNT_NAME, Events._SYNC_ID };
 
         private static final String[] ATTENDEES_COLUMNS =
                 new String[] { AttendeesColumns.ATTENDEE_NAME,
@@ -1049,6 +1063,29 @@
          * The default sort order for this table
          */
         public static final String DEFAULT_SORT_ORDER = "";
+
+        /**
+         * These are columns that should only ever be updated by the provider,
+         * either because they are views mapped to another table or because they
+         * are used for provider only functionality.
+         */
+        public static String[] PROVIDER_WRITABLE_COLUMNS = new String[] {
+                ACCOUNT_NAME,
+                ACCOUNT_TYPE
+        };
+
+        /**
+         * These fields are only writable by a sync adapter. To modify them the
+         * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and
+         * _SYNC_ACCOUNT_TYPE in the query parameters.
+         */
+        public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
+            _SYNC_ID,
+            _SYNC_TIME,
+            _SYNC_VERSION,
+            DIRTY,
+            SYNC_DATA1
+        };
     }
 
     /**
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 7f23ed5..7d21489 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -34,5 +34,6 @@
     void setMenuKeyVisible(boolean visible);
     void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
     void setHardKeyboardStatus(boolean available, boolean enabled);
+    void userActivity();
 }
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index d6ca426..bfc717b 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -46,4 +46,5 @@
     void onNotificationClear(String pkg, String tag, int id);
     void setSystemUiVisibility(int vis);
     void setHardKeyboardEnabled(boolean enabled);
+    void userActivity();
 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 8beb94b..223008c 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -151,6 +151,7 @@
 	android_backup_BackupDataOutput.cpp \
 	android_backup_FileBackupHelperBase.cpp \
 	android_backup_BackupHelperDispatcher.cpp \
+	android_app_backup_FullBackup.cpp \
 	android_content_res_ObbScanner.cpp \
 	android_content_res_Configuration.cpp \
     android_animation_PropertyValuesHolder.cpp
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 17f9246..b787e9f 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -165,6 +165,7 @@
 extern int register_android_backup_BackupDataOutput(JNIEnv *env);
 extern int register_android_backup_FileBackupHelperBase(JNIEnv *env);
 extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env);
+extern int register_android_app_backup_FullBackup(JNIEnv *env);
 extern int register_android_app_ActivityThread(JNIEnv *env);
 extern int register_android_app_NativeActivity(JNIEnv *env);
 extern int register_android_view_InputChannel(JNIEnv* env);
@@ -1208,7 +1209,7 @@
     REG_JNI(register_android_backup_BackupDataOutput),
     REG_JNI(register_android_backup_FileBackupHelperBase),
     REG_JNI(register_android_backup_BackupHelperDispatcher),
-
+    REG_JNI(register_android_app_backup_FullBackup),
     REG_JNI(register_android_app_ActivityThread),
     REG_JNI(register_android_app_NativeActivity),
     REG_JNI(register_android_view_InputChannel),
diff --git a/core/jni/android_app_backup_FullBackup.cpp b/core/jni/android_app_backup_FullBackup.cpp
new file mode 100644
index 0000000..ecfe5ff
--- /dev/null
+++ b/core/jni/android_app_backup_FullBackup.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "FullBackup_native"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include "JNIHelp.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include <utils/BackupHelpers.h>
+
+#include <string.h>
+
+namespace android
+{
+
+// android.app.backup.BackupDataOutput
+static struct {
+    // This is actually a native pointer to the underlying BackupDataWriter instance
+    jfieldID mBackupWriter;
+} sBackupDataOutput;
+
+/*
+ * Write files to the given output.  This implementation does *not* create
+ * a standalone archive suitable for restore on its own.  In particular, the identification of
+ * the application's name etc is not in-band here; it's assumed that the calling code has
+ * taken care of supplying that information previously in the output stream.
+ *
+ * The file format is 'tar's, with special semantics applied by use of a "fake" directory
+ * hierarchy within the tar stream:
+ *
+ * apps/packagename/a/Filename.apk - this is an actual application binary, which will be
+ *   installed on the target device at restore time.  These need to appear first in the tar
+ *   stream.
+ * apps/packagename/obb/[relpath] - OBB containers belonging the app
+ * apps/packagename/r/[relpath] - these are files at the root of the app's data tree
+ * apps/packagename/f/[relpath] - this is a file within the app's getFilesDir() tree, stored
+ *   at [relpath] relative to the top of that tree.
+ * apps/packagename/db/[relpath] - as with "files" but for the getDatabasePath() tree
+ * apps/packagename/sp/[relpath] - as with "files" but for the getSharedPrefsFile() tree
+ * apps/packagename/c/[relpath] - as with "files" but for the getCacheDir() tree
+ *
+ * and for the shared storage hierarchy:
+ *
+ * shared/[relpaths] - files belonging in the device's shared storage location.  This will
+ *    *not* include .obb files; those are saved with their owning apps.
+ *
+ * This method writes one file data block.  'domain' is the name of the appropriate pseudo-
+ * directory to be applied for this file; 'linkdomain' is the pseudo-dir for a relative
+ * symlink's antecedent.
+ *
+ * packagename: the package name to use as the top level directory tag
+ * domain:      which semantic name the file is to be stored under (a, r, f, db, etc)
+ * linkdomain:  where a symlink points for purposes of rewriting; current unused
+ * rootpath:    prefix to be snipped from full path when encoding in tar
+ * path:        absolute path to the file to be saved
+ * dataOutput:  the BackupDataOutput object that we're saving into
+ */
+static int backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
+        jstring domainObj, jstring linkdomain,
+        jstring rootpathObj, jstring pathObj, jobject dataOutputObj) {
+    // Extract the various strings, allowing for null object pointers
+    const char* packagenamechars = env->GetStringUTFChars(packageNameObj, NULL);
+    const char* rootchars = env->GetStringUTFChars(rootpathObj, NULL);
+    const char* pathchars = env->GetStringUTFChars(pathObj, NULL);
+    const char* domainchars = env->GetStringUTFChars(domainObj, NULL);
+
+    String8 packageName(packagenamechars ? packagenamechars : "");
+    String8 rootpath(rootchars ? rootchars : "");
+    String8 path(pathchars ? pathchars : "");
+    String8 domain(domainchars ? domainchars : "");
+
+    if (domainchars) env->ReleaseStringUTFChars(domainObj, domainchars);
+    if (pathchars) env->ReleaseStringUTFChars(pathObj, pathchars);
+    if (rootchars) env->ReleaseStringUTFChars(rootpathObj, rootchars);
+    if (packagenamechars) env->ReleaseStringUTFChars(packageNameObj, packagenamechars);
+
+    // Extract the data output fd
+    BackupDataWriter* writer = (BackupDataWriter*) env->GetIntField(dataOutputObj,
+            sBackupDataOutput.mBackupWriter);
+
+    // Validate
+    if (!writer) {
+        LOGE("No output stream provided [%s]", path.string());
+        return -1;
+    }
+
+    if (path.length() < rootpath.length()) {
+        LOGE("file path [%s] shorter than root path [%s]",
+                path.string(), rootpath.string());
+        return -1;
+    }
+
+    return write_tarfile(packageName, domain, rootpath, path, writer);
+}
+
+static const JNINativeMethod g_methods[] = {
+    { "backupToTar",
+            "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/BackupDataOutput;)I",
+            (void*)backupToTar },
+};
+
+int register_android_app_backup_FullBackup(JNIEnv* env)
+{
+    jclass clazz = env->FindClass("android/app/backup/BackupDataOutput");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.app.backup.BackupDataOutput");
+
+    sBackupDataOutput.mBackupWriter = env->GetFieldID(clazz, "mBackupWriter", "I");
+    LOG_FATAL_IF(sBackupDataOutput.mBackupwriter == NULL,
+            "Unable to find mBackupWriter field in android.app.backup.BackupDataOutput");
+
+    return AndroidRuntime::registerNativeMethods(env, "android/app/backup/FullBackup",
+            g_methods, NELEM(g_methods));
+}
+
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1c4bffd..2d85ccc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1241,6 +1241,14 @@
         android:description="@string/permdesc_backup"
         android:protectionLevel="signatureOrSystem" />
 
+    <!-- Allows a package to launch the secure full-backup confirmation UI.
+         ONLY the system process may hold this permission.
+         @hide -->
+    <permission android:name="android.permission.CONFIRM_FULL_BACKUP"
+        android:label="@string/permlab_confirm_full_backup"
+        android:description="@string/permdesc_confirm_full_backup"
+        android:protectionLevel="signature" />
+
     <!-- Must be required by a {@link android.widget.RemoteViewsService},
          to ensure that only the system can bind to it. -->
     <permission android:name="android.permission.BIND_REMOTEVIEWS"
@@ -1329,12 +1337,17 @@
           android:protectionLevel="signature" />
     <uses-permission android:name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"/>
 
+    <!-- The system process is explicitly the only one allowed to launch the
+         confirmation UI for full backup/restore -->
+    <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
                  android:label="@string/android_system_label"
                  android:allowClearUserData="false"
                  android:backupAgent="com.android.server.SystemBackupAgent"
+                 android:fullBackupAgent="com.android.server.SystemBackupAgent"
                  android:killAfterRestore="false"
                  android:icon="@drawable/ic_launcher_android">
         <activity android:name="com.android.internal.app.ChooserActivity"
diff --git a/core/res/res/values-large/config.xml b/core/res/res/values-large/config.xml
index 05dd050..c94256e 100644
--- a/core/res/res/values-large/config.xml
+++ b/core/res/res/values-large/config.xml
@@ -22,4 +22,13 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- see comment in values/config.xml -->
     <dimen name="config_prefDialogWidth">440dp</dimen>
+
+    <bool name="config_statusBarCanHide">false</bool>
+
+    <!-- see comment in values/config.xml -->
+    <integer name="config_longPressOnPowerBehavior">2</integer>
+
+    <!-- see comment in values/config.xml -->
+    <integer name="config_longPressOnHomeBehavior">0</integer>
+
 </resources>
diff --git a/core/res/res/values-large/dimens.xml b/core/res/res/values-large/dimens.xml
index 5355847..da36b67 100644
--- a/core/res/res/values-large/dimens.xml
+++ b/core/res/res/values-large/dimens.xml
@@ -17,6 +17,31 @@
 */
 -->
 <resources>
+    <!-- Height of the status bar -->
+    <dimen name="status_bar_height">48dip</dimen>
+    <!-- Width and height of a single notification icon in the status bar -->
+    <dimen name="status_bar_icon_size">32dip</dimen>
+    <!-- Size of the giant number (unread count) in the notifications -->
+    <dimen name="status_bar_content_number_size">48sp</dimen>
+
+    <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
+    <!-- Margin for permanent screen decorations at the bottom. -->
+    <dimen name="screen_margin_bottom">48dip</dimen>
+
+    <!-- Default height of a key in the password keyboard for alpha -->
+    <dimen name="password_keyboard_key_height_alpha">75dip</dimen>
+    <!-- Default height of a key in the password keyboard for numeric -->
+    <dimen name="password_keyboard_key_height_numeric">75dip</dimen>
+    <!-- keyboardHeight = key_height*4 + key_bottom_gap*3 -->
+    <dimen name="password_keyboard_height">48.0mm</dimen>
+
+    <!-- The width that is used when creating thumbnails of applications. -->
+    <dimen name="thumbnail_width">230dp</dimen>
+    <!-- The height that is used when creating thumbnails of applications. -->
+    <dimen name="thumbnail_height">135dp</dimen>
+    <!-- Minimum width of the search view text entry area. -->
+    <dimen name="search_view_text_min_width">192dip</dimen>
+
     <item type="dimen" name="dialog_min_width_major">55%</item>
     <item type="dimen" name="dialog_min_width_minor">80%</item>
 
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 9b04f78..1463bc7 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -685,6 +685,13 @@
          <p>The default value of this attribute is <code>false</code>. -->
     <attr name="restoreAnyVersion" format="boolean" />
 
+    <!-- The agent to use for a *full* backup of the package.  Only system applications
+         can use this to override the ordinary FullBackupAgent with a custom implementation.
+         It's needed strictly for packages with strongly device-specific data, such as the
+         Settings provider.
+         -->
+    <attr name="fullBackupAgent" format="string" />
+
     <!-- The default install location defined by an application. -->
     <attr name="installLocation">
         <!-- Let the system decide ideal install location -->
@@ -782,6 +789,7 @@
         <attr name="killAfterRestore" />
         <attr name="restoreNeedsApplication" />
         <attr name="restoreAnyVersion" />
+        <attr name="fullBackupAgent" />
         <attr name="neverEncrypt" />
         <!-- Request that your application's processes be created with
              a large Dalvik heap.  This applies to <em>all</em> processes
@@ -790,7 +798,7 @@
              to allow multiple applications to use a process, they all must
              use this option consistently or will get unpredictable results. -->
         <attr name="largeHeap" format="boolean" />
-        <!-- Declare that this applicationn can't participate in the normal
+        <!-- Declare that this application can't participate in the normal
              state save/restore mechanism.  Since it is not able to save and
              restore its state on demand,
              it can not participate in the normal activity lifecycle.  It will
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 620d293..990885d 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -623,4 +623,44 @@
          If false, mms read reports are not supported and the preference
          option to enable/disable read reports is removed in the Messaging app. -->
     <bool name="config_mms_read_reports_support">true</bool>
+
+    <!-- National Language Identifier codes for the following two config items.
+         (from 3GPP TS 23.038 V9.1.1 Table 6.2.1.2.4.1):
+          0  - reserved
+          1  - Turkish
+          2  - Spanish (single shift table only)
+          3  - Portuguese
+          4  - Bengali
+          5  - Gujarati
+          6  - Hindi
+          7  - Kannada
+          8  - Malayalam
+          9  - Oriya
+         10  - Punjabi
+         11  - Tamil
+         12  - Telugu
+         13  - Urdu
+         14+ - reserved -->
+
+    <!-- National language single shift tables to enable for SMS encoding.
+         Decoding is always enabled. 3GPP TS 23.038 states that this feature
+         should not be enabled until a formal request is issued by the relevant
+         national regulatory body. Array elements are codes from the table above.
+         Example 1: devices sold in Turkey must include table 1 to conform with
+           By-Law Number 27230. (http://www.btk.gov.tr/eng/pdf/2009/BY-LAW_SMS.pdf)
+         Example 2: devices sold in India should include tables 4 through 13
+           to enable use of the new Release 9 tables for Indic languages. -->
+    <integer-array name="config_sms_enabled_single_shift_tables"></integer-array>
+
+    <!-- National language locking shift tables to enable for SMS encoding.
+         Decoding is always enabled. 3GPP TS 23.038 states that this feature
+         should not be enabled until a formal request is issued by the relevant
+         national regulatory body. Array elements are codes from the table above.
+         Example 1: devices sold in Turkey must include table 1 after the
+           Turkish Telecommunication Authority requires locking shift encoding
+           to be enabled (est. July 2012). (http://www.btk.gov.tr/eng/pdf/2009/BY-LAW_SMS.pdf)
+           See also: http://www.mobitech.com.tr/tr/ersanozturkblog_en/index.php?entry=entry090223-160014
+         Example 2: devices sold in India should include tables 4 through 13
+         to enable use of the new Release 9 tables for Indic languages. -->
+    <integer-array name="config_sms_enabled_locking_shift_tables"></integer-array>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7ca5e98..652d791 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1668,4 +1668,6 @@
   <public type="attr" name="textEditSuggestionsTopWindowLayout" />
   <public type="attr" name="textEditSuggestionItemLayout" />
 
+  <public type="attr" name="fullBackupAgent" />
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 358e0e0..3951623 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -631,6 +631,11 @@
     <string name="permdesc_backup">Allows the application to control the system\'s backup and restore mechanism.  Not for use by normal applications.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_confirm_full_backup">confirm a full backup or restore operation</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_confirm_full_backup">Allows the application to launch the full backup confirmation UI.  Not to be used by any application.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_internalSystemWindow">display unauthorized windows</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_internalSystemWindow">Allows the creation of
diff --git a/docs/html/guide/topics/usb/adk.jd b/docs/html/guide/topics/usb/adk.jd
index c0a0f27..44d7fc2 100644
--- a/docs/html/guide/topics/usb/adk.jd
+++ b/docs/html/guide/topics/usb/adk.jd
@@ -1,90 +1,136 @@
 page.title=Android Open Accessory Development Kit
 @jd:body
 
-<div id="qv-wrapper">
-<div id="qv">
-    <h2>In this document</h2>
-    <ol>
-      <li><a href="#getting-started">Getting Started with the ADK</a>
-        <ol>
-          <li><a href="#installing">Installing the Arduino software and necessary libraries</a></li>
-          <li><a href="#installing-firmware">Installing the firmware to the ADK board</a></li>
-          <li><a href="#running-demokit">Running the DemoKit Android application</a></li>
-          <li><a href="#monitoring">Monitoring the ADK board</a></li>
-        </ol>
-      </li>
-      <li><a href="#how">How an Accessory Communicates with an Android-powered Device
-      in Accessory Mode</a>
-        <ol>
-          <li><a href="#wait">Wait for and detect connected devices</a></li>
-          <li><a href="#determine">Determine the connected device's accessory mode support</a></li>
-          <li><a href="#start">Attempt to start the device in accessory mode</a></li>
-          <li><a href="#establish">Establish communication with the device</a></li>
-        </ol>
-      </li>
-      <li><a href="#firmware">How the ADK board communicates with an Android-powered Device
-      in Accessory Mode</a>
-        <ol>
-          <li><a href="#wait-adk">Wait for and detect connected devices</a></li>
-          <li><a href="#determine-adk">Determine the connected device's accessory mode support</a></li>
-          <li><a href="#start-adk">Attempt to start the device in accessory mode</a></li>
-          <li><a href="#establish-adk">Establish communication with the device</a></li>
-        </ol>
-      </li>
-    </ol>
-</div>
-</div>
+  <div id="qv-wrapper">
+    <div id="qv">
+      <h2>In this document</h2>
 
-  <p>The Android 3.1 platform (also backported to Android 2.3.4) introduces Android Open Accessory support, which allows external USB hardware (an Android USB accessory)
-  to interact with an Android-powered device in a special "accessory" mode. When an Android-powered powered device is in accessory mode,
-  the connected accessory acts as the USB host (powers the bus and enumerates devices) and the Android-powered device acts as the device. 
-  Android USB accessories are specifically designed to attach to Android-powered devices and
-  adhere to a simple protocol (Android accessory protocol) that allows them to detect Android-powered devices that support accessory mode. Accessories must also provide 
-  500mA at 5V for charging power. Many previously released
-  Android-powered devices are only capable of acting as a USB device and cannot initiate connections with external USB devices. Android Open Accessory support
-  overcomes this limitation and allows you to build accessories that can interact with an assortment of Android-powered devices by allowing the accessory
-  initiate the connection.</p>
-  
-  <p class="note"><strong>Note:</strong> Accessory mode is ultimately dependent on the device's hardware
-  and not all devices will support accessory mode. Devices that support accessory mode can be filtered using a <code>&lt;uses-feature&gt;</code>
-  element in your corresponding application's Android manifest. For more information, see the <a href="{@docRoot}guide/topics/usb/accessory.html#manifest">USB Accessory</a> Developer
-  Guide.</p>
-  
-  <p>The Android Open Accessory Development Kit (ADK) provides an implementation of an Android USB accessory that is based on the
-  <a href="http://www.arduino.cc/">Arduino open source electronics prototyping platform</a>, the accessory's hardware design files,
-  code that implements
-  the accessory's firmware, and the Android application that interacts with the
-  accessory. The hardware design files and code are contained in the <a href="https://dl-ssl.google.com/android/adk/adk_release_0506.zip">ADK package download</a>.
-  You can <a href="http://www.rt-net.jp/products/rt-adk">buy the hardware components</a> of the ADK if you do not already have them.
-  The main hardware and software components of the ADK include:</p>
+      <ol>
+        <li>
+          <a href="#getting-started">Getting Started with the ADK</a>
+
+          <ol>
+            <li><a href="#installing">Installing the Arduino software and necessary
+            libraries</a></li>
+
+            <li><a href="#installing-firmware">Installing the firmware to the ADK board</a></li>
+
+            <li><a href="#running-demokit">Running the DemoKit Android application</a></li>
+
+            <li><a href="#monitoring">Monitoring the ADK board</a></li>
+          </ol>
+        </li>
+
+        <li>
+          <a href="#how">How an Accessory Communicates with an Android-powered Device in Accessory
+          Mode</a>
+
+          <ol>
+            <li><a href="#wait">Wait for and detect connected devices</a></li>
+
+            <li><a href="#determine">Determine the connected device's accessory mode
+            support</a></li>
+
+            <li><a href="#start">Attempt to start the device in accessory mode</a></li>
+
+            <li><a href="#establish">Establish communication with the device</a></li>
+          </ol>
+        </li>
+
+        <li>
+          <a href="#firmware">How the ADK board communicates with an Android-powered Device in
+          Accessory Mode</a>
+
+          <ol>
+            <li><a href="#wait-adk">Wait for and detect connected devices</a></li>
+
+            <li><a href="#determine-adk">Determine the connected device's accessory mode
+            support</a></li>
+
+            <li><a href="#start-adk">Attempt to start the device in accessory mode</a></li>
+
+            <li><a href="#establish-adk">Establish communication with the device</a></li>
+          </ol>
+        </li>
+      </ol>
+
+      <h2>Where to Buy</h2>
+
+      <ol>
+        <li><a href=
+        "http://www.rt-shop.sakura.ne.jp/rt-shop/index.php?main_page=product_info&amp;products_id=2731&language=en">
+        RT Corp</a></li>
+
+        <li><a href=
+        "http://www.microchip.com/android">
+        Microchip</a></li>
+      </ol>
+    </div>
+  </div>
+
+  <p>The Android 3.1 platform (also backported to Android 2.3.4) introduces Android Open Accessory
+  support, which allows external USB hardware (an Android USB accessory) to interact with an
+  Android-powered device in a special "accessory" mode. When an Android-powered powered device is
+  in accessory mode, the connected accessory acts as the USB host (powers the bus and enumerates
+  devices) and the Android-powered device acts as the device. Android USB accessories are
+  specifically designed to attach to Android-powered devices and adhere to a simple protocol
+  (Android accessory protocol) that allows them to detect Android-powered devices that support
+  accessory mode. Accessories must also provide 500mA at 5V for charging power. Many previously
+  released Android-powered devices are only capable of acting as a USB device and cannot initiate
+  connections with external USB devices. Android Open Accessory support overcomes this limitation
+  and allows you to build accessories that can interact with an assortment of Android-powered
+  devices by allowing the accessory initiate the connection.</p>
+
+  <p class="note"><strong>Note:</strong> Accessory mode is ultimately dependent on the device's
+  hardware and not all devices will support accessory mode. Devices that support accessory mode can
+  be filtered using a <code>&lt;uses-feature&gt;</code> element in your corresponding application's
+  Android manifest. For more information, see the <a href=
+  "{@docRoot}guide/topics/usb/accessory.html#manifest">USB Accessory</a> Developer Guide.</p>
+
+  <p>The Android Open Accessory Development Kit (ADK) provides an implementation of an Android USB
+  accessory that is based on the <a href="http://www.arduino.cc/">Arduino open source electronics
+  prototyping platform</a>, the accessory's hardware design files, code that implements the
+  accessory's firmware, and the Android application that interacts with the accessory. The hardware
+  design files and code are contained in the <a href=
+  "https://dl-ssl.google.com/android/adk/adk_release_0506.zip">ADK package download</a>. You can
+  <a href=
+  "http://www.rt-shop.sakura.ne.jp/rt-shop/index.php?main_page=product_info&amp;products_id=2731&language=en">buy
+  the hardware components</a> of the ADK if you do not already have them. There is also a <a href=
+  "http://www.microchip.com/android">
+  PIC based USB microcontroller</a> that is not based on the ADK design, but that you can still use
+  to create your own Android open accessories. We expect more hardware distributers to create a
+  variety of kits, so please stay tuned for further developments.</p>
+
+  <p>The main hardware and software components of the ADK include:</p>
 
   <ul>
-    <li>A USB micro-controller board that is based on the Arduino Mega2560 and Circuits@Home USB Host
-    Shield designs (now referred to as the ADK board), which you will later implement as an Android USB accessory.
-    The ADK board provides input and output pins that you can implement through the use of attachments called "shields."
-    Custom firmware, written in C++, is installed on the board to define the board's functionality and interaction with
-    the attached shield and Android-powered device. The hardware design files for the
-    board are located in <code>hardware/</code> directory.</li>
+    <li>A USB micro-controller board that is based on the Arduino Mega2560 and Circuits@Home USB
+    Host Shield designs (now referred to as the ADK board), which you will later implement as an
+    Android USB accessory. The ADK board provides input and output pins that you can implement
+    through the use of attachments called "shields." Custom firmware, written in C++, is installed
+    on the board to define the board's functionality and interaction with the attached shield and
+    Android-powered device. The hardware design files for the board are located in
+    <code>hardware/</code> directory.</li>
 
-    <li>An Android Demo Shield (ADK shield) that affixes atop the ADK board implements the
-    input and output points on the board. These implementations include a joystick, LED
-    outputs, and temperature and light sensors. You can create or buy your own shields or wire
-    your own features to the ADK board to implement custom functionality. 
-    The hardware design files for the shield are located in <code>hardware/</code>.</li>
+    <li>An Android Demo Shield (ADK shield) that affixes atop the ADK board implements the input
+    and output points on the board. These implementations include a joystick, LED outputs, and
+    temperature and light sensors. You can create or buy your own shields or wire your own features
+    to the ADK board to implement custom functionality. The hardware design files for the shield
+    are located in <code>hardware/</code>.</li>
 
     <li>A library based on the <a href=
     "http://www.circuitsathome.com/arduino_usb_host_shield_projects">Arduino USB Host Shield</a>
     library provides the logic for the USB micro-controller board to act as a USB Host. This allows
-    the board to initiate transactions with USB devices. Describing how to
-    use this entire library is out of the scope of this document. Where needed, this document
-    points out important interactions with the library. For more information, see the source code
-    for the Arduino USB Host Shield library in the
-    <code>firmware/arduino_libs/USB_Host_Shield</code> directory.</li>
+    the board to initiate transactions with USB devices. Describing how to use this entire library
+    is out of the scope of this document. Where needed, this document points out important
+    interactions with the library. For more information, see the source code for the Arduino USB
+    Host Shield library in the <code>firmware/arduino_libs/USB_Host_Shield</code> directory.</li>
 
     <li>An Arduino sketch, <code>firmware/demokit/demokit.pde</code>, defines the firmware that
-    runs on the ADK board and is written in C++. The sketch calls the Android accessory protocol library
-    to interact with the Android-powered device. It also sends data from the ADK board and shield to the Android application
-    and receives data from the Android application and outputs it to the ADK board and shield.</li>
+    runs on the ADK board and is written in C++. The sketch calls the Android accessory protocol
+    library to interact with the Android-powered device. It also sends data from the ADK board and
+    shield to the Android application and receives data from the Android application and outputs it
+    to the ADK board and shield.</li>
 
     <li>The Android accessory protocol library, which is located in the
     <code>firmware/arduino_libs/AndroidAccessory</code> directory. This library defines how to
@@ -113,9 +159,10 @@
 
   <h2 id="getting-started">Getting Started with the ADK</h2>
 
-  <p>The following sections describe how to install the Arduino software on your computer, use the Arduino software
-  to install the ADK board's firmware, and install and run the accompanying Android application for the ADK board.
-  Before you begin, download the following items to set up your development environment:</p>
+  <p>The following sections describe how to install the Arduino software on your computer, use the
+  Arduino software to install the ADK board's firmware, and install and run the accompanying
+  Android application for the ADK board. Before you begin, download the following items to set up
+  your development environment:</p>
 
   <ul>
     <li><a href="http://www.arduino.cc/en/Main/software">Arduino Software</a>: contains libraries
@@ -125,28 +172,34 @@
     libraries to sense human capacitance. This is needed for the capacative button that is located
     on the ADK shield.</li>
 
-    <li><a href="">The ADK package</a>: contains the firmware for the ADK board and
-    hardware design files for the ADK board and shield.</li>
+    <li><a href="">The ADK package</a>: contains the firmware for the ADK board and hardware design
+    files for the ADK board and shield.</li>
   </ul>
 
-<h3 id="installing">Installing the Arduino software and necessary libraries</h3>
-<p>To install the Arduino software:</p>
+  <h3 id="installing">Installing the Arduino software and necessary libraries</h3>
+
+  <p>To install the Arduino software:</p>
+
   <ol>
-    <li><a href="http://arduino.cc/en/Guide/HomePage">Download and install</a> the Arduino Software
-    as described on the Arduino website.
-    
-    <p class="note"><strong>Note:</strong> If you are on a Mac, install the FTDI USB Serial Driver
-    that is included in the Arduino package,
-    even though the installation instructions say otherwise.</p>
+    <li>
+      <a href="http://arduino.cc/en/Guide/HomePage">Download and install</a> the Arduino Software
+      as described on the Arduino website.
+
+      <p class="note"><strong>Note:</strong> If you are on a Mac, install the FTDI USB Serial
+      Driver that is included in the Arduino package, even though the installation instructions say
+      otherwise.</p>
     </li>
 
-    <li><a href="https://dl-ssl.google.com/android/adk/adk_release_0506.zip">Download</a> and extract the ADK package to a directory of your choice. You should have
-    an <code>app</code>, <code>firmware</code>, and <code>hardware</code> directories.</li>
+    <li><a href="https://dl-ssl.google.com/android/adk/adk_release_0506.zip">Download</a> and
+    extract the ADK package to a directory of your choice. You should have an <code>app</code>,
+    <code>firmware</code>, and <code>hardware</code> directories.</li>
 
     <li>Extract the CapSense download to a directory of your choice.</li>
 
     <li>Install the necessary libraries:
+
       <p>On Windows:</p>
+
       <ol type="a">
         <li>Copy the <code>firmware/arduino_libs/AndroidAccessory</code> and
         <code>firmware/arduino_libs/USB_Host_Shield</code> directories (the complete directories,
@@ -161,64 +214,73 @@
       </ol>
 
       <p>On Mac:</p>
+
       <ol type="a">
         <li>Right-click on the Arduino application in Finder and select <strong>Show Package
         Contents</strong>.</li>
 
         <li>Copy the <code>firmware/arduino_libs/AndroidAccessory</code> and
         <code>firmware/arduino_libs/USB_Host_Shield</code> directories (the complete directories,
-        not just the files within) to the <code>Contents/Resources/Java/libraries</code>
-        directory inside the Arduino application.</li>
+        not just the files within) to the <code>Contents/Resources/Java/libraries</code> directory
+        inside the Arduino application.</li>
 
         <li>Create a <code>CapSense</code> directory in the
         <code>Contents/Resources/Java/libraries</code> directory.</li>
 
         <li>Copy <code>CapSense.cpp</code> and <code>CapSense.h</code> from the unzipped CapSense
         download to the <code>CapSense</code> directory.</li>
-        
       </ol>
-      
+
       <p>On Linux (Ubuntu):</p>
-       <ol type="a">
+
+      <ol type="a">
         <li>Copy the <code>firmware/arduino_libs/AndroidAccessory</code> and
         <code>firmware/arduino_libs/USB_Host_Shield</code> directories (the complete directories,
-        not just the files within) to the <code>&lt;arduino_installation_root&gt;/libraries/</code> directory.</li>
+        not just the files within) to the <code>&lt;arduino_installation_root&gt;/libraries/</code>
+        directory.</li>
 
         <li>Create a <code>CapSense</code> directory in the
         <code>&lt;arduino_installation_root&gt;/libraries/</code> directory.</li>
 
         <li>Copy <code>CapSense.cpp</code> and <code>CapSense.h</code> from the unzipped CapSense
         download to the <code>CapSense</code> directory.</li>
-        
-        <li>Install the avr-libc library by entering <code>sudo apt-get install avr-libc</code> from a shell prompt.</li>
+
+        <li>Install the avr-libc library by entering <code>sudo apt-get install avr-libc</code>
+        from a shell prompt.</li>
       </ol>
+    </li>
+  </ol>
 
-</li>
-</ol>
-      <p>You should now have three new directories in the Arduino libraries directory:
-      <code>AndroidAccessory</code>, <code>USB_Host_Shield</code>, and <code>CapSense</code>.</p>
+  <p>You should now have three new directories in the Arduino libraries directory:
+  <code>AndroidAccessory</code>, <code>USB_Host_Shield</code>, and <code>CapSense</code>.</p>
 
-<h3 id="installing-firmware">Installing the firmware to the ADK board</h3>
-<p>To install the firmware to the ADK board:</p>
-<ol>
-    <li>Connect the ADK board to your computer using the micro-USB port, which allows two-way communication and provides power to the ADK board.</li>
+  <h3 id="installing-firmware">Installing the firmware to the ADK board</h3>
+
+  <p>To install the firmware to the ADK board:</p>
+
+  <ol>
+    <li>Connect the ADK board to your computer using the micro-USB port, which allows two-way
+    communication and provides power to the ADK board.</li>
 
     <li>Launch Arduino.</li>
 
-    <li>Click <strong>Tools &gt; Board &gt; Arduino Mega 2560</strong> to specify the ADK board's type.</li>
+    <li>Click <strong>Tools &gt; Board &gt; Arduino Mega 2560</strong> to specify the ADK board's
+    type.</li>
 
     <li>Select the appropriate USB port:
-    <ul>
-    <li>On Windows: click <strong>Tools &gt; Serial Port &gt; COM#</strong> to specify the port of
-    communication. The COM port number varies depending on your computer. COM1 is usually reserved for serial port
-    connections. You most likely want COM2 or COM3.  </li>  
-    
-    <li>On Mac: Click <strong>Tools &gt; Serial Port &gt; dev/tty.usbserial-###</strong> to specify the
-    port of communication.</li>
-    
-    <li>On Linux (Ubuntu): Click <strong>Tools &gt; Serial Port &gt; dev/ttyUSB#</strong> to specify the
-    port of communication.</li>
-    </ul></li>
+
+      <ul>
+        <li>On Windows: click <strong>Tools &gt; Serial Port &gt; COM#</strong> to specify the port
+        of communication. The COM port number varies depending on your computer. COM1 is usually
+        reserved for serial port connections. You most likely want COM2 or COM3.</li>
+
+        <li>On Mac: Click <strong>Tools &gt; Serial Port &gt; dev/tty.usbserial-###</strong> to
+        specify the port of communication.</li>
+
+        <li>On Linux (Ubuntu): Click <strong>Tools &gt; Serial Port &gt; dev/ttyUSB#</strong> to
+        specify the port of communication.</li>
+      </ul>
+    </li>
 
     <li>To open the firmware code (a sketch), click <strong>File &gt; Open</strong> and select
     <code>firmware/demokit/demokit.pde</code>.</li>
@@ -226,13 +288,11 @@
     <li>Click <strong>Sketch &gt; Compile/Verify</strong> to ensure that the sketch has no
     errors.</li>
 
-    <li>Select <strong>File &gt; Upload to I/O Board</strong>. When Arduino outputs <strong>Done uploading.</strong>, the board 
-    is ready to communicate with your Android-powered device.</li>
-    
-    </ol>
-    
+    <li>Select <strong>File &gt; Upload to I/O Board</strong>. When Arduino outputs <strong>Done
+    uploading.</strong>, the board is ready to communicate with your Android-powered device.</li>
+  </ol>
 
-<h3 id="running-demokit">Running the DemoKit Android application</h3>
+  <h3 id="running-demokit">Running the DemoKit Android application</h3>
 
   <p>The DemoKit Android application runs on your Android-powered device and communicates with the
   ADK board. The ADK board receives commands such as lighting up the board's LEDs or sends data
@@ -241,53 +301,68 @@
   <p>To install and run the application in Eclipse:</p>
 
   <ol>
-    <li><a href="http://code.google.com/android/add-ons/google-apis/installing.html">Install the Google APIs API Level 10 add-on library</a>, 
-    which includes the Open Accessory library for 2.3.4 devices that support accessory mode.
-    This library is also forward compatible with Android 3.1 or newer devices that support accessory mode. If you only care
-    about Android 3.1 or newer devices, all you need is API Level 12. For more information
-    on deciding which API level to use, see the <a href="{@docRoot}guide/topics/USB/accessory.html#choosing">USB Accessory</a> documentation.</li>
-    <li>Click <strong>File &gt; New &gt; Project...</strong>, then select <strong>Android &gt; Android Project</strong></li>
-    <li>In the <strong>Project name:</strong> field, type DemoKit.
-    <li>Choose <strong>Create project from existing source</strong>, click <strong>Browse</strong>, select the
-    <code>app</code> directory, and click <strong>Finish</strong>.</li>
+    <li><a href="http://code.google.com/android/add-ons/google-apis/installing.html">Install the
+    Google APIs API Level 10 add-on library</a>, which includes the Open Accessory library for
+    2.3.4 devices that support accessory mode. This library is also forward compatible with Android
+    3.1 or newer devices that support accessory mode. If you only care about Android 3.1 or newer
+    devices, all you need is API Level 12. For more information on deciding which API level to use,
+    see the <a href="{@docRoot}guide/topics/USB/accessory.html#choosing">USB Accessory</a>
+    documentation.</li>
+
+    <li>Click <strong>File &gt; New &gt; Project...</strong>, then select <strong>Android &gt;
+    Android Project</strong></li>
+
+    <li>In the <strong>Project name:</strong> field, type DemoKit.</li>
+
+    <li>Choose <strong>Create project from existing source</strong>, click <strong>Browse</strong>,
+    select the <code>app</code> directory, and click <strong>Finish</strong>.</li>
 
     <li>For Build Target, select <strong>Google APIs</strong> (Platform 2.3.3, API Level 10).
-    <p class="note"><strong>Note:</strong> Even though the add-on is labeled as 
-    <strong>2.3.3</strong>, the newest Google API add-on library for API level 10 adds
-    USB Open Accessory API support for 2.3.4 devices.</li>
+
+      <p class="note"><strong>Note:</strong> Even though the add-on is labeled as
+      <strong>2.3.3</strong>, the newest Google API add-on library for API level 10 adds USB Open
+      Accessory API support for 2.3.4 devices.</p>
+    </li>
 
     <li>Click <strong>Finish</strong>.</li>
+
     <li>Install the application to your device.</li>
 
-    <li>Connect the ADK board (USB-A) to your Android-powered device (micro-USB). Ensure that the power cable to the
-    accessory is plugged in or that the micro-USB port on the accesory is connected to your computer
-    for power (this also allows you to <a href="monitoring">monitor the ADK board</a>). When
-    connected, accept the prompt that asks for whether or not to open the
-    DemoKit application to connect to the accessory. If the prompt does not show up, connect and reconnect the accessory.</li>
+    <li>Connect the ADK board (USB-A) to your Android-powered device (micro-USB). Ensure that the
+    power cable to the accessory is plugged in or that the micro-USB port on the accesory is
+    connected to your computer for power (this also allows you to <a href="monitoring">monitor the
+    ADK board</a>). When connected, accept the prompt that asks for whether or not to open the
+    DemoKit application to connect to the accessory. If the prompt does not show up, connect and
+    reconnect the accessory.</li>
   </ol>
-  <p>You can now interact with the ADK board by moving the color LED or servo sliders (make sure the servos are connected) 
-  or by pressing the relay buttons in the application. On the ADK shield, you can press the
-  buttons and move the joystick to see their outputs displayed in the application.</p>
 
-<h3 id="monitoring">Monitoring the ADK Board</h3>
-<p>The ADK firmware consists of a few files that you should be looking at if you want to build your own accessory.
-The files in the <code>firmware/arduino_libs/AndroidAccessory</code> directory are the most important files and have the logic to detect and connect
-to Android-powered devices that support accessory mode. Feel free to add debug statements (Arduino <code>Serial.print()</code> statements) to the code located in the
-<code>arduino_libraries_directory/AndroidAccessory</code> directory and <code>firmware/demokit/demokit.pde</code> sketch and re-upload the sketch to the ADK board to discover more
-about how the firmware works.</p>
+  <p>You can now interact with the ADK board by moving the color LED or servo sliders (make sure
+  the servos are connected) or by pressing the relay buttons in the application. On the ADK shield,
+  you can press the buttons and move the joystick to see their outputs displayed in the
+  application.</p>
 
-<p>You can view the debug statements in the Arduino Serial Monitor by clicking
-<strong>Tools > Serial Monitor</strong> and setting the baud to 115200.
-The following sections about how accessories communicate with Android-powered
-devices describe much of what you should be doing in your own accessory.</p>
-</p>
+  <h3 id="monitoring">Monitoring the ADK Board</h3>
+
+  <p>The ADK firmware consists of a few files that you should be looking at if you want to build
+  your own accessory. The files in the <code>firmware/arduino_libs/AndroidAccessory</code>
+  directory are the most important files and have the logic to detect and connect to
+  Android-powered devices that support accessory mode. Feel free to add debug statements (Arduino
+  <code>Serial.print()</code> statements) to the code located in the
+  <code>arduino_libraries_directory/AndroidAccessory</code> directory and
+  <code>firmware/demokit/demokit.pde</code> sketch and re-upload the sketch to the ADK board to
+  discover more about how the firmware works.</p>
+
+  <p>You can view the debug statements in the Arduino Serial Monitor by clicking <strong>Tools &gt;
+  Serial Monitor</strong> and setting the baud to 115200. The following sections about how
+  accessories communicate with Android-powered devices describe much of what you should be doing in
+  your own accessory.</p>
 
   <h2 id="how">How an Accessory Communicates with an Android-powered Device in Accessory Mode</h2>
 
   <p>When you connect an accessory to an Android-powered device, the accessory's firmware must
-  carry out some standard steps to set up communication with the Android-powered device. If you are building an
-  accessory along with an application, this section goes over some general steps that your firmware
-  should carry out.</p>
+  carry out some standard steps to set up communication with the Android-powered device. If you are
+  building an accessory along with an application, this section goes over some general steps that
+  your firmware should carry out.</p>
 
   <p>In general, an accessory should carry out the following steps:</p>
 
@@ -301,27 +376,31 @@
     <li>Establish communication with the device if it supports the Android accessory protocol</li>
   </ol>
 
-  <h3 id="wait"><p>Wait for and detect connected devices</h3>Your accessory should have logic to
-  continuously check for connected Android-powered devices. When a device is connected, your accessory should
+  <h3 id="wait">Wait for and detect connected devices</h3>
+
+  <p>Your accessory should have logic to continuously check
+  for connected Android-powered devices. When a device is connected, your accessory should
   determine if the device supports accessory mode.</p>
 
-  <h3 id="determine"><p>Determine the device's accessory mode support</h3>
+  <h3 id="determine">Determine the device's accessory mode support</h3>
+
+
   <p>When an Android-powered device is connected, it can be in one of three states:</p>
 
   <ol type="a">
-  
     <li>The attached device supports Android accessory mode and is already in accessory mode.</li>
-    <li>The attached device supports Android accessory mode, but it is not in accessory mode.</li>
-    <li>The attached device does not support Android accessory mode.</li>
 
-    
+    <li>The attached device supports Android accessory mode, but it is not in accessory mode.</li>
+
+    <li>The attached device does not support Android accessory mode.</li>
   </ol>
 
-  <p>During the initial connection, the accessory should check the vendor and product IDs of the connected device's USB
-  device descriptor. The vendor ID should match Google's ID (0x18D1) and the product ID should be
-  0x2D00 or 0x2D01 if the device is already in accessory mode (case A). If so, the accessory can now <a href=
-  "#establish">establish communication with the device</a> through bulk transfer endpoints with its
-  own communication protocol. There is no need to start the device in accessory mode.</p>
+  <p>During the initial connection, the accessory should check the vendor and product IDs of the
+  connected device's USB device descriptor. The vendor ID should match Google's ID (0x18D1) and the
+  product ID should be 0x2D00 or 0x2D01 if the device is already in accessory mode (case A). If so,
+  the accessory can now <a href="#establish">establish communication with the device</a> through
+  bulk transfer endpoints with its own communication protocol. There is no need to start the device
+  in accessory mode.</p>
 
   <p class="note"><strong>Note:</strong> 0x2D00 is reserved for Android-powered devices that
   support accessory mode. 0x2D01 is reserved for devices that support accessory mode as well as the
@@ -331,37 +410,39 @@
   implementing a passthrough to ADB on the device.</p>
 
   <p>If the vendor and product ID do not match, there is no way to distinguish between states b and
-  c, so the accessory <a href="#start">attempts to start the device in accessory mode</a> to
-  figure out if the device is supported.</p>
+  c, so the accessory <a href="#start">attempts to start the device in accessory mode</a> to figure
+  out if the device is supported.</p>
 
   <h3 id="start">Attempt to start the device in accessory mode</h3>
 
-  <p>If the vendor and product IDs do not correspond to an Android-powered device in accessory mode, the accessory
-  cannot discern whether the device supports accessory mode and is not in that state, or if the
-  device does not support accessory mode at all. This is because devices that support accessory mode but aren't in it
-  initially report the device's manufacturer vendor ID and product ID, and not the special Google ones.
-  In either case, the accessory should try to start the device
-  into accessory mode to figure out if the device supports it. The following steps explain how to do this:</p>
-  
+  <p>If the vendor and product IDs do not correspond to an Android-powered device in accessory
+  mode, the accessory cannot discern whether the device supports accessory mode and is not in that
+  state, or if the device does not support accessory mode at all. This is because devices that
+  support accessory mode but aren't in it initially report the device's manufacturer vendor ID and
+  product ID, and not the special Google ones. In either case, the accessory should try to start
+  the device into accessory mode to figure out if the device supports it. The following steps
+  explain how to do this:</p>
+
   <ol>
-  <li>Send a 51 control request ("Get Protocol") to figure out if the device supports
-  the Android accessory protocol. A non-zero number is returned if the protocol is supported, which
-  represents the version of the protocol that the device supports (currently, only version 1
-  exists). This request is a control request on endpoint 0 with the following characteristics:
-  <pre>
+    <li>Send a 51 control request ("Get Protocol") to figure out if the device supports the Android
+    accessory protocol. A non-zero number is returned if the protocol is supported, which
+    represents the version of the protocol that the device supports (currently, only version 1
+    exists). This request is a control request on endpoint 0 with the following characteristics:
+      <pre>
 requestType:    USB_DIR_IN | USB_TYPE_VENDOR
 request:        51
 value:          0
 index:          0
 data:           protocol version number (16 bits little endian sent from the device to the accessory)
 </pre>
-</li>
+    </li>
 
-  <li>If the device returns a proper protocol version, send identifying string information to the device.
-  This information allows the device to figure out an appropriate application for this accessory and also present the user
-  with a URL if an appropriate application does not exist. These requests
-  are control requests on endpoint 0 (for each string ID) with the following characteristics:
-  <pre>
+    <li>If the device returns a proper protocol version, send identifying string information to the
+    device. This information allows the device to figure out an appropriate application for this
+    accessory and also present the user with a URL if an appropriate application does not exist.
+    These requests are control requests on endpoint 0 (for each string ID) with the following
+    characteristics:
+      <pre>
 requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
 request:        52
 value:          0
@@ -369,9 +450,9 @@
 data            zero terminated UTF8 string sent from accessory to device
 </pre>
 
-  <p>The following string IDs are supported, with a maximum size of 256 bytes for each string (must
-  be zero terminated with \0).</p>
-<pre>
+      <p>The following string IDs are supported, with a maximum size of 256 bytes for each string
+      (must be zero terminated with \0).</p>
+      <pre>
 manufacturer name:  1
 model name:         2
 description:        3
@@ -379,67 +460,73 @@
 URI:                5
 serial number:      6
 </pre>
-</li>
+    </li>
 
-  <li>When the identifying strings are sent, request the device start up in accessory mode.
-  This request is a control request on endpoint 0 with the following
-  characteristics:
-  <pre>
+    <li>When the identifying strings are sent, request the device start up in accessory mode. This
+    request is a control request on endpoint 0 with the following characteristics:
+      <pre>
 requestType:    USB_DIR_OUT | USB_TYPE_VENDOR
 request:        53
 value:          0
 index:          0
 data:           none
 </pre>
-</li>
-</ol>
-  <p>After sending the final control request, the connected USB device should re-introduce itself on the bus
-  in accessory mode and the accessory can re-enumerate the connected devices. The algorithm jumps back to
-  <a href="#determine">determining the device's accessory mode support</a> to check for the
-  vendor and product ID. The vendor ID and product ID of the device will be different if the device
-  successfully switched to accessory mode and will now correspond to Google's vendor and product
-  IDs instead of the device manufacturer's IDs. The accessory can now <a href="#establish">establish communication with the device</a>.</p>
+    </li>
+  </ol>
+
+  <p>After sending the final control request, the connected USB device should re-introduce itself
+  on the bus in accessory mode and the accessory can re-enumerate the connected devices. The
+  algorithm jumps back to <a href="#determine">determining the device's accessory mode support</a>
+  to check for the vendor and product ID. The vendor ID and product ID of the device will be
+  different if the device successfully switched to accessory mode and will now correspond to
+  Google's vendor and product IDs instead of the device manufacturer's IDs. The accessory can now
+  <a href="#establish">establish communication with the device</a>.</p>
 
   <p>If at any point these steps fail, the device does not support Android accessory mode and the
   accessory should wait for the next device to be connected.</p>
-  
 
   <h3 id="establish">Establish communication with the device</h3>
 
-  <p>If an Android-powered device in accessory mode is detected, the accessory can query the device's interface and
-  endpoint descriptors to obtain the bulk endpoints to communicate with the device. An
-  Android-powered device that has a product ID of 0x2D00 has one interface with two bulk endpoints for
-  input and output communication. A device with product ID of 0x2D01 has two interfaces with two bulk endpoints
-  each for input and output communication. The first interface is for standard communication while the second
-  interface is for ADB communication. To communicate on an interface, all you need to do is find the first bulk input and output endpoints,
-  set the device's configuration to a value of 1 with a SET_CONFIGURATION (0x09) device request, then communicate using the endpoints.</p>
+  <p>If an Android-powered device in accessory mode is detected, the accessory can query the
+  device's interface and endpoint descriptors to obtain the bulk endpoints to communicate with the
+  device. An Android-powered device that has a product ID of 0x2D00 has one interface with two bulk
+  endpoints for input and output communication. A device with product ID of 0x2D01 has two
+  interfaces with two bulk endpoints each for input and output communication. The first interface
+  is for standard communication while the second interface is for ADB communication. To communicate
+  on an interface, all you need to do is find the first bulk input and output endpoints, set the
+  device's configuration to a value of 1 with a SET_CONFIGURATION (0x09) device request, then
+  communicate using the endpoints.</p>
 
-  
+  <h2 id="firmware">How the ADK board communicates with an Android-powered Device in Accessory
+  Mode</h2>
 
-  <h2 id="firmware">How the ADK board communicates with an Android-powered Device in Accessory Mode</h2>
+  <p>If you have access to the ADK board and shield, the following sections describe the firmware
+  code that you installed onto the ADK board. The firmware demonstrates a practical example of how
+  to communicate with an Android-powered device. Even if you do not have the ADK board and shield,
+  reading through how the hardware detects and interacts with devices in accessory mode is still
+  useful if you want to port the code over for your own accessories.</p>
 
-  <p>If you have access to the ADK board and shield, the following sections describe the firmware code that you installed onto the ADK board. The firmware demonstrates a practical
-  example of how to communicate with an Android-powered device. Even if you do not have the ADK board and shield, reading through how the hardware detects
-  and interacts with devices in accessory mode is still useful if you want to port the code over for your own accessories.</p>
+  <p>The important pieces of the firmware are the
+  <code>accessory/demokit/demokit/demokit.pde</code> sketch, which is the code that receives and
+  sends data to the DemoKit application running on the Android-powered device. The code to detect
+  and set up communication with the Android-powered device is contained in the
+  <code>accessory/arduino_libs/AndroidAccessory/AndroidAccessory.h</code> and
+  <code>accessory/arduino_libs/AndroidAccessory/AndroidAccessory.cpp</code> files. This code
+  includes most of the logic that will help you implement your own accessory's firmware. It might
+  be useful to have all three of these files open in a text editor as you read through these next
+  sections.</p>
 
-  <p>The important pieces of the firmware are the <code>accessory/demokit/demokit/demokit.pde</code> sketch, which is the code that
-  receives and sends data to the DemoKit application running on the Android-powered device. The
-  code to detect and set up communication with the Android-powered device is contained in the
-  <code>accessory/arduino_libs/AndroidAccessory/AndroidAccessory.h</code> and <code>accessory/arduino_libs/AndroidAccessory/AndroidAccessory.cpp</code>
-  files. This code includes most of the logic that will help you implement your own accessory's firmware.
-  It might be useful to have all three of these files open in a text editor as you read through these next sections.</p>
-
-  <p>The following sections describe the firmware code in the context of the
-  algorithm described in <a href="#how">How an Accessory Communicates with an Android-powered
-  Device in Accessory Mode</a>.</p>
+  <p>The following sections describe the firmware code in the context of the algorithm described in
+  <a href="#how">How an Accessory Communicates with an Android-powered Device in Accessory
+  Mode</a>.</p>
 
   <h3 id="wait-adk">Wait for and detect connected devices</h3>
 
-  <p>In the firmware code (<code>demokit.pde</code>), the
-  <code>loop()</code> function runs repeatedly and calls
-  <code>AndroidAccessory::isConnected()</code> to check for any connected devices. If there is a
-  connected device, it continuously updates the input and output streams going to and from the
-  board and application. If nothing is connected, it continuously checks for a device to be connected:</p>
+  <p>In the firmware code (<code>demokit.pde</code>), the <code>loop()</code> function runs
+  repeatedly and calls <code>AndroidAccessory::isConnected()</code> to check for any connected
+  devices. If there is a connected device, it continuously updates the input and output streams
+  going to and from the board and application. If nothing is connected, it continuously checks for
+  a device to be connected:</p>
   <pre>
 ...
 
@@ -466,16 +553,16 @@
 
   <h3 id="determine-adk">Determine the connected device's accessory mode support</h3>
 
-  <p>When a device is connected to the ADK board, it can already be in accessory mode,
-  support accessory mode and is not in that mode, or does not support accessory mode. The
+  <p>When a device is connected to the ADK board, it can already be in accessory mode, support
+  accessory mode and is not in that mode, or does not support accessory mode. The
   <code>AndroidAccessory::isConnected()</code> method checks for these cases and responds
   accordingly when the <code>loop()</code> function calls it. This function first checks to see if
   the device that is connected hasn't already been handled. If not, it gets the connected device's
   device descriptor to figure out if the device is already in accessory mode by calling
   <code>AndroidAccessory::isAccessoryDevice()</code>. This method checks the vendor and product ID
   of the device descriptor. A device in accessory mode has a vendor ID of 0x18D1 and a product ID
-  of 0x2D00 or 0x2D01. If the device is in accessory mode, then the ADK board can
-  <a href="#establish-a">establish communication with the device</a>. If not, the board <a href=
+  of 0x2D00 or 0x2D01. If the device is in accessory mode, then the ADK board can <a href=
+  "#establish-a">establish communication with the device</a>. If not, the board <a href=
   "start-a">attempts to start the device in accessory mode</a>.</p>
   <pre>
 bool AndroidAccessory::isConnected(void)
@@ -516,14 +603,13 @@
 
   <h3 id="start-adk">Attempt to start the device in accessory mode</h3>
 
-  <p>If the device is not already in accessory mode, then the ADK board must
-  determine whether or not it supports it by sending control request 51 to check the version of the
-  USB accessory protocol that the device supports (see
-  <code>AndroidAccessory::getProtocol()</code>). Protocol version 1 is the only version for now, but this can
-  be an integer greater than zero in the future. If
-  the appropriate protocol version is returned, the board sends control request 52 (one for each
-  string with <code>AndroidAcessory:sendString()</code>) to send it's identifying information, and
-  tries to start the device in accessory mode with control request 53. The
+  <p>If the device is not already in accessory mode, then the ADK board must determine whether or
+  not it supports it by sending control request 51 to check the version of the USB accessory
+  protocol that the device supports (see <code>AndroidAccessory::getProtocol()</code>). Protocol
+  version 1 is the only version for now, but this can be an integer greater than zero in the
+  future. If the appropriate protocol version is returned, the board sends control request 52 (one
+  for each string with <code>AndroidAcessory:sendString()</code>) to send it's identifying
+  information, and tries to start the device in accessory mode with control request 53. The
   <code>AndroidAccessory::switchDevice()</code> method takes care of this:</p>
   <pre>
 bool AndroidAccessory::switchDevice(byte addr)
@@ -555,9 +641,9 @@
   <h3 id="establish-adk">Establish communication with the device</h3>
 
   <p>If a device is detected as being in accessory mode, the accessory must find the proper bulk
-  endpoints and set up communication with the device. When the ADK board detects an
-  Android-powered device in accessory mode, it calls the
-  <code>AndroidAccessory::configureAndroid()</code> function:</p>
+  endpoints and set up communication with the device. When the ADK board detects an Android-powered
+  device in accessory mode, it calls the <code>AndroidAccessory::configureAndroid()</code>
+  function:</p>
   <pre>
 ...
 if (isAccessoryDevice(devDesc)) {
@@ -581,16 +667,16 @@
 ...
 </pre>
 
-  <p>The <code>AndroidAccessory::findEndpoints()</code> function queries the Android-powered device's
-  configuration descriptor and finds the bulk data endpoints in which to communicate with the USB
-  device. To do this, it first gets the device's first four bytes of the configuration
+  <p>The <code>AndroidAccessory::findEndpoints()</code> function queries the Android-powered
+  device's configuration descriptor and finds the bulk data endpoints in which to communicate with
+  the USB device. To do this, it first gets the device's first four bytes of the configuration
   descriptor (only need descBuff[2] and descBuff[3]), which contains the information about the
   total length of data returned by getting the descriptor. This data is used to determine whether
   or not the descriptor can fit in the descriptor buffer. This descriptor also contains information
   about all the interfaces and endpoint descriptors. If the descriptor is of appropriate size, the
   method reads the entire configuration descriptor and fills the entire descriptor buffer with this
-  device's configuration descriptor. If for some reason the descriptor is no longer attainable,
-  an error is returned.</p>
+  device's configuration descriptor. If for some reason the descriptor is no longer attainable, an
+  error is returned.</p>
   <pre>
 ...
 
@@ -626,12 +712,11 @@
   <p>Once the descriptor is in memory, a pointer is assigned to the first position of the buffer
   and is used to index the buffer for reading. There are two endpoint pointers (input and output)
   that are passed into <code>AndroidAccessory::findEndpoints()</code> and their addresses are set
-  to 0, because the code hasn't found any suitable bulk endpoints yet. A loop reads the buffer, parsing
-  each configuration, interface, or endpoint descriptor. For each descriptor,
-  Position 0 always contains the size of the descriptor in bytes and position 1 always contains the
-  descriptor type. Using these two values, the loop skips any configuration and interface
-  descriptors and increments the buffer with the <code>descLen</code> variable to get to the next
-  descriptor.</p>
+  to 0, because the code hasn't found any suitable bulk endpoints yet. A loop reads the buffer,
+  parsing each configuration, interface, or endpoint descriptor. For each descriptor, Position 0
+  always contains the size of the descriptor in bytes and position 1 always contains the descriptor
+  type. Using these two values, the loop skips any configuration and interface descriptors and
+  increments the buffer with the <code>descLen</code> variable to get to the next descriptor.</p>
 
   <p class="note"><strong>Note:</strong> An Android-powered device in accessory mode can
   potentially have two interfaces, one for the default communication to the device and the other
@@ -642,8 +727,8 @@
 
   <p>When it finds the first input and output endpoint descriptors, it sets the endpoint pointers
   to those addresses. If the findEndpoints() function finds both an input and output endpoint, it
-  returns true. It ignores any other endpoints that it finds (the endpoints for the ADB interface, if
-  present).</p>
+  returns true. It ignores any other endpoints that it finds (the endpoints for the ADB interface,
+  if present).</p>
   <pre>
 ...
     p = descBuff;
@@ -701,10 +786,10 @@
 </pre>
 
   <p>Back in the <code>configureAndroid()</code> function, if there were endpoints found, they are
-  appropriately set up for communication. The device's configuration is set to 1 and the state of the device is set to "running", which
-  signifies that the device is properly set up to communicate with your USB accessory. Setting this
-  status prevents the device from being re-detected and re-configured in the
-  <code>AndroidAccessory::isConnected()</code> function.</p>
+  appropriately set up for communication. The device's configuration is set to 1 and the state of
+  the device is set to "running", which signifies that the device is properly set up to communicate
+  with your USB accessory. Setting this status prevents the device from being re-detected and
+  re-configured in the <code>AndroidAccessory::isConnected()</code> function.</p>
   <pre>
 bool AndroidAccessory::configureAndroid(void)
 {
@@ -745,11 +830,11 @@
 
   <p>Lastly, methods to read and write to the appropriate endpoints are needed. The
   <code>demokit.pde</code> sketch calls these methods depending on the data that is read from the
-  Android-powered device or sent by the ADK board. For instance, moving the joystick
-  on the ADK shield writes data that is read by the DemoKit application running on the
-  Android-powered device. Moving sliders on the DemoKit application is read by the
-  <code>demokit.pde</code> sketch and changes the state of the accessory, such as lighting up or
-  changing the color of the LED lights.</p>
+  Android-powered device or sent by the ADK board. For instance, moving the joystick on the ADK
+  shield writes data that is read by the DemoKit application running on the Android-powered device.
+  Moving sliders on the DemoKit application is read by the <code>demokit.pde</code> sketch and
+  changes the state of the accessory, such as lighting up or changing the color of the LED
+  lights.</p>
   <pre>
 int AndroidAccessory::read(void *buff, int len, unsigned int nakLimit) { 
   return usb.newInTransfer(1, in, len, (char *)buff, nakLimit); } 
@@ -760,4 +845,5 @@
   
 </pre>
 
-  <p>See the <code>firmware/demokit/demokit.pde</code> file for information about how the Demo Shield reads and writes data.</p>
+  <p>See the <code>firmware/demokit/demokit.pde</code> file for information about how the ADK board
+  reads and writes data.</p>
\ No newline at end of file
diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h
index d2d3bb8..e705c6f 100644
--- a/include/gui/ISurfaceTexture.h
+++ b/include/gui/ISurfaceTexture.h
@@ -31,20 +31,27 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
+class SurfaceTextureClient;
+
 class ISurfaceTexture : public IInterface
 {
 public:
     DECLARE_META_INTERFACE(SurfaceTexture);
 
-    enum { BUFFER_NEEDS_REALLOCATION = 1 };
+protected:
+    friend class SurfaceTextureClient;
+
+    enum {
+        BUFFER_NEEDS_REALLOCATION = 0x1,
+        RELEASE_ALL_BUFFERS       = 0x2,
+    };
 
     // requestBuffer requests a new buffer for the given index. The server (i.e.
     // the ISurfaceTexture implementation) assigns the newly created buffer to
     // the given slot index, and the client is expected to mirror the
     // slot->buffer mapping so that it's not necessary to transfer a
     // GraphicBuffer for every dequeue operation.
-    virtual sp<GraphicBuffer> requestBuffer(int slot, uint32_t w, uint32_t h,
-            uint32_t format, uint32_t usage) = 0;
+    virtual sp<GraphicBuffer> requestBuffer(int slot) = 0;
 
     // setBufferCount sets the number of buffer slots available. Calling this
     // will also cause all buffer slots to be emptied. The caller should empty
@@ -60,7 +67,8 @@
     // in the contents of its associated buffer contents and call queueBuffer.
     // If dequeueBuffer return BUFFER_NEEDS_REALLOCATION, the client is
     // expected to call requestBuffer immediately.
-    virtual status_t dequeueBuffer(int *slot) = 0;
+    virtual status_t dequeueBuffer(int *slot, uint32_t w, uint32_t h,
+            uint32_t format, uint32_t usage) = 0;
 
     // queueBuffer indicates that the client has finished filling in the
     // contents of the buffer associated with slot and transfers ownership of
@@ -85,6 +93,17 @@
     // Holding this binder reference prevents SurfaceFlinger from freeing the
     // buffers before the client is done with them.
     virtual sp<IBinder> getAllocator() = 0;
+
+    // query retrieves some information for this surface
+    // 'what' tokens allowed are that of android_natives.h
+    virtual int query(int what, int* value) = 0;
+
+    // setSynchronousMode set whether dequeueBuffer is synchronous or
+    // asynchronous. In synchronous mode, dequeueBuffer blocks until
+    // a buffer is available, the currently bound buffer can be dequeued and
+    // queued buffers will be retired in order.
+    // The default mode is asynchronous.
+    virtual status_t setSynchronousMode(bool enabled) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 96828c6..43b2fa9 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -34,14 +34,23 @@
 // ----------------------------------------------------------------------------
 
 class IGraphicBufferAlloc;
+class String8;
 
 class SurfaceTexture : public BnSurfaceTexture {
 public:
     enum { MIN_UNDEQUEUED_BUFFERS = 2 };
-    enum { MIN_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1 };
+    enum {
+        MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1,
+        MIN_SYNC_BUFFER_SLOTS  = MIN_UNDEQUEUED_BUFFERS
+    };
     enum { NUM_BUFFER_SLOTS = 32 };
 
     struct FrameAvailableListener : public virtual RefBase {
+        // onFrameAvailable() is called from queueBuffer() is the FIFO is
+        // empty. You can use SurfaceTexture::getQueuedCount() to
+        // figure out if there are more frames waiting.
+        // This is called without any lock held can be called concurrently by
+        // multiple threads.
         virtual void onFrameAvailable() = 0;
     };
 
@@ -56,15 +65,15 @@
     // SurfaceTexture object (i.e. they are not owned by the client).
     virtual status_t setBufferCount(int bufferCount);
 
-    virtual sp<GraphicBuffer> requestBuffer(int buf, uint32_t w, uint32_t h,
-            uint32_t format, uint32_t usage);
+    virtual sp<GraphicBuffer> requestBuffer(int buf);
 
     // dequeueBuffer gets the next buffer slot index for the client to use. If a
     // buffer slot is available then that slot index is written to the location
     // pointed to by the buf argument and a status of OK is returned.  If no
     // slot is available then a status of -EBUSY is returned and buf is
     // unmodified.
-    virtual status_t dequeueBuffer(int *buf);
+    virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h,
+            uint32_t format, uint32_t usage);
 
     // queueBuffer returns a filled buffer to the SurfaceTexture. In addition, a
     // timestamp must be provided for the buffer. The timestamp is in
@@ -76,6 +85,15 @@
     virtual status_t setCrop(const Rect& reg);
     virtual status_t setTransform(uint32_t transform);
 
+    virtual int query(int what, int* value);
+
+    // setSynchronousMode set whether dequeueBuffer is synchronous or
+    // asynchronous. In synchronous mode, dequeueBuffer blocks until
+    // a buffer is available, the currently bound buffer can be dequeued and
+    // queued buffers will be retired in order.
+    // The default mode is asynchronous.
+    virtual status_t setSynchronousMode(bool enabled);
+
     // updateTexImage sets the image contents of the target texture to that of
     // the most recently queued buffer.
     //
@@ -83,6 +101,16 @@
     // target texture belongs is bound to the calling thread.
     status_t updateTexImage();
 
+    // getqueuedCount returns the number of queued frames waiting in the
+    // FIFO. In asynchronous mode, this always returns 0 or 1 since
+    // frames are not accumulating in the FIFO.
+    size_t getQueuedCount() const;
+
+    // setBufferCountServer set the buffer count. If the client has requested
+    // a buffer count using setBufferCount, the server-buffer count will
+    // take effect once the client sets the count back to zero.
+    status_t setBufferCountServer(int bufferCount);
+
     // getTransformMatrix retrieves the 4x4 texture coordinate transform matrix
     // associated with the texture image set by the most recent call to
     // updateTexImage.
@@ -140,6 +168,10 @@
     // getCurrentTransform returns the transform of the current buffer
     uint32_t getCurrentTransform() const;
 
+    // dump our state in a String
+    void dump(String8& result) const;
+    void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
+
 protected:
 
     // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
@@ -154,9 +186,21 @@
     EGLImageKHR createImage(EGLDisplay dpy,
             const sp<GraphicBuffer>& graphicBuffer);
 
+    status_t setBufferCountServerLocked(int bufferCount);
+
     enum { INVALID_BUFFER_SLOT = -1 };
 
     struct BufferSlot {
+
+        BufferSlot()
+            : mEglImage(EGL_NO_IMAGE_KHR),
+              mEglDisplay(EGL_NO_DISPLAY),
+              mBufferState(BufferSlot::FREE),
+              mRequestBufferCalled(false),
+              mLastQueuedTransform(0),
+              mLastQueuedTimestamp(0) {
+        }
+
         // mGraphicBuffer points to the buffer allocated for this slot or is NULL
         // if no buffer has been allocated.
         sp<GraphicBuffer> mGraphicBuffer;
@@ -167,11 +211,32 @@
         // mEglDisplay is the EGLDisplay used to create mEglImage.
         EGLDisplay mEglDisplay;
 
-        // mOwnedByClient indicates whether the slot is currently accessible to a
+        // mBufferState indicates whether the slot is currently accessible to a
         // client and should not be used by the SurfaceTexture object. It gets
         // set to true when dequeueBuffer returns the slot and is reset to false
         // when the client calls either queueBuffer or cancelBuffer on the slot.
-        bool mOwnedByClient;
+        enum { DEQUEUED=-2, FREE=-1, QUEUED=0 };
+        int8_t mBufferState;
+
+
+        // mRequestBufferCalled is used for validating that the client did
+        // call requestBuffer() when told to do so. Technically this is not
+        // needed but useful for debugging and catching client bugs.
+        bool mRequestBufferCalled;
+
+        // mLastQueuedCrop is the crop rectangle for the buffer that was most
+        // recently queued. This gets set to mNextCrop each time queueBuffer gets
+        // called.
+        Rect mLastQueuedCrop;
+
+        // mLastQueuedTransform is the transform identifier for the buffer that was
+        // most recently queued. This gets set to mNextTransform each time
+        // queueBuffer gets called.
+        uint32_t mLastQueuedTransform;
+
+        // mLastQueuedTimestamp is the timestamp for the buffer that was most
+        // recently queued. This gets set by queueBuffer.
+        int64_t mLastQueuedTimestamp;
     };
 
     // mSlots is the array of buffer slots that must be mirrored on the client
@@ -193,16 +258,19 @@
     // in requestBuffers() if a format of zero is specified.
     uint32_t mPixelFormat;
 
-    // mUseDefaultSize indicates whether or not the default size should be used
-    // that is, if the last requestBuffer has been called with both width
-    // and height null.
-    bool mUseDefaultSize;
-
     // mBufferCount is the number of buffer slots that the client and server
-    // must maintain. It defaults to MIN_BUFFER_SLOTS and can be changed by
-    // calling setBufferCount.
+    // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed
+    // by calling setBufferCount or setBufferCountServer
     int mBufferCount;
 
+    // mRequestedBufferCount is the number of buffer slots requested by the
+    // client. The default is zero, which means the client doesn't care how
+    // many buffers there is.
+    int mClientBufferCount;
+
+    // mServerBufferCount buffer count requested by the server-side
+    int mServerBufferCount;
+
     // mCurrentTexture is the buffer slot index of the buffer that is currently
     // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
     // indicating that no buffer slot is currently bound to the texture. Note,
@@ -233,25 +301,6 @@
     // gets set to mLastQueuedTimestamp each time updateTexImage is called.
     int64_t mCurrentTimestamp;
 
-    // mLastQueued is the buffer slot index of the most recently enqueued buffer.
-    // At construction time it is initialized to INVALID_BUFFER_SLOT, and is
-    // updated each time queueBuffer is called.
-    int mLastQueued;
-
-    // mLastQueuedCrop is the crop rectangle for the buffer that was most
-    // recently queued. This gets set to mNextCrop each time queueBuffer gets
-    // called.
-    Rect mLastQueuedCrop;
-
-    // mLastQueuedTransform is the transform identifier for the buffer that was
-    // most recently queued. This gets set to mNextTransform each time
-    // queueBuffer gets called.
-    uint32_t mLastQueuedTransform;
-
-    // mLastQueuedTimestamp is the timestamp for the buffer that was most
-    // recently queued. This gets set by queueBuffer.
-    int64_t mLastQueuedTimestamp;
-
     // mNextCrop is the crop rectangle that will be used for the next buffer
     // that gets queued. It is set by calling setCrop.
     Rect mNextCrop;
@@ -274,6 +323,16 @@
     // queueBuffer.
     sp<FrameAvailableListener> mFrameAvailableListener;
 
+    // mSynchronousMode whether we're in synchronous mode or not
+    bool mSynchronousMode;
+
+    // mDequeueCondition condition used for dequeueBuffer in synchronous mode
+    mutable Condition mDequeueCondition;
+
+    // mQueue is a FIFO of queued buffers used in synchronous mode
+    typedef Vector<int> Fifo;
+    Fifo mQueue;
+
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables of SurfaceTexture objects. It must be locked whenever the
     // member variables are accessed.
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index c77bc4c..e7c6e24 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -84,7 +84,6 @@
     int getConnectedApi() const;
 
     enum { MIN_UNDEQUEUED_BUFFERS = SurfaceTexture::MIN_UNDEQUEUED_BUFFERS };
-    enum { MIN_BUFFER_SLOTS = SurfaceTexture::MIN_BUFFER_SLOTS };
     enum { NUM_BUFFER_SLOTS = SurfaceTexture::NUM_BUFFER_SLOTS };
     enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
 
diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h
index b1f5045..1bb04a7 100644
--- a/include/utils/BackupHelpers.h
+++ b/include/utils/BackupHelpers.h
@@ -70,6 +70,14 @@
     ~BackupDataWriter();
 
     status_t WriteEntityHeader(const String8& key, size_t dataSize);
+
+    /* Note: WriteEntityData will write arbitrary data into the file without
+     * validation or a previously-supplied header.  The full backup implementation
+     * uses it this way to generate a controlled binary stream that is not
+     * entity-structured.  If the implementation here is changed, either this
+     * use case must remain valid, or the full backup implementation should be
+     * adjusted to use some other appropriate mechanism.
+     */
     status_t WriteEntityData(const void* data, size_t size);
 
     void SetKeyPrefix(const String8& keyPrefix);
@@ -103,7 +111,7 @@
 
     bool HasEntities();
     status_t ReadEntityHeader(String8* key, size_t* dataSize);
-    status_t SkipEntityData(); // must be called with the pointer at the begining of the data.
+    status_t SkipEntityData(); // must be called with the pointer at the beginning of the data.
     ssize_t ReadEntityData(void* data, size_t size);
 
 private:
@@ -126,6 +134,9 @@
 int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD,
         char const* const* files, char const* const *keys, int fileCount);
 
+int write_tarfile(const String8& packageName, const String8& domain,
+        const String8& rootPath, const String8& filePath, BackupDataWriter* outputStream);
+
 class RestoreHelperBase
 {
 public:
diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp
index bc14ad5..16e3780 100644
--- a/libs/gui/ISurfaceTexture.cpp
+++ b/libs/gui/ISurfaceTexture.cpp
@@ -39,6 +39,8 @@
     SET_CROP,
     SET_TRANSFORM,
     GET_ALLOCATOR,
+    QUERY,
+    SET_SYNCHRONOUS_MODE,
 };
 
 
@@ -50,15 +52,10 @@
     {
     }
 
-    virtual sp<GraphicBuffer> requestBuffer(int bufferIdx,
-            uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
+    virtual sp<GraphicBuffer> requestBuffer(int bufferIdx) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
         data.writeInt32(bufferIdx);
-        data.writeInt32(w);
-        data.writeInt32(h);
-        data.writeInt32(format);
-        data.writeInt32(usage);
         remote()->transact(REQUEST_BUFFER, data, &reply);
         sp<GraphicBuffer> buffer;
         bool nonNull = reply.readInt32();
@@ -79,9 +76,14 @@
         return err;
     }
 
-    virtual status_t dequeueBuffer(int *buf) {
+    virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h,
+            uint32_t format, uint32_t usage) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+        data.writeInt32(w);
+        data.writeInt32(h);
+        data.writeInt32(format);
+        data.writeInt32(usage);
         remote()->transact(DEQUEUE_BUFFER, data, &reply);
         *buf = reply.readInt32();
         int result = reply.readInt32();
@@ -132,6 +134,27 @@
         remote()->transact(GET_ALLOCATOR, data, &reply);
         return reply.readStrongBinder();
     }
+
+    virtual int query(int what, int* value) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+        data.writeInt32(what);
+        remote()->transact(QUERY, data, &reply);
+        value[0] = reply.readInt32();
+        status_t result = reply.readInt32();
+        return result;
+    }
+
+    virtual status_t setSynchronousMode(bool enabled) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+        data.writeInt32(enabled);
+        remote()->transact(SET_SYNCHRONOUS_MODE, data, &reply);
+        status_t result = reply.readInt32();
+        return result;
+    }
+
+
 };
 
 IMPLEMENT_META_INTERFACE(SurfaceTexture, "android.gui.SurfaceTexture");
@@ -145,12 +168,7 @@
         case REQUEST_BUFFER: {
             CHECK_INTERFACE(ISurfaceTexture, data, reply);
             int bufferIdx   = data.readInt32();
-            uint32_t w      = data.readInt32();
-            uint32_t h      = data.readInt32();
-            uint32_t format = data.readInt32();
-            uint32_t usage  = data.readInt32();
-            sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, w, h, format,
-                    usage));
+            sp<GraphicBuffer> buffer(requestBuffer(bufferIdx));
             reply->writeInt32(buffer != 0);
             if (buffer != 0) {
                 reply->write(*buffer);
@@ -166,8 +184,12 @@
         } break;
         case DEQUEUE_BUFFER: {
             CHECK_INTERFACE(ISurfaceTexture, data, reply);
+            uint32_t w      = data.readInt32();
+            uint32_t h      = data.readInt32();
+            uint32_t format = data.readInt32();
+            uint32_t usage  = data.readInt32();
             int buf;
-            int result = dequeueBuffer(&buf);
+            int result = dequeueBuffer(&buf, w, h, format, usage);
             reply->writeInt32(buf);
             reply->writeInt32(result);
             return NO_ERROR;
@@ -210,6 +232,22 @@
             reply->writeStrongBinder(result);
             return NO_ERROR;
         } break;
+        case QUERY: {
+            CHECK_INTERFACE(ISurfaceTexture, data, reply);
+            int value;
+            int what = data.readInt32();
+            int res = query(what, &value);
+            reply->writeInt32(value);
+            reply->writeInt32(res);
+            return NO_ERROR;
+        } break;
+        case SET_SYNCHRONOUS_MODE: {
+            CHECK_INTERFACE(ISurfaceTexture, data, reply);
+            bool enabled = data.readInt32();
+            status_t res = setSynchronousMode(enabled);
+            reply->writeInt32(res);
+            return NO_ERROR;
+        } break;
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 2619629..d7c449c 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -34,6 +34,7 @@
 #include <surfaceflinger/IGraphicBufferAlloc.h>
 
 #include <utils/Log.h>
+#include <utils/String8.h>
 
 namespace android {
 
@@ -81,23 +82,17 @@
     mDefaultWidth(1),
     mDefaultHeight(1),
     mPixelFormat(PIXEL_FORMAT_RGBA_8888),
-    mUseDefaultSize(true),
-    mBufferCount(MIN_BUFFER_SLOTS),
+    mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
+    mClientBufferCount(0),
+    mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
     mCurrentTexture(INVALID_BUFFER_SLOT),
     mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES),
     mCurrentTransform(0),
     mCurrentTimestamp(0),
-    mLastQueued(INVALID_BUFFER_SLOT),
-    mLastQueuedTransform(0),
-    mLastQueuedTimestamp(0),
     mNextTransform(0),
-    mTexName(tex) {
+    mTexName(tex),
+    mSynchronousMode(false) {
     LOGV("SurfaceTexture::SurfaceTexture");
-    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
-        mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
-        mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
-        mSlots[i].mOwnedByClient = false;
-    }
     sp<ISurfaceComposer> composer(ComposerService::getComposerService());
     mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
     mNextCrop.makeInvalid();
@@ -108,18 +103,80 @@
     freeAllBuffers();
 }
 
+status_t SurfaceTexture::setBufferCountServerLocked(int bufferCount) {
+    if (bufferCount > NUM_BUFFER_SLOTS)
+        return BAD_VALUE;
+
+    // special-case, nothing to do
+    if (bufferCount == mBufferCount)
+        return OK;
+
+    if (!mClientBufferCount &&
+        bufferCount >= mBufferCount) {
+        // easy, we just have more buffers
+        mBufferCount = bufferCount;
+        mServerBufferCount = bufferCount;
+        mDequeueCondition.signal();
+    } else {
+        // we're here because we're either
+        // - reducing the number of available buffers
+        // - or there is a client-buffer-count in effect
+
+        // less than 2 buffers is never allowed
+        if (bufferCount < 2)
+            return BAD_VALUE;
+
+        // when there is non client-buffer-count in effect, the client is not
+        // allowed to dequeue more than one buffer at a time,
+        // so the next time they dequeue a buffer, we know that they don't
+        // own one. the actual resizing will happen during the next
+        // dequeueBuffer.
+
+        mServerBufferCount = bufferCount;
+    }
+    return OK;
+}
+
+status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
+    Mutex::Autolock lock(mMutex);
+    return setBufferCountServerLocked(bufferCount);
+}
+
 status_t SurfaceTexture::setBufferCount(int bufferCount) {
     LOGV("SurfaceTexture::setBufferCount");
+    Mutex::Autolock lock(mMutex);
 
-    if (bufferCount < MIN_BUFFER_SLOTS) {
+    // Error out if the user has dequeued buffers
+    for (int i=0 ; i<mBufferCount ; i++) {
+        if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
+            LOGE("setBufferCount: client owns some buffers");
+            return -EINVAL;
+        }
+    }
+
+    if (bufferCount == 0) {
+        const int minBufferSlots = mSynchronousMode ?
+                MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+        mClientBufferCount = 0;
+        bufferCount = (mServerBufferCount >= minBufferSlots) ?
+                mServerBufferCount : minBufferSlots;
+        return setBufferCountServerLocked(bufferCount);
+    }
+
+    // We don't allow the client to set a buffer-count less than
+    // MIN_ASYNC_BUFFER_SLOTS (3), there is no reason for it.
+    if (bufferCount < MIN_ASYNC_BUFFER_SLOTS) {
         return BAD_VALUE;
     }
 
-    Mutex::Autolock lock(mMutex);
+    // here we're guaranteed that the client doesn't have dequeued buffers
+    // and will release all of its buffer references.
     freeAllBuffers();
     mBufferCount = bufferCount;
+    mClientBufferCount = bufferCount;
     mCurrentTexture = INVALID_BUFFER_SLOT;
-    mLastQueued = INVALID_BUFFER_SLOT;
+    mQueue.clear();
+    mDequeueCondition.signal();
     return OK;
 }
 
@@ -133,8 +190,7 @@
     return OK;
 }
 
-sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf,
-        uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
+sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf) {
     LOGV("SurfaceTexture::requestBuffer");
     Mutex::Autolock lock(mMutex);
     if (buf < 0 || mBufferCount <= buf) {
@@ -142,11 +198,123 @@
                 mBufferCount, buf);
         return 0;
     }
+    mSlots[buf].mRequestBufferCalled = true;
+    return mSlots[buf].mGraphicBuffer;
+}
+
+status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
+        uint32_t format, uint32_t usage) {
+    LOGV("SurfaceTexture::dequeueBuffer");
+
     if ((w && !h) || (!w & h)) {
-        LOGE("requestBuffer: invalid size: w=%u, h=%u: %d", w, h, buf);
-        return 0;
+        LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
+        return BAD_VALUE;
     }
 
+    Mutex::Autolock lock(mMutex);
+
+    status_t returnFlags(OK);
+
+    int found, foundSync;
+    int dequeuedCount = 0;
+    bool tryAgain = true;
+    while (tryAgain) {
+        // We need to wait for the FIFO to drain if the number of buffer
+        // needs to change.
+        //
+        // The condition "number of buffer needs to change" is true if
+        // - the client doesn't care about how many buffers there are
+        // - AND the actual number of buffer is different from what was
+        //   set in the last setBufferCountServer()
+        //                         - OR -
+        //   setBufferCountServer() was set to a value incompatible with
+        //   the synchronization mode (for instance because the sync mode
+        //   changed since)
+        //
+        // As long as this condition is true AND the FIFO is not empty, we
+        // wait on mDequeueCondition.
+
+        int minBufferCountNeeded = mSynchronousMode ?
+                MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+
+        if (!mClientBufferCount &&
+                ((mServerBufferCount != mBufferCount) ||
+                        (mServerBufferCount < minBufferCountNeeded))) {
+            // wait for the FIFO to drain
+            while (!mQueue.isEmpty()) {
+                mDequeueCondition.wait(mMutex);
+            }
+            minBufferCountNeeded = mSynchronousMode ?
+                    MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+        }
+
+
+        if (!mClientBufferCount &&
+                ((mServerBufferCount != mBufferCount) ||
+                        (mServerBufferCount < minBufferCountNeeded))) {
+            // here we're guaranteed that mQueue is empty
+            freeAllBuffers();
+            mBufferCount = mServerBufferCount;
+            if (mBufferCount < minBufferCountNeeded)
+                mBufferCount = minBufferCountNeeded;
+            mCurrentTexture = INVALID_BUFFER_SLOT;
+            returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
+        }
+
+        // look for a free buffer to give to the client
+        found = INVALID_BUFFER_SLOT;
+        foundSync = INVALID_BUFFER_SLOT;
+        dequeuedCount = 0;
+        for (int i = 0; i < mBufferCount; i++) {
+            const int state = mSlots[i].mBufferState;
+            if (state == BufferSlot::DEQUEUED) {
+                dequeuedCount++;
+            }
+            if (state == BufferSlot::FREE || i == mCurrentTexture) {
+                foundSync = i;
+                if (i != mCurrentTexture) {
+                    found = i;
+                    break;
+                }
+            }
+        }
+
+        // clients are not allowed to dequeue more than one buffer
+        // if they didn't set a buffer count.
+        if (!mClientBufferCount && dequeuedCount) {
+            return -EINVAL;
+        }
+
+        // make sure the client is not trying to dequeue more buffers
+        // than allowed.
+        const int avail = mBufferCount - (dequeuedCount+1);
+        if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) {
+            LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded (dequeued=%d)",
+                    MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode),
+                    dequeuedCount);
+            return -EBUSY;
+        }
+
+        // we're in synchronous mode and didn't find a buffer, we need to wait
+        // for for some buffers to be consumed
+        tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
+        if (tryAgain) {
+            mDequeueCondition.wait(mMutex);
+        }
+    }
+
+    if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
+        // foundSync guaranteed to be != INVALID_BUFFER_SLOT
+        found = foundSync;
+    }
+
+    if (found == INVALID_BUFFER_SLOT) {
+        return -EBUSY;
+    }
+
+    const int buf = found;
+    *outBuf = found;
+
     const bool useDefaultSize = !w && !h;
     if (useDefaultSize) {
         // use the default size
@@ -160,78 +328,115 @@
         format = mPixelFormat;
     }
 
-    usage |= GraphicBuffer::USAGE_HW_TEXTURE;
-    sp<GraphicBuffer> graphicBuffer(
-            mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage));
-    if (graphicBuffer == 0) {
-        LOGE("requestBuffer: SurfaceComposer::createGraphicBuffer failed");
-    } else {
-        mUseDefaultSize = useDefaultSize;
+    // buffer is now in DEQUEUED (but can also be current at the same time,
+    // if we're in synchronous mode)
+    mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
+
+    const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer);
+    if ((buffer == NULL) ||
+        (uint32_t(buffer->width)  != w) ||
+        (uint32_t(buffer->height) != h) ||
+        (uint32_t(buffer->format) != format) ||
+        ((uint32_t(buffer->usage) & usage) != usage))
+    {
+        usage |= GraphicBuffer::USAGE_HW_TEXTURE;
+        sp<GraphicBuffer> graphicBuffer(
+                mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage));
+        if (graphicBuffer == 0) {
+            LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer failed");
+            return NO_MEMORY;
+        }
         if (updateFormat) {
             mPixelFormat = format;
         }
         mSlots[buf].mGraphicBuffer = graphicBuffer;
+        mSlots[buf].mRequestBufferCalled = false;
         if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
             eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage);
             mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
             mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
         }
+        returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
     }
-    return graphicBuffer;
+    return returnFlags;
 }
 
-status_t SurfaceTexture::dequeueBuffer(int *buf) {
-    LOGV("SurfaceTexture::dequeueBuffer");
+status_t SurfaceTexture::setSynchronousMode(bool enabled) {
     Mutex::Autolock lock(mMutex);
-    int found = INVALID_BUFFER_SLOT;
-    for (int i = 0; i < mBufferCount; i++) {
-        if (!mSlots[i].mOwnedByClient && i != mCurrentTexture && i != mLastQueued) {
-            mSlots[i].mOwnedByClient = true;
-            found = i;
-            break;
+
+    status_t err = OK;
+    if (!enabled) {
+        // going to asynchronous mode, drain the queue
+        while (mSynchronousMode != enabled && !mQueue.isEmpty()) {
+            mDequeueCondition.wait(mMutex);
         }
     }
-    if (found == INVALID_BUFFER_SLOT) {
-        return -EBUSY;
-    }
 
-    *buf = found;
-
-    const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
-    if (buffer == NULL) {
-        return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+    if (mSynchronousMode != enabled) {
+        // - if we're going to asynchronous mode, the queue is guaranteed to be
+        // empty here
+        // - if the client set the number of buffers, we're guaranteed that
+        // we have at least 3 (because we don't allow less)
+        mSynchronousMode = enabled;
+        mDequeueCondition.signal();
     }
-
-    if ((mUseDefaultSize) &&
-        ((uint32_t(buffer->width) != mDefaultWidth) ||
-         (uint32_t(buffer->height) != mDefaultHeight))) {
-        return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
-    }
-    return OK;
+    return err;
 }
 
 status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) {
     LOGV("SurfaceTexture::queueBuffer");
+
+    sp<FrameAvailableListener> listener;
+
+    { // scope for the lock
     Mutex::Autolock lock(mMutex);
-    if (buf < 0 || mBufferCount <= buf) {
+    if (buf < 0 || buf >= mBufferCount) {
         LOGE("queueBuffer: slot index out of range [0, %d]: %d",
                 mBufferCount, buf);
         return -EINVAL;
-    } else if (!mSlots[buf].mOwnedByClient) {
-        LOGE("queueBuffer: slot %d is not owned by the client", buf);
+    } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+        LOGE("queueBuffer: slot %d is not owned by the client (state=%d)",
+                buf, mSlots[buf].mBufferState);
         return -EINVAL;
-    } else if (mSlots[buf].mGraphicBuffer == 0) {
+    } else if (buf == mCurrentTexture) {
+        LOGE("queueBuffer: slot %d is current!", buf);
+        return -EINVAL;
+    } else if (!mSlots[buf].mRequestBufferCalled) {
         LOGE("queueBuffer: slot %d was enqueued without requesting a buffer",
                 buf);
         return -EINVAL;
     }
-    mSlots[buf].mOwnedByClient = false;
-    mLastQueued = buf;
-    mLastQueuedCrop = mNextCrop;
-    mLastQueuedTransform = mNextTransform;
-    mLastQueuedTimestamp = timestamp;
-    if (mFrameAvailableListener != 0) {
-        mFrameAvailableListener->onFrameAvailable();
+
+    if (mQueue.empty()) {
+        listener = mFrameAvailableListener;
+    }
+
+    if (mSynchronousMode) {
+        // in synchronous mode we queue all buffers in a FIFO
+        mQueue.push_back(buf);
+    } else {
+        // in asynchronous mode we only keep the most recent buffer
+        if (mQueue.empty()) {
+            mQueue.push_back(buf);
+        } else {
+            Fifo::iterator front(mQueue.begin());
+            // buffer currently queued is freed
+            mSlots[*front].mBufferState = BufferSlot::FREE;
+            // and we record the new buffer index in the queued list
+            *front = buf;
+        }
+    }
+
+    mSlots[buf].mBufferState = BufferSlot::QUEUED;
+    mSlots[buf].mLastQueuedCrop = mNextCrop;
+    mSlots[buf].mLastQueuedTransform = mNextTransform;
+    mSlots[buf].mLastQueuedTimestamp = timestamp;
+    mDequeueCondition.signal();
+    } // scope for the lock
+
+    // call back without lock held
+    if (listener != 0) {
+        listener->onFrameAvailable();
     }
     return OK;
 }
@@ -239,15 +444,17 @@
 void SurfaceTexture::cancelBuffer(int buf) {
     LOGV("SurfaceTexture::cancelBuffer");
     Mutex::Autolock lock(mMutex);
-    if (buf < 0 || mBufferCount <= buf) {
-        LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount,
-                buf);
+    if (buf < 0 || buf >= mBufferCount) {
+        LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
+                mBufferCount, buf);
         return;
-    } else if (!mSlots[buf].mOwnedByClient) {
-        LOGE("cancelBuffer: slot %d is not owned by the client", buf);
+    } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+        LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
+                buf, mSlots[buf].mBufferState);
         return;
     }
-    mSlots[buf].mOwnedByClient = false;
+    mSlots[buf].mBufferState = BufferSlot::FREE;
+    mDequeueCondition.signal();
 }
 
 status_t SurfaceTexture::setCrop(const Rect& crop) {
@@ -266,18 +473,31 @@
 
 status_t SurfaceTexture::updateTexImage() {
     LOGV("SurfaceTexture::updateTexImage");
+
     Mutex::Autolock lock(mMutex);
 
-    // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT,
+    int buf = mCurrentTexture;
+    if (!mQueue.empty()) {
+        // in asynchronous mode the list is guaranteed to be one buffer deep,
+        // while in synchronous mode we use the oldest buffer
+        Fifo::iterator front(mQueue.begin());
+        buf = *front;
+        mQueue.erase(front);
+        if (mQueue.isEmpty()) {
+            mDequeueCondition.signal();
+        }
+    }
+
+    // Initially both mCurrentTexture and buf are INVALID_BUFFER_SLOT,
     // so this check will fail until a buffer gets queued.
-    if (mCurrentTexture != mLastQueued) {
+    if (mCurrentTexture != buf) {
         // Update the GL texture object.
-        EGLImageKHR image = mSlots[mLastQueued].mEglImage;
+        EGLImageKHR image = mSlots[buf].mEglImage;
         if (image == EGL_NO_IMAGE_KHR) {
             EGLDisplay dpy = eglGetCurrentDisplay();
-            image = createImage(dpy, mSlots[mLastQueued].mGraphicBuffer);
-            mSlots[mLastQueued].mEglImage = image;
-            mSlots[mLastQueued].mEglDisplay = dpy;
+            image = createImage(dpy, mSlots[buf].mGraphicBuffer);
+            mSlots[buf].mEglImage = image;
+            mSlots[buf].mEglDisplay = dpy;
             if (image == EGL_NO_IMAGE_KHR) {
                 // NOTE: if dpy was invalid, createImage() is guaranteed to
                 // fail. so we'd end up here.
@@ -287,11 +507,10 @@
 
         GLint error;
         while ((error = glGetError()) != GL_NO_ERROR) {
-            LOGE("GL error cleared before updating SurfaceTexture: %#04x", error);
+            LOGW("updateTexImage: clearing GL error: %#04x", error);
         }
 
-        GLenum target = getTextureTarget(
-                mSlots[mLastQueued].mGraphicBuffer->format);
+        GLenum target = getTextureTarget(mSlots[buf].mGraphicBuffer->format);
         if (target != mCurrentTextureTarget) {
             glDeleteTextures(1, &mTexName);
         }
@@ -301,20 +520,29 @@
         bool failed = false;
         while ((error = glGetError()) != GL_NO_ERROR) {
             LOGE("error binding external texture image %p (slot %d): %#04x",
-                    image, mLastQueued, error);
+                    image, buf, error);
             failed = true;
         }
         if (failed) {
             return -EINVAL;
         }
 
+        if (mCurrentTexture != INVALID_BUFFER_SLOT) {
+            // the current buffer becomes FREE if it was still in the queued
+            // state. If it has already been given to the client
+            // (synchronous mode), then it stays in DEQUEUED state.
+            if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED)
+                mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE;
+        }
+
         // Update the SurfaceTexture state.
-        mCurrentTexture = mLastQueued;
+        mCurrentTexture = buf;
         mCurrentTextureTarget = target;
-        mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer;
-        mCurrentCrop = mLastQueuedCrop;
-        mCurrentTransform = mLastQueuedTransform;
-        mCurrentTimestamp = mLastQueuedTimestamp;
+        mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
+        mCurrentCrop = mSlots[buf].mLastQueuedCrop;
+        mCurrentTransform = mSlots[buf].mLastQueuedTransform;
+        mCurrentTimestamp = mSlots[buf].mLastQueuedTimestamp;
+        mDequeueCondition.signal();
     } else {
         // We always bind the texture even if we don't update its contents.
         glBindTexture(mCurrentTextureTarget, mTexName);
@@ -322,6 +550,11 @@
     return OK;
 }
 
+size_t SurfaceTexture::getQueuedCount() const {
+    Mutex::Autolock lock(mMutex);
+    return mQueue.size();
+}
+
 bool SurfaceTexture::isExternalFormat(uint32_t format)
 {
     switch (format) {
@@ -470,7 +703,7 @@
 void SurfaceTexture::freeAllBuffers() {
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
         mSlots[i].mGraphicBuffer = 0;
-        mSlots[i].mOwnedByClient = false;
+        mSlots[i].mBufferState = BufferSlot::FREE;
         if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
             eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage);
             mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
@@ -510,6 +743,98 @@
     return mCurrentTransform;
 }
 
+int SurfaceTexture::query(int what, int* outValue)
+{
+    Mutex::Autolock lock(mMutex);
+    int value;
+    switch (what) {
+    case NATIVE_WINDOW_WIDTH:
+        value = mDefaultWidth;
+        if (!mDefaultWidth && !mDefaultHeight && mCurrentTextureBuf!=0)
+            value = mCurrentTextureBuf->width;
+        break;
+    case NATIVE_WINDOW_HEIGHT:
+        value = mDefaultHeight;
+        if (!mDefaultWidth && !mDefaultHeight && mCurrentTextureBuf!=0)
+            value = mCurrentTextureBuf->height;
+        break;
+    case NATIVE_WINDOW_FORMAT:
+        value = mPixelFormat;
+        break;
+    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+        value = mSynchronousMode ?
+                (MIN_UNDEQUEUED_BUFFERS-1) : MIN_UNDEQUEUED_BUFFERS;
+        break;
+    default:
+        return BAD_VALUE;
+    }
+    outValue[0] = value;
+    return NO_ERROR;
+}
+
+void SurfaceTexture::dump(String8& result) const
+{
+    char buffer[1024];
+    dump(result, "", buffer, 1024);
+}
+
+void SurfaceTexture::dump(String8& result, const char* prefix,
+        char* buffer, size_t SIZE) const
+{
+    Mutex::Autolock _l(mMutex);
+    snprintf(buffer, SIZE,
+            "%smBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], "
+            "mPixelFormat=%d, mTexName=%d\n",
+            prefix, mBufferCount, mSynchronousMode, mDefaultWidth, mDefaultHeight,
+            mPixelFormat, mTexName);
+    result.append(buffer);
+
+    String8 fifo;
+    int fifoSize = 0;
+    Fifo::const_iterator i(mQueue.begin());
+    while (i != mQueue.end()) {
+        snprintf(buffer, SIZE, "%02d ", *i++);
+        fifoSize++;
+        fifo.append(buffer);
+    }
+
+    snprintf(buffer, SIZE,
+            "%scurrent: {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d, target=0x%04x}\n"
+            "%snext   : {crop=[%d,%d,%d,%d], transform=0x%02x, FIFO(%d)={%s}}\n"
+            ,
+            prefix, mCurrentCrop.left,
+            mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
+            mCurrentTransform, mCurrentTexture, mCurrentTextureTarget,
+            prefix, mNextCrop.left, mNextCrop.top, mNextCrop.right, mNextCrop.bottom,
+            mCurrentTransform, fifoSize, fifo.string()
+    );
+    result.append(buffer);
+
+    struct {
+        const char * operator()(int state) const {
+            switch (state) {
+                case BufferSlot::DEQUEUED: return "DEQUEUED";
+                case BufferSlot::QUEUED: return "QUEUED";
+                case BufferSlot::FREE: return "FREE";
+                default: return "Unknown";
+            }
+        }
+    } stateName;
+
+    for (int i=0 ; i<mBufferCount ; i++) {
+        const BufferSlot& slot(mSlots[i]);
+        snprintf(buffer, SIZE,
+                "%s%s[%02d] state=%-8s, crop=[%d,%d,%d,%d], transform=0x%02x, "
+                "timestamp=%lld\n"
+                ,
+                prefix, (i==mCurrentTexture)?">":" ", i, stateName(slot.mBufferState),
+                slot.mLastQueuedCrop.left, slot.mLastQueuedCrop.top,
+                slot.mLastQueuedCrop.right, slot.mLastQueuedCrop.bottom,
+                slot.mLastQueuedTransform, slot.mLastQueuedTimestamp
+        );
+        result.append(buffer);
+    }
+}
 
 static void mtxMul(float out[16], const float a[16], const float b[16]) {
     out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3];
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index ec6da43..6f10320 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -39,6 +39,9 @@
     ANativeWindow::query            = query;
     ANativeWindow::perform          = perform;
 
+    const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
+    const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
+
     // Get a reference to the allocator.
     mAllocator = mSurfaceTexture->getAllocator();
 }
@@ -90,27 +93,40 @@
 }
 
 int SurfaceTextureClient::setSwapInterval(int interval) {
-    return INVALID_OPERATION;
+    // EGL specification states:
+    //  interval is silently clamped to minimum and maximum implementation
+    //  dependent values before being stored.
+    // Although we don't have to, we apply the same logic here.
+
+    if (interval < minSwapInterval)
+        interval = minSwapInterval;
+
+    if (interval > maxSwapInterval)
+        interval = maxSwapInterval;
+
+    status_t res = mSurfaceTexture->setSynchronousMode(interval ? true : false);
+
+    return res;
 }
 
 int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) {
     LOGV("SurfaceTextureClient::dequeueBuffer");
     Mutex::Autolock lock(mMutex);
     int buf = -1;
-    status_t err = mSurfaceTexture->dequeueBuffer(&buf);
-    if (err < 0) {
-        LOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer failed: %d", err);
-        return err;
+    status_t result = mSurfaceTexture->dequeueBuffer(&buf, mReqWidth, mReqHeight,
+            mReqFormat, mReqUsage);
+    if (result < 0) {
+        LOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)"
+             "failed: %d", result, mReqWidth, mReqHeight, mReqFormat, mReqUsage);
+        return result;
     }
     sp<GraphicBuffer>& gbuf(mSlots[buf]);
-    if (err == ISurfaceTexture::BUFFER_NEEDS_REALLOCATION ||
-        gbuf == 0 ||
-        (mReqWidth && gbuf->getWidth() != mReqWidth) ||
-        (mReqHeight && gbuf->getHeight() != mReqHeight) ||
-        (mReqFormat && uint32_t(gbuf->getPixelFormat()) != mReqFormat) ||
-        (gbuf->getUsage() & mReqUsage) != mReqUsage) {
-        gbuf = mSurfaceTexture->requestBuffer(buf, mReqWidth, mReqHeight,
-                mReqFormat, mReqUsage);
+    if (result & ISurfaceTexture::RELEASE_ALL_BUFFERS) {
+        freeAllBuffers();
+    }
+
+    if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
+        gbuf = mSurfaceTexture->requestBuffer(buf);
         if (gbuf == 0) {
             LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed");
             return NO_MEMORY;
@@ -163,29 +179,17 @@
 
 int SurfaceTextureClient::query(int what, int* value) const {
     LOGV("SurfaceTextureClient::query");
-    Mutex::Autolock lock(mMutex);
     switch (what) {
-    case NATIVE_WINDOW_WIDTH:
-        *value = mQueryWidth ? mQueryWidth : mReqWidth;
-        return NO_ERROR;
-    case NATIVE_WINDOW_HEIGHT:
-        *value = mQueryHeight ? mQueryHeight : mReqHeight;
-        return NO_ERROR;
-    case NATIVE_WINDOW_FORMAT:
-        *value = mQueryFormat ? mQueryFormat : mReqFormat;
-        return NO_ERROR;
-    case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
-        *value = MIN_UNDEQUEUED_BUFFERS;
-        return NO_ERROR;
     case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
-        // SurfaceTextureClient currently never queues frames to SurfaceFlinger.
+        // TODO: this is not needed anymore
         *value = 0;
         return NO_ERROR;
     case NATIVE_WINDOW_CONCRETE_TYPE:
+        // TODO: this is not needed anymore
         *value = NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT;
         return NO_ERROR;
     }
-    return BAD_VALUE;
+    return mSurfaceTexture->query(what, value);
 }
 
 int SurfaceTextureClient::perform(int operation, va_list args)
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 753e933..59a4cc5 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -17,23 +17,78 @@
 #include <EGL/egl.h>
 #include <gtest/gtest.h>
 #include <gui/SurfaceTextureClient.h>
+#include <utils/threads.h>
 
 namespace android {
 
 class SurfaceTextureClientTest : public ::testing::Test {
 protected:
+    SurfaceTextureClientTest():
+            mEglDisplay(EGL_NO_DISPLAY),
+            mEglSurface(EGL_NO_SURFACE),
+            mEglContext(EGL_NO_CONTEXT) {
+    }
+
     virtual void SetUp() {
         mST = new SurfaceTexture(123);
         mSTC = new SurfaceTextureClient(mST);
+
+        // We need a valid GL context so we can test updateTexImage()
+        // This initializes EGL and create a dummy GL context with a
+        // pbuffer render target.
+        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
+
+        EGLint majorVersion, minorVersion;
+        EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        EGLConfig myConfig;
+        EGLint numConfigs = 0;
+        EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(),
+                &myConfig, 1, &numConfigs));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        EGLint pbufferAttribs[] = {
+            EGL_WIDTH, 16,
+            EGL_HEIGHT, 16,
+            EGL_NONE };
+        mEglSurface = eglCreatePbufferSurface(mEglDisplay, myConfig, pbufferAttribs);
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
+
+        mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, 0);
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
+
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
     }
 
     virtual void TearDown() {
         mST.clear();
         mSTC.clear();
+        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+        eglDestroyContext(mEglDisplay, mEglContext);
+        eglDestroySurface(mEglDisplay, mEglSurface);
+        eglTerminate(mEglDisplay);
+    }
+
+    virtual EGLint const* getConfigAttribs() {
+        static EGLint sDefaultConfigAttribs[] = {
+            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+            EGL_NONE
+        };
+
+        return sDefaultConfigAttribs;
     }
 
     sp<SurfaceTexture> mST;
     sp<SurfaceTextureClient> mSTC;
+    EGLDisplay mEglDisplay;
+    EGLSurface mEglSurface;
+    EGLContext mEglContext;
 };
 
 TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) {
@@ -94,8 +149,8 @@
 
     EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, anw.get(),
             NULL);
-    ASSERT_NE(EGL_NO_SURFACE, eglSurface);
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    EXPECT_NE(EGL_NO_SURFACE, eglSurface);
+    EXPECT_EQ(EGL_SUCCESS, eglGetError());
 
     eglTerminate(dpy);
 }
@@ -204,6 +259,7 @@
     sp<ANativeWindow> anw(mSTC);
     sp<SurfaceTexture> st(mST);
     ANativeWindowBuffer* buf[2];
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
     ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
     ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
     EXPECT_NE(buf[0], buf[1]);
@@ -225,6 +281,7 @@
     sp<ANativeWindow> anw(mSTC);
     sp<SurfaceTexture> st(mST);
     ANativeWindowBuffer* buf[2];
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
     EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
     ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
     ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
@@ -247,4 +304,203 @@
     ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
 }
 
+TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, st->setSynchronousMode(false));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(OK, st->updateTexImage());
+
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(OK, st->updateTexImage());
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeSlowRetire) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+    EXPECT_NE(buf[0], buf[1]);
+    EXPECT_NE(buf[1], buf[2]);
+    EXPECT_NE(buf[2], buf[0]);
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]);
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeFastRetire) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+    EXPECT_NE(buf[0], buf[1]);
+    EXPECT_NE(buf[1], buf[2]);
+    EXPECT_NE(buf[2], buf[0]);
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]);
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDQQR) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]);
+
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    EXPECT_NE(buf[0], buf[1]);
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+    EXPECT_NE(buf[1], buf[2]);
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDequeueCurrent) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    android_native_buffer_t* firstBuf;
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &firstBuf));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), firstBuf));
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), firstBuf);
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+    EXPECT_NE(buf[0], buf[1]);
+    EXPECT_NE(buf[1], buf[2]);
+    EXPECT_NE(buf[2], buf[0]);
+    EXPECT_EQ(firstBuf, buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeMinUndequeued) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+    EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    EXPECT_EQ(-EBUSY, anw->dequeueBuffer(anw.get(), &buf[2]));
+
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+
+    EXPECT_EQ(OK, st->updateTexImage());
+    EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+
+    EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[2]));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeWaitRetire) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+
+    class MyThread : public Thread {
+        sp<SurfaceTexture> st;
+        EGLContext ctx;
+        EGLSurface sur;
+        EGLDisplay dpy;
+        bool mBufferRetired;
+        Mutex mLock;
+        virtual bool threadLoop() {
+            eglMakeCurrent(dpy, sur, sur, ctx);
+            usleep(20000);
+            Mutex::Autolock _l(mLock);
+            st->updateTexImage();
+            mBufferRetired = true;
+            eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+            return false;
+        }
+    public:
+        MyThread(const sp<SurfaceTexture>& st)
+            : st(st), mBufferRetired(false) {
+            ctx = eglGetCurrentContext();
+            sur = eglGetCurrentSurface(EGL_DRAW);
+            dpy = eglGetCurrentDisplay();
+            eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+        }
+        ~MyThread() {
+            eglMakeCurrent(dpy, sur, sur, ctx);
+        }
+        void bufferDequeued() {
+            Mutex::Autolock _l(mLock);
+            EXPECT_EQ(true, mBufferRetired);
+        }
+    };
+
+    android_native_buffer_t* buf[3];
+    ASSERT_EQ(OK, st->setSynchronousMode(true));
+    ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+    // dequeue/queue/update so we have a current buffer
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    st->updateTexImage();
+
+    MyThread* thread = new MyThread(st);
+    sp<Thread> threadBase(thread);
+
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+    thread->run();
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+    ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+    thread->bufferDequeued();
+    thread->requestExitAndWait();
+}
+
 }
diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp
index e41dd39..22034c5 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/utils/AssetManager.cpp
@@ -681,6 +681,9 @@
                 delete ass;
             }
         }
+        if (idmap != NULL) {
+            delete idmap;
+        }
     }
 
     if (required && !rt) LOGW("Unable to find resources file resources.arsc");
diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp
index adb3174..f963058 100644
--- a/libs/utils/BackupData.cpp
+++ b/libs/utils/BackupData.cpp
@@ -20,12 +20,15 @@
 #include <utils/ByteOrder.h>
 
 #include <stdio.h>
+#include <string.h>
 #include <unistd.h>
 
 #include <cutils/log.h>
 
 namespace android {
 
+static const bool DEBUG = false;
+
 /*
  * File Format (v1):
  *
@@ -75,6 +78,7 @@
     paddingSize = padding_extra(n);
     if (paddingSize > 0) {
         uint32_t padding = 0xbcbcbcbc;
+        if (DEBUG) LOGI("writing %d padding bytes for %d", paddingSize, n);
         amt = write(m_fd, &padding, paddingSize);
         if (amt != paddingSize) {
             m_status = errno;
@@ -107,8 +111,8 @@
     } else {
         k = key;
     }
-    if (false) {
-        LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(),
+    if (DEBUG) {
+        LOGD("Writing header: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(),
                 dataSize);
     }
 
@@ -121,6 +125,7 @@
     header.keyLen = tolel(keyLen);
     header.dataSize = tolel(dataSize);
 
+    if (DEBUG) LOGI("writing entity header, %d bytes", sizeof(entity_header_v1));
     amt = write(m_fd, &header, sizeof(entity_header_v1));
     if (amt != sizeof(entity_header_v1)) {
         m_status = errno;
@@ -128,6 +133,7 @@
     }
     m_pos += amt;
 
+    if (DEBUG) LOGI("writing entity header key, %d bytes", keyLen+1);
     amt = write(m_fd, k.string(), keyLen+1);
     if (amt != keyLen+1) {
         m_status = errno;
@@ -145,7 +151,12 @@
 status_t
 BackupDataWriter::WriteEntityData(const void* data, size_t size)
 {
+    if (DEBUG) LOGD("Writing data: size=%lu", (unsigned long) size);
+
     if (m_status != NO_ERROR) {
+        if (DEBUG) {
+            LOGD("Not writing data - stream in error state %d (%s)", m_status, strerror(m_status));
+        }
         return m_status;
     }
 
@@ -155,6 +166,7 @@
     ssize_t amt = write(m_fd, data, size);
     if (amt != (ssize_t)size) {
         m_status = errno;
+        if (DEBUG) LOGD("write returned error %d (%s)", m_status, strerror(m_status));
         return m_status;
     }
     m_pos += amt;
diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp
index 4ad9b51..ad4a308 100644
--- a/libs/utils/BackupHelpers.cpp
+++ b/libs/utils/BackupHelpers.cpp
@@ -442,6 +442,184 @@
     return 0;
 }
 
+// Utility function, equivalent to stpcpy(): perform a strcpy, but instead of
+// returning the initial dest, return a pointer to the trailing NUL.
+static char* strcpy_ptr(char* dest, const char* str) {
+    if (dest && str) {
+        while ((*dest = *str) != 0) {
+            dest++;
+            str++;
+        }
+    }
+    return dest;
+}
+
+int write_tarfile(const String8& packageName, const String8& domain,
+        const String8& rootpath, const String8& filepath, BackupDataWriter* writer)
+{
+    // In the output stream everything is stored relative to the root
+    const char* relstart = filepath.string() + rootpath.length();
+    if (*relstart == '/') relstart++;     // won't be true when path == rootpath
+    String8 relpath(relstart);
+
+    // Too long a name for the ustar format?
+    //    "apps/" + packagename + '/' + domainpath < 155 chars
+    //    relpath < 100 chars
+    if ((5 + packageName.length() + 1 + domain.length() >= 155) || (relpath.length() >= 100)) {
+        LOGE("Filename [%s] too long, skipping", relpath.string());
+        return -1;
+    }
+
+    int err = 0;
+    struct stat64 s;
+    if (lstat64(filepath.string(), &s) != 0) {
+        err = errno;
+        LOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string());
+        return err;
+    }
+
+    const int isdir = S_ISDIR(s.st_mode);
+
+    // !!! TODO: use mmap when possible to avoid churning the buffer cache
+    // !!! TODO: this will break with symlinks; need to use readlink(2)
+    int fd = open(filepath.string(), O_RDONLY);
+    if (fd < 0) {
+        err = errno;
+        LOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string());
+        return err;
+    }
+
+    // read/write up to this much at a time.
+    const size_t BUFSIZE = 32 * 1024;
+
+    char* buf = new char[BUFSIZE];
+    if (buf == NULL) {
+        LOGE("Out of mem allocating transfer buffer");
+        err = ENOMEM;
+        goto done;
+    }
+
+    // Good to go -- first construct the standard tar header at the start of the buffer
+    memset(buf, 0, 512);    // tar header is 512 bytes
+
+    // Magic fields for the ustar file format
+    strcat(buf + 257, "ustar");
+    strcat(buf + 263, "00");
+
+    {
+        // Prefix and main relative path.  Path lengths have been preflighted.
+
+        // [ 345 : 155 ] filename path prefix [ustar]
+        //
+        // packagename and domain can each be empty.
+        char* cp = buf + 345;
+        if (packageName.length() > 0) {
+            // it's an app; so prefix with "apps/packagename/"
+            cp = strcpy_ptr(cp, "apps/");
+            cp = strcpy_ptr(cp, packageName.string());
+        }
+
+        if (domain.length() > 0) {
+            // only need a / if there was a package name
+            if (packageName.length() > 0) *cp++ = '/';
+            cp = strcpy_ptr(cp, domain.string());
+        }
+
+        // [   0 : 100 ]; file name/path
+        strncpy(buf, relpath.string(), 100);
+
+        LOGI("   Name: %s/%s", buf + 345, buf);
+    }
+
+    // [ 100 :   8 ] file mode
+    snprintf(buf + 100, 8, "0%o", s.st_mode);
+
+    // [ 108 :   8 ] uid -- ignored in Android format; uids are remapped at restore time
+    // [ 116 :   8 ] gid -- ignored in Android format
+    snprintf(buf + 108, 8, "0%lo", s.st_uid);
+    snprintf(buf + 116, 8, "0%lo", s.st_gid);
+
+    // [ 124 :  12 ] file size in bytes
+    snprintf(buf + 124, 12, "0%llo", s.st_size);
+
+    // [ 136 :  12 ] last mod time as a UTC time_t
+    snprintf(buf + 136, 12, "%0lo", s.st_mtime);
+
+    // [ 148 :   8 ] checksum -- to be calculated with this field as space chars
+    memset(buf + 148, ' ', 8);
+
+    // [ 156 :   1 ] link/file type
+    uint8_t type;
+    if (isdir) {
+        type = '5';     // tar magic: '5' == directory
+    } else if (S_ISREG(s.st_mode)) {
+        type = '0';     // tar magic: '0' == normal file
+    } else {
+        LOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string());
+        goto cleanup;
+    }
+    buf[156] = type;
+
+    // [ 157 : 100 ] name of linked file [not implemented]
+
+    // Now go back and calculate the header checksum
+    {
+        uint16_t sum = 0;
+        for (uint8_t* p = (uint8_t*) buf; p < ((uint8_t*)buf) + 512; p++) {
+            sum += *p;
+        }
+
+        // Now write the real checksum value:
+        // [ 148 :   8 ]  checksum: 6 octal digits [leading zeroes], NUL, SPC
+        sprintf(buf + 148, "%06o", sum); // the trailing space is already in place
+    }
+
+    // Write the 512-byte tar file header block to the output
+    writer->WriteEntityData(buf, 512);
+
+    // Now write the file data itself, for real files.  We honor tar's convention that
+    // only full 512-byte blocks are sent to write().
+    if (!isdir) {
+        off64_t toWrite = s.st_size;
+        while (toWrite > 0) {
+            size_t toRead = (toWrite < BUFSIZE) ? toWrite : BUFSIZE;
+            ssize_t nRead = read(fd, buf, toRead);
+            if (nRead < 0) {
+                err = errno;
+                LOGE("Unable to read file [%s], err=%d (%s)", filepath.string(),
+                        err, strerror(err));
+                break;
+            } else if (nRead == 0) {
+                LOGE("EOF but expect %lld more bytes in [%s]", (long long) toWrite,
+                        filepath.string());
+                err = EIO;
+                break;
+            }
+
+            // At EOF we might have a short block; NUL-pad that to a 512-byte multiple.  This
+            // depends on the OS guarantee that for ordinary files, read() will never return
+            // less than the number of bytes requested.
+            ssize_t partial = (nRead+512) % 512;
+            if (partial > 0) {
+                ssize_t remainder = 512 - partial;
+                memset(buf + nRead, 0, remainder);
+                nRead += remainder;
+            }
+            writer->WriteEntityData(buf, nRead);
+            toWrite -= nRead;
+        }
+    }
+
+cleanup:
+    delete [] buf;
+done:
+    close(fd);
+    return err;
+}
+// end tarfile
+
+
+
 #define RESTORE_BUF_SIZE (8*1024)
 
 RestoreHelperBase::RestoreHelperBase()
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 6c61297..84f588e 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -802,13 +802,19 @@
     public void setDataSource(String path, Map<String, String> headers)
             throws IOException, IllegalArgumentException, IllegalStateException
     {
-        int i = 0;
-        String[] keys = new String[headers.size()];
-        String[] values = new String[headers.size()];
-        for (Map.Entry<String, String> entry: headers.entrySet()) {
-            keys[i] = entry.getKey();
-            values[i] = entry.getValue();
-            ++i;
+        String[] keys = null;
+        String[] values = null;
+
+        if (headers != null) {
+            keys = new String[headers.size()];
+            values = new String[headers.size()];
+
+            int i = 0;
+            for (Map.Entry<String, String> entry: headers.entrySet()) {
+                keys[i] = entry.getKey();
+                values[i] = entry.getValue();
+                ++i;
+            }
         }
         _setDataSource(path, keys, values);
     }
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index edefb22..9593f30 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -767,6 +767,71 @@
      */
     public static final int MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED = 801;
 
+    /** informational events for individual tracks, for testing purpose.
+     * The track informational event usually contains two parts in the ext1
+     * arg of the onInfo() callback: bit 31-28 contains the track id; and
+     * the rest of the 28 bits contains the informational event defined here.
+     * For example, ext1 = (1 << 28 | MEDIA_RECORDER_TRACK_INFO_TYPE) if the
+     * track id is 1 for informational event MEDIA_RECORDER_TRACK_INFO_TYPE;
+     * while ext1 = (0 << 28 | MEDIA_RECORDER_TRACK_INFO_TYPE) if the track
+     * id is 0 for informational event MEDIA_RECORDER_TRACK_INFO_TYPE. The
+     * application should extract the track id and the type of informational
+     * event from ext1, accordingly.
+     *
+     * FIXME:
+     * Please update the comment for onInfo also when these
+     * events are unhidden so that application knows how to extract the track
+     * id and the informational event type from onInfo callback.
+     *
+     * {@hide}
+     */
+    public static final int MEDIA_RECORDER_TRACK_INFO_LIST_START        = 1000;
+    /** Signal the completion of the track for the recording session.
+     * {@hide}
+     */
+    public static final int MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS = 1000;
+    /** Indicate the recording progress in time (ms) during recording.
+     * {@hide}
+     */
+    public static final int MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME  = 1001;
+    /** Indicate the track type: 0 for Audio and 1 for Video.
+     * {@hide}
+     */
+    public static final int MEDIA_RECORDER_TRACK_INFO_TYPE              = 1002;
+    /** Provide the track duration information.
+     * {@hide}
+     */
+    public static final int MEDIA_RECORDER_TRACK_INFO_DURATION_MS       = 1003;
+    /** Provide the max chunk duration in time (ms) for the given track.
+     * {@hide}
+     */
+    public static final int MEDIA_RECORDER_TRACK_INFO_MAX_CHUNK_DUR_MS  = 1004;
+    /** Provide the total number of recordd frames.
+     * {@hide}
+     */
+    public static final int MEDIA_RECORDER_TRACK_INFO_ENCODED_FRAMES    = 1005;
+    /** Provide the max spacing between neighboring chunks for the given track.
+     * {@hide}
+     */
+    public static final int MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS    = 1006;
+    /** Provide the elapsed time measuring from the start of the recording
+     * till the first output frame of the given track is received, excluding
+     * any intentional start time offset of a recording session for the
+     * purpose of eliminating the recording sound in the recorded file.
+     * {@hide}
+     */
+    public static final int MEDIA_RECORDER_TRACK_INFO_INITIAL_DELAY_MS  = 1007;
+    /** Provide the start time difference (delay) betweeen this track and
+     * the start of the movie.
+     * {@hide}
+     */
+    public static final int MEDIA_RECORDER_TRACK_INFO_START_OFFSET_MS   = 1008;
+    /**
+     * {@hide}
+     */
+    public static final int MEDIA_RECORDER_TRACK_INFO_LIST_END          = 2000;
+
+
     /**
      * Interface definition for a callback to be invoked when an error
      * occurs while recording.
@@ -811,8 +876,17 @@
         /* Do not change these values without updating their counterparts
          * in include/media/mediarecorder.h!
          */
-        private static final int MEDIA_RECORDER_EVENT_ERROR = 1;
-        private static final int MEDIA_RECORDER_EVENT_INFO  = 2;
+        private static final int MEDIA_RECORDER_EVENT_LIST_START = 1;
+        private static final int MEDIA_RECORDER_EVENT_ERROR      = 1;
+        private static final int MEDIA_RECORDER_EVENT_INFO       = 2;
+        private static final int MEDIA_RECORDER_EVENT_LIST_END   = 99;
+
+        /* Events related to individual tracks */
+        private static final int MEDIA_RECORDER_TRACK_EVENT_LIST_START = 100;
+        private static final int MEDIA_RECORDER_TRACK_EVENT_ERROR      = 100;
+        private static final int MEDIA_RECORDER_TRACK_EVENT_INFO       = 101;
+        private static final int MEDIA_RECORDER_TRACK_EVENT_LIST_END   = 1000;
+
 
         @Override
         public void handleMessage(Message msg) {
@@ -822,12 +896,14 @@
             }
             switch(msg.what) {
             case MEDIA_RECORDER_EVENT_ERROR:
+            case MEDIA_RECORDER_TRACK_EVENT_ERROR:
                 if (mOnErrorListener != null)
                     mOnErrorListener.onError(mMediaRecorder, msg.arg1, msg.arg2);
 
                 return;
 
             case MEDIA_RECORDER_EVENT_INFO:
+            case MEDIA_RECORDER_TRACK_EVENT_INFO:
                 if (mOnInfoListener != null)
                     mOnInfoListener.onInfo(mMediaRecorder, msg.arg1, msg.arg2);
 
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 0d03361..03db8d7 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -338,6 +338,10 @@
     nativeWindow(window), buffer(0), previousBuffer(0), module(0),
     bits(NULL)
 {
+    hw_module_t const* pModule;
+    hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule);
+    module = reinterpret_cast<gralloc_module_t const*>(pModule);
+
     pixelFormatTable = gglGetPixelFormatTable();
     
     // keep a reference on the window
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 2502f15..da26229 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -179,7 +179,8 @@
         __eglMustCastToProperFunctionPointerType* curr, 
         getProcAddressType getProcAddress) 
 {
-    char scrap[256];
+    const size_t SIZE = 256;
+    char scrap[SIZE];
     while (*api) {
         char const * name = *api;
         __eglMustCastToProperFunctionPointerType f = 
@@ -191,7 +192,7 @@
         if (f == NULL) {
             // Try without the OES postfix
             ssize_t index = ssize_t(strlen(name)) - 3;
-            if ((index>0 && (index<255)) && (!strcmp(name+index, "OES"))) {
+            if ((index>0 && (index<SIZE-1)) && (!strcmp(name+index, "OES"))) {
                 strncpy(scrap, name, index);
                 scrap[index] = 0;
                 f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap);
@@ -201,10 +202,8 @@
         if (f == NULL) {
             // Try with the OES postfix
             ssize_t index = ssize_t(strlen(name)) - 3;
-            if ((index>0 && (index<252)) && (strcmp(name+index, "OES"))) {
-                strncpy(scrap, name, index);
-                scrap[index] = 0;
-                strcat(scrap, "OES");
+            if (index>0 && strcmp(name+index, "OES")) {
+                snprintf(scrap, SIZE, "%sOES", name);
                 f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap);
                 //LOGD_IF(f, "found <%s> instead", scrap);
             }
diff --git a/packages/BackupRestoreConfirmation/Android.mk b/packages/BackupRestoreConfirmation/Android.mk
new file mode 100644
index 0000000..e775b44
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/Android.mk
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := BackupRestoreConfirmation
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+########################
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/packages/BackupRestoreConfirmation/AndroidManifest.xml b/packages/BackupRestoreConfirmation/AndroidManifest.xml
new file mode 100644
index 0000000..ed9a519
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.backupconfirm" >
+
+    <uses-permission android:name="android.permission.BACKUP" />
+
+    <application android:allowClearUserData="false"
+                 android:killAfterRestore="false"
+                 android:permission="android.permission.CONFIRM_FULL_BACKUP" >
+
+        <activity android:name=".BackupRestoreConfirmation" 
+                  android:windowSoftInputMode="stateAlwaysHidden"
+                  android:excludeFromRecents="true"
+                  android:exported="true" >
+        </activity>
+    </application>
+</manifest> 
diff --git a/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml b/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml
new file mode 100644
index 0000000..109cfff
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/res/layout/confirm_backup.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="fill_parent" 
+                android:layout_height="wrap_content"
+                android:padding="10dp" >
+
+    <TextView android:id="@+id/confirm_text"
+              android:layout_width="match_parent" 
+              android:layout_height="wrap_content"
+              android:layout_marginBottom="30dp"
+              android:text="@string/backup_confirm_text" />
+
+    <TextView android:id="@+id/package_name"
+              android:layout_width="match_parent"
+              android:layout_height="20dp"
+              android:layout_marginLeft="30dp"
+              android:layout_below="@id/confirm_text"
+              android:layout_marginBottom="30dp" />
+
+    <Button android:id="@+id/button_allow"
+            android:text="@string/allow_backup_button_label"
+            android:layout_below="@id/package_name"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+    <Button android:id="@+id/button_deny"
+            android:text="@string/deny_backup_button_label"
+            android:layout_below="@id/package_name"
+            android:layout_toRightOf="@id/button_allow"
+            android:layout_alignTop="@id/button_allow"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+</RelativeLayout>
diff --git a/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml b/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml
new file mode 100644
index 0000000..a1f9a4a
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/res/layout/confirm_restore.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="fill_parent" 
+                android:layout_height="wrap_content"
+                android:padding="10dp" >
+
+    <TextView android:id="@+id/confirm_text"
+              android:layout_width="match_parent" 
+              android:layout_height="wrap_content"
+              android:layout_marginBottom="30dp"
+              android:text="@string/restore_confirm_text" />
+
+    <TextView android:id="@+id/package_name"
+              android:layout_width="match_parent"
+              android:layout_height="20dp"
+              android:layout_marginLeft="30dp"
+              android:layout_below="@id/confirm_text"
+              android:layout_marginBottom="30dp" />
+
+    <Button android:id="@+id/button_allow"
+            android:text="@string/allow_restore_button_label"
+            android:layout_below="@id/package_name"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+    <Button android:id="@+id/button_deny"
+            android:text="@string/deny_restore_button_label"
+            android:layout_below="@id/package_name"
+            android:layout_toRightOf="@id/button_allow"
+            android:layout_alignTop="@id/button_allow"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+</RelativeLayout>
diff --git a/packages/BackupRestoreConfirmation/res/values/strings.xml b/packages/BackupRestoreConfirmation/res/values/strings.xml
new file mode 100644
index 0000000..3d85e86
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/res/values/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Text for message to user that a full backup has been requested, and must be confirmed. -->
+    <string name="backup_confirm_text">A full backup of all data to a connected desktop computer has been requested.  Do you want to allow this to happen\?\n\nIf you did not request the backup yourself, do not allow the operation to proceed.</string>
+    <!-- Button to allow a requested full backup to occur -->
+    <string name="allow_backup_button_label">Back up my data</string>
+    <!-- Button to refuse to allow the requested full backup -->
+    <string name="deny_backup_button_label">Do not back up</string>
+
+    <!-- Text for message to user that a full restore has been requested, and must be confirmed. -->
+    <string name="restore_confirm_text">A full restore of all data from a connected desktop computer has been requested.  Do you want to allow this to happen\?\n\nIf you did not request the restore yourself, do not allow the operation to proceed.  This will replace any data currently on the device!</string>
+    <!-- Button to allow a requested full restore to occur -->
+    <string name="allow_restore_button_label">Restore my data</string>
+    <!-- Button to refuse to allow the requested full restore -->
+    <string name="deny_restore_button_label">Do not restore</string>
+
+</resources>
diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
new file mode 100644
index 0000000..805b905
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.backupconfirm;
+
+import android.app.Activity;
+import android.app.backup.FullBackup;
+import android.app.backup.IBackupManager;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Confirm with the user that a requested full backup/restore operation is legitimate.
+ * Any attempt to perform a full backup/restore will launch this UI and wait for a
+ * designated timeout interval (nominally 30 seconds) for the user to confirm.  If the
+ * user fails to respond within the timeout period, or explicitly refuses the operation
+ * within the UI presented here, no data will be transferred off the device.
+ *
+ * Note that the fully scoped name of this class is baked into the backup manager service.
+ *
+ * @hide
+ */
+public class BackupRestoreConfirmation extends Activity {
+    static final String TAG = "BackupRestoreConfirmation";
+    static final boolean DEBUG = true;
+
+    static final int MSG_START_BACKUP = 1;
+    static final int MSG_BACKUP_PACKAGE = 2;
+    static final int MSG_END_BACKUP = 3;
+    static final int MSG_START_RESTORE = 11;
+    static final int MSG_RESTORE_PACKAGE = 12;
+    static final int MSG_END_RESTORE = 13;
+    static final int MSG_TIMEOUT = 100;
+
+    Handler mHandler;
+    IBackupManager mBackupManager;
+    FullObserver mObserver;
+    int mToken;
+
+    TextView mStatusView;
+    Button mAllowButton;
+    Button mDenyButton;
+
+    // Handler for dealing with observer callbacks on the main thread
+    class ObserverHandler extends Handler {
+        Context mContext;
+        ObserverHandler(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_START_BACKUP: {
+                    Toast.makeText(mContext, "!!! Backup starting !!!", Toast.LENGTH_LONG);
+                }
+                break;
+
+                case MSG_BACKUP_PACKAGE: {
+                    String name = (String) msg.obj;
+                    mStatusView.setText(name);
+                }
+                break;
+
+                case MSG_END_BACKUP: {
+                    Toast.makeText(mContext, "!!! Backup ended !!!", Toast.LENGTH_SHORT);
+                }
+                break;
+
+                case MSG_START_RESTORE: {
+                    Toast.makeText(mContext, "!!! Restore starting !!!", Toast.LENGTH_LONG);
+                }
+                break;
+
+                case MSG_RESTORE_PACKAGE: {
+                }
+                break;
+
+                case MSG_END_RESTORE: {
+                    Toast.makeText(mContext, "!!! Restore ended !!!", Toast.LENGTH_SHORT);
+                }
+                break;
+
+                case MSG_TIMEOUT: {
+                }
+                break;
+            }
+        }
+    }
+    
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        final Intent intent = getIntent();
+        final String action = intent.getAction();
+
+        int layoutId;
+        if (action.equals(FullBackup.FULL_BACKUP_INTENT_ACTION)) {
+            layoutId = R.layout.confirm_backup;
+        } else if (action.equals(FullBackup.FULL_RESTORE_INTENT_ACTION)) {
+            layoutId = R.layout.confirm_restore;
+        } else {
+            Slog.w(TAG, "Backup/restore confirmation activity launched with invalid action!");
+            finish();
+            return;
+        }
+
+        mToken = intent.getIntExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, -1);
+        if (mToken < 0) {
+            Slog.e(TAG, "Backup/restore confirmation requested but no token passed!");
+            finish();
+            return;
+        }
+
+        mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService(Context.BACKUP_SERVICE));
+
+        mHandler = new ObserverHandler(getApplicationContext());
+        mObserver = new FullObserver();
+
+        setContentView(layoutId);
+
+        // Same resource IDs for each layout variant (backup / restore)
+        mStatusView = (TextView) findViewById(R.id.package_name);
+        mAllowButton = (Button) findViewById(R.id.button_allow);
+        mDenyButton = (Button) findViewById(R.id.button_deny);
+
+        mAllowButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                try {
+                    mBackupManager.acknowledgeFullBackupOrRestore(mToken, true, mObserver);
+                } catch (RemoteException e) {
+                    // TODO: bail gracefully if we can't contact the backup manager
+                }
+                mAllowButton.setEnabled(false);
+                mDenyButton.setEnabled(false);
+            }
+        });
+
+        mDenyButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                try {
+                    mBackupManager.acknowledgeFullBackupOrRestore(mToken, false, mObserver);
+                } catch (RemoteException e) {
+                    // TODO: bail gracefully if we can't contact the backup manager
+                }
+                mAllowButton.setEnabled(false);
+                mDenyButton.setEnabled(false);
+            }
+        });
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+
+        // We explicitly equate departure from the UI with refusal.  This includes the
+        // implicit configuration-changed stop/restart cycle.
+        try {
+            mBackupManager.acknowledgeFullBackupOrRestore(mToken, false, null);
+        } catch (RemoteException e) {
+            // if this fails we'll still time out with no acknowledgment
+        }
+        finish();
+    }
+
+    /**
+     * The observer binder for showing backup/restore progress.  This binder just bounces
+     * the notifications onto the main thread.
+     */
+    class FullObserver extends IFullBackupRestoreObserver.Stub {
+        //
+        // IFullBackupRestoreObserver implementation
+        //
+        @Override
+        public void onStartBackup() throws RemoteException {
+            mHandler.sendEmptyMessage(MSG_START_BACKUP);
+        }
+
+        @Override
+        public void onBackupPackage(String name) throws RemoteException {
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_BACKUP_PACKAGE, name));
+        }
+
+        @Override
+        public void onEndBackup() throws RemoteException {
+            mHandler.sendEmptyMessage(MSG_END_BACKUP);
+        }
+
+        @Override
+        public void onStartRestore() throws RemoteException {
+            mHandler.sendEmptyMessage(MSG_START_RESTORE);
+        }
+
+        @Override
+        public void onRestorePackage(String name) throws RemoteException {
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_RESTORE_PACKAGE, name));
+        }
+
+        @Override
+        public void onEndRestore() throws RemoteException {
+            mHandler.sendEmptyMessage(MSG_END_RESTORE);
+        }        
+
+        @Override
+        public void onTimeout() throws RemoteException {
+            mHandler.sendEmptyMessage(MSG_TIMEOUT);
+        }
+    }
+}
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index dd0d064..e5f52e2 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -6,6 +6,7 @@
                  android:label="@string/app_label"
                  android:process="system"
                  android:backupAgent="SettingsBackupAgent"
+                 android:fullBackupAgent="SettingsBackupAgent"
                  android:killAfterRestore="false"
                  android:icon="@drawable/ic_launcher_settings">
                  
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 744798e..45bb2b6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -36,6 +36,7 @@
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
 import android.app.backup.BackupAgentHelper;
+import android.app.backup.FullBackup;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
@@ -99,6 +100,10 @@
     private static final String KEY_WIFI_SUPPLICANT = "\uffedWIFI";
     private static final String KEY_WIFI_CONFIG = "\uffedCONFIG_WIFI";
 
+    // Name of the temporary file we use during full backup/restore.  This is
+    // stored in the full-backup tarfile as well, so should not be changed.
+    private static final String STAGE_FILE = "flattened-data";
+
     private SettingsHelper mSettingsHelper;
     private WifiManager mWfm;
     private static String mWifiConfigFile;
@@ -121,22 +126,52 @@
         byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
         byte[] wifiConfigData = getFileData(mWifiConfigFile);
 
-        long[] stateChecksums = readOldChecksums(oldState);
+        // This same agent class is used for both full and incremental backups.  A full
+        // backup is flagged by a 'null' oldState argument.  In the case of a full backup,
+        // the output is structured as tarfile contents.
+        if (oldState != null) {
+            long[] stateChecksums = readOldChecksums(oldState);
 
-        stateChecksums[STATE_SYSTEM] =
+            stateChecksums[STATE_SYSTEM] =
                 writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
-        stateChecksums[STATE_SECURE] =
+            stateChecksums[STATE_SECURE] =
                 writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
-        stateChecksums[STATE_LOCALE] =
+            stateChecksums[STATE_LOCALE] =
                 writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
-        stateChecksums[STATE_WIFI_SUPPLICANT] =
+            stateChecksums[STATE_WIFI_SUPPLICANT] =
                 writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT,
-                wifiSupplicantData, data);
-        stateChecksums[STATE_WIFI_CONFIG] =
+                        wifiSupplicantData, data);
+            stateChecksums[STATE_WIFI_CONFIG] =
                 writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData,
-                data);
+                        data);
 
-        writeNewChecksums(stateChecksums, newState);
+            writeNewChecksums(stateChecksums, newState);
+        } else {
+            // Write the data to the staging file, then emit that as our tarfile
+            // 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);
+
+            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.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);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/res/drawable-large-hdpi/app_icon.png b/packages/SystemUI/res/drawable-large-hdpi/app_icon.png
new file mode 100644
index 0000000..aedf7e7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/app_icon.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-large-hdpi/recents_bg_protect_tile.png
new file mode 100644
index 0000000..a57c27a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/recents_bg_protect_tile.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_1x.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_1x.png
new file mode 100644
index 0000000..1629575
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_3g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_3g.png
new file mode 100644
index 0000000..e87bcec
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_4g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_4g.png
new file mode 100644
index 0000000..fd44002
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_e.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_e.png
new file mode 100644
index 0000000..e455ad6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_g.png
new file mode 100644
index 0000000..6613585
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_h.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_h.png
new file mode 100644
index 0000000..7502a54
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_1x.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_1x.png
new file mode 100644
index 0000000..39dfcd3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_3g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_3g.png
new file mode 100644
index 0000000..e78d872
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_4g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_4g.png
new file mode 100644
index 0000000..5f88279
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_e.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_e.png
new file mode 100644
index 0000000..ad73252
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_g.png
new file mode 100644
index 0000000..d02de5b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_h.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_h.png
new file mode 100644
index 0000000..cb13b91
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_in_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_1x.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_1x.png
new file mode 100644
index 0000000..39dfcd3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_3g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_3g.png
new file mode 100644
index 0000000..e78d872
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_4g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_4g.png
new file mode 100644
index 0000000..8c04bd61
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_e.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_e.png
new file mode 100644
index 0000000..ad73252
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_g.png
new file mode 100644
index 0000000..d02de5b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_h.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_h.png
new file mode 100644
index 0000000..cb13b91
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_inandout_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_1x.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_1x.png
new file mode 100644
index 0000000..39dfcd3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_3g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_3g.png
new file mode 100644
index 0000000..e78d872
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_4g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_4g.png
new file mode 100644
index 0000000..456d0b6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_e.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_e.png
new file mode 100644
index 0000000..ad73252
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_g.png
new file mode 100644
index 0000000..d02de5b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_h.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_h.png
new file mode 100644
index 0000000..cb13b91
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_out_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_roam.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_roam.png
new file mode 100644
index 0000000..1309176
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_connected_roam.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_1x.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_1x.png
new file mode 100644
index 0000000..810714e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_3g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_3g.png
new file mode 100644
index 0000000..72176ba
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_4g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_4g.png
new file mode 100644
index 0000000..82a2bf0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_e.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_e.png
new file mode 100644
index 0000000..1e6c604
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_g.png
new file mode 100644
index 0000000..13aee06
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_h.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_h.png
new file mode 100644
index 0000000..76c48a8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_connected_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_1x.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_1x.png
new file mode 100644
index 0000000..39dfcd3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_3g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_3g.png
new file mode 100644
index 0000000..e78d872
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_4g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_4g.png
new file mode 100644
index 0000000..456d0b6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_e.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_e.png
new file mode 100644
index 0000000..ad73252
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_g.png
new file mode 100644
index 0000000..d02de5b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_h.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_h.png
new file mode 100644
index 0000000..cb13b91
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_in_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_1x.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_1x.png
new file mode 100644
index 0000000..39dfcd3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_3g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_3g.png
new file mode 100644
index 0000000..e78d872
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_4g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_4g.png
new file mode 100644
index 0000000..456d0b6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_e.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_e.png
new file mode 100644
index 0000000..ad73252
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_g.png
new file mode 100644
index 0000000..d02de5b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_h.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_h.png
new file mode 100644
index 0000000..cb13b91
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_inandout_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_1x.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_1x.png
new file mode 100644
index 0000000..39dfcd3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_3g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_3g.png
new file mode 100644
index 0000000..e78d872
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_4g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_4g.png
new file mode 100644
index 0000000..456d0b6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_e.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_e.png
new file mode 100644
index 0000000..ad73252
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_g.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_g.png
new file mode 100644
index 0000000..d02de5b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_h.png b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_h.png
new file mode 100644
index 0000000..cb13b91
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-hdpi/stat_sys_data_fully_out_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/app_icon.png b/packages/SystemUI/res/drawable-large-mdpi/app_icon.png
new file mode 100644
index 0000000..50a8ac8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/app_icon.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/recents_bg_protect_tile.png b/packages/SystemUI/res/drawable-large-mdpi/recents_bg_protect_tile.png
new file mode 100644
index 0000000..87c7be6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/recents_bg_protect_tile.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/recents_blue_glow.9.png b/packages/SystemUI/res/drawable-large-mdpi/recents_blue_glow.9.png
new file mode 100644
index 0000000..4f4ae78
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/recents_blue_glow.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/recents_callout_line.png b/packages/SystemUI/res/drawable-large-mdpi/recents_callout_line.png
new file mode 100644
index 0000000..5f4c035
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/recents_callout_line.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/recents_thumbnail_bg.png b/packages/SystemUI/res/drawable-large-mdpi/recents_thumbnail_bg.png
new file mode 100644
index 0000000..87a67c9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/recents_thumbnail_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/recents_thumbnail_bg_press.png b/packages/SystemUI/res/drawable-large-mdpi/recents_thumbnail_bg_press.png
new file mode 100644
index 0000000..a1c39e6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/recents_thumbnail_bg_press.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_1x.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_1x.png
new file mode 100644
index 0000000..73cdabe
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_3g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_3g.png
new file mode 100644
index 0000000..b5a39cc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_4g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_4g.png
new file mode 100644
index 0000000..6de880c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_e.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_e.png
new file mode 100644
index 0000000..2dbbfc0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_g.png
new file mode 100644
index 0000000..a45f011
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_h.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_h.png
new file mode 100644
index 0000000..517a6bb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_1x.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_1x.png
new file mode 100644
index 0000000..f50c987
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_3g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_3g.png
new file mode 100644
index 0000000..1fff17e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_4g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_4g.png
new file mode 100644
index 0000000..821d00e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_e.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_e.png
new file mode 100644
index 0000000..e27439e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_g.png
new file mode 100644
index 0000000..8b53f0f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_h.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_h.png
new file mode 100644
index 0000000..a23ec6e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_in_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_1x.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_1x.png
new file mode 100644
index 0000000..f50c987
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_3g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_3g.png
new file mode 100644
index 0000000..1fff17e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_4g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_4g.png
new file mode 100644
index 0000000..4cdce84
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_e.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_e.png
new file mode 100644
index 0000000..e27439e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_g.png
new file mode 100644
index 0000000..8b53f0f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_h.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_h.png
new file mode 100644
index 0000000..a23ec6e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_inandout_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_1x.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_1x.png
new file mode 100644
index 0000000..f50c987
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_3g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_3g.png
new file mode 100644
index 0000000..1fff17e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_4g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_4g.png
new file mode 100644
index 0000000..5a2662a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_e.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_e.png
new file mode 100644
index 0000000..e27439e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_g.png
new file mode 100644
index 0000000..8b53f0f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_h.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_h.png
new file mode 100644
index 0000000..a23ec6e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_out_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_roam.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_roam.png
new file mode 100644
index 0000000..5226c1c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_connected_roam.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_1x.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_1x.png
new file mode 100644
index 0000000..a845648
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_3g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_3g.png
new file mode 100644
index 0000000..1d15587
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_4g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_4g.png
new file mode 100644
index 0000000..52d5736
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_e.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_e.png
new file mode 100644
index 0000000..79a2a05
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_g.png
new file mode 100644
index 0000000..b032594
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_h.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_h.png
new file mode 100644
index 0000000..c4abcc9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_connected_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_1x.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_1x.png
new file mode 100644
index 0000000..f50c987
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_3g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_3g.png
new file mode 100644
index 0000000..1fff17e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_4g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_4g.png
new file mode 100644
index 0000000..5a2662a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_e.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_e.png
new file mode 100644
index 0000000..e27439e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_g.png
new file mode 100644
index 0000000..8b53f0f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_h.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_h.png
new file mode 100644
index 0000000..a23ec6e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_in_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_1x.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_1x.png
new file mode 100644
index 0000000..f50c987
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_3g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_3g.png
new file mode 100644
index 0000000..1fff17e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_4g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_4g.png
new file mode 100644
index 0000000..5a2662a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_e.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_e.png
new file mode 100644
index 0000000..e27439e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_g.png
new file mode 100644
index 0000000..8b53f0f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_h.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_h.png
new file mode 100644
index 0000000..a23ec6e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_inandout_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_1x.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_1x.png
new file mode 100644
index 0000000..f50c987
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_3g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_3g.png
new file mode 100644
index 0000000..1fff17e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_4g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_4g.png
new file mode 100644
index 0000000..5a2662a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_4g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_e.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_e.png
new file mode 100644
index 0000000..e27439e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_g.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_g.png
new file mode 100644
index 0000000..8b53f0f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_h.png b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_h.png
new file mode 100644
index 0000000..a23ec6e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-large-mdpi/stat_sys_data_fully_out_h.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-large/status_bar.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar.xml
rename to packages/SystemUI/res/layout-large/status_bar.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_input_methods_item.xml b/packages/SystemUI/res/layout-large/status_bar_input_methods_item.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_input_methods_item.xml
rename to packages/SystemUI/res/layout-large/status_bar_input_methods_item.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_input_methods_panel.xml b/packages/SystemUI/res/layout-large/status_bar_input_methods_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_input_methods_panel.xml
rename to packages/SystemUI/res/layout-large/status_bar_input_methods_panel.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_notification_area.xml b/packages/SystemUI/res/layout-large/status_bar_notification_area.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_notification_area.xml
rename to packages/SystemUI/res/layout-large/status_bar_notification_area.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel.xml b/packages/SystemUI/res/layout-large/status_bar_notification_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_notification_panel.xml
rename to packages/SystemUI/res/layout-large/status_bar_notification_panel.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel_title.xml b/packages/SystemUI/res/layout-large/status_bar_notification_panel_title.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_notification_panel_title.xml
rename to packages/SystemUI/res/layout-large/status_bar_notification_panel_title.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_notification_peek.xml b/packages/SystemUI/res/layout-large/status_bar_notification_peek.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_notification_peek.xml
rename to packages/SystemUI/res/layout-large/status_bar_notification_peek.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_notification_row.xml b/packages/SystemUI/res/layout-large/status_bar_notification_row.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_notification_row.xml
rename to packages/SystemUI/res/layout-large/status_bar_notification_row.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_pocket_panel.xml b/packages/SystemUI/res/layout-large/status_bar_pocket_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_pocket_panel.xml
rename to packages/SystemUI/res/layout-large/status_bar_pocket_panel.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml b/packages/SystemUI/res/layout-large/status_bar_recent_item.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
rename to packages/SystemUI/res/layout-large/status_bar_recent_item.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-large/status_bar_recent_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
rename to packages/SystemUI/res/layout-large/status_bar_recent_panel.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel_footer.xml b/packages/SystemUI/res/layout-large/status_bar_recent_panel_footer.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_recent_panel_footer.xml
rename to packages/SystemUI/res/layout-large/status_bar_recent_panel_footer.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml b/packages/SystemUI/res/layout-large/status_bar_settings_view.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml
rename to packages/SystemUI/res/layout-large/status_bar_settings_view.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_ticker_compat.xml b/packages/SystemUI/res/layout-large/status_bar_ticker_compat.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_ticker_compat.xml
rename to packages/SystemUI/res/layout-large/status_bar_ticker_compat.xml
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_ticker_panel.xml b/packages/SystemUI/res/layout-large/status_bar_ticker_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-xlarge/status_bar_ticker_panel.xml
rename to packages/SystemUI/res/layout-large/status_bar_ticker_panel.xml
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index eba4480..b97c6a5 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -25,100 +25,106 @@
     android:layout_width="match_parent"
     >
 
-    <LinearLayout android:id="@+id/rot0"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent"
-        android:paddingLeft="8dip"
-        android:paddingRight="8dip"
-        android:background="#FF000000"
-        android:orientation="horizontal"
-        >
-
-        <!-- navigation controls -->
-        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:src="@drawable/ic_sysbar_back"
-            systemui:keyCode="4"
-            android:layout_weight="1"
-            />
-        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:src="@drawable/ic_sysbar_home"
-            systemui:keyCode="3"
-            android:layout_weight="1"
-            />
-        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:src="@drawable/ic_sysbar_menu"
-            systemui:keyCode="82"
-            android:layout_weight="1"
-            />
-    </LinearLayout>
-
-    <LinearLayout android:id="@+id/rot90"
+    <FrameLayout
+        android:id="@+id/background"
         android:layout_height="match_parent"
         android:layout_width="match_parent"
         android:background="#FF000000"
-        android:orientation="vertical"
-        android:visibility="gone"
         >
 
-        <!-- navigation controls -->
-        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
+        <LinearLayout android:id="@+id/rot0"
+            android:layout_height="match_parent"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:src="@drawable/ic_sysbar_menu"
-            systemui:keyCode="82"
-            android:layout_weight="1"
-            />
-        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:src="@drawable/ic_sysbar_home"
-            systemui:keyCode="3"
-            android:layout_weight="1"
-            />
-        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:src="@drawable/ic_sysbar_back"
-            systemui:keyCode="4"
-            android:layout_weight="1"
-            />
-    </LinearLayout>
+            android:paddingLeft="8dip"
+            android:paddingRight="8dip"
+            android:orientation="horizontal"
+            >
 
-    <LinearLayout android:id="@+id/rot270"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent"
-        android:background="#FF000000"
-        android:orientation="vertical"
-        android:visibility="gone"
-        >
+            <!-- navigation controls -->
+            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:src="@drawable/ic_sysbar_back"
+                systemui:keyCode="4"
+                android:layout_weight="1"
+                />
+            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:src="@drawable/ic_sysbar_home"
+                systemui:keyCode="3"
+                android:layout_weight="1"
+                />
+            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:src="@drawable/ic_sysbar_menu"
+                systemui:keyCode="82"
+                android:layout_weight="1"
+                />
+        </LinearLayout>
 
-        <!-- navigation controls -->
-        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
+        <LinearLayout android:id="@+id/rot90"
+            android:layout_height="match_parent"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:src="@drawable/ic_sysbar_back"
-            systemui:keyCode="4"
-            android:layout_weight="1"
-            />
-        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
+            android:orientation="vertical"
+            android:visibility="gone"
+            >
+
+            <!-- navigation controls -->
+            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:src="@drawable/ic_sysbar_menu"
+                systemui:keyCode="82"
+                android:layout_weight="1"
+                />
+            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:src="@drawable/ic_sysbar_home"
+                systemui:keyCode="3"
+                android:layout_weight="1"
+                />
+            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:src="@drawable/ic_sysbar_back"
+                systemui:keyCode="4"
+                android:layout_weight="1"
+                />
+        </LinearLayout>
+
+        <LinearLayout android:id="@+id/rot270"
+            android:layout_height="match_parent"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:src="@drawable/ic_sysbar_home"
-            systemui:keyCode="3"
-            android:layout_weight="1"
-            />
-        <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:src="@drawable/ic_sysbar_menu"
-            systemui:keyCode="82"
-            android:layout_weight="1"
-            />
-    </LinearLayout>
+            android:orientation="vertical"
+            android:visibility="gone"
+            >
+
+            <!-- navigation controls -->
+            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:src="@drawable/ic_sysbar_back"
+                systemui:keyCode="4"
+                android:layout_weight="1"
+                />
+            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:src="@drawable/ic_sysbar_home"
+                systemui:keyCode="3"
+                android:layout_weight="1"
+                />
+            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:src="@drawable/ic_sysbar_menu"
+                systemui:keyCode="82"
+                android:layout_weight="1"
+                />
+        </LinearLayout>
+    
+    </FrameLayout>
 </com.android.systemui.statusbar.phone.NavigationBarView>
diff --git a/packages/SystemUI/res/values-large-port/dimens.xml b/packages/SystemUI/res/values-large-port/dimens.xml
new file mode 100644
index 0000000..56effa3
--- /dev/null
+++ b/packages/SystemUI/res/values-large-port/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+*/
+-->
+<resources>
+    <!-- gap on either side of status bar notification icons -->
+    <dimen name="status_bar_icon_padding">2dp</dimen>
+</resources>
+
+
+
diff --git a/packages/SystemUI/res/values-large/colors.xml b/packages/SystemUI/res/values-large/colors.xml
new file mode 100644
index 0000000..a7a70c3
--- /dev/null
+++ b/packages/SystemUI/res/values-large/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <drawable name="status_bar_background">#000000</drawable>
+    <drawable name="notification_icon_area_smoke">#aa000000</drawable>
+</resources>
+
diff --git a/packages/SystemUI/res/values-xlarge/config.xml b/packages/SystemUI/res/values-large/config.xml
similarity index 100%
rename from packages/SystemUI/res/values-xlarge/config.xml
rename to packages/SystemUI/res/values-large/config.xml
diff --git a/packages/SystemUI/res/values-large/dimens.xml b/packages/SystemUI/res/values-large/dimens.xml
new file mode 100644
index 0000000..9d89e21
--- /dev/null
+++ b/packages/SystemUI/res/values-large/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+*/
+-->
+<resources>
+    <!-- The width of the ticker, including the icon -->
+    <dimen name="notification_ticker_width">360dp</dimen>
+    <!-- Status bar panel bottom offset (height of status bar - overlap) -->
+    <dimen name="status_bar_panel_bottom_offset">36dp</dimen>
+    <!-- gap on either side of status bar notification icons -->
+    <dimen name="status_bar_icon_padding">8dp</dimen>
+</resources>
+
+
diff --git a/packages/SystemUI/res/values-xlarge/strings.xml b/packages/SystemUI/res/values-large/strings.xml
similarity index 100%
rename from packages/SystemUI/res/values-xlarge/strings.xml
rename to packages/SystemUI/res/values-large/strings.xml
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 05ed089..c0c39d0 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -38,5 +38,9 @@
     <!-- Whether or not we show the number in the bar. -->
     <bool name="config_statusBarShowNumber">true</bool>
 
+    <!-- Whether a software navigation bar should be shown. NOTE: in the future this may be
+         autodetected from the Configuration. -->
+    <bool name="config_showNavigationBar">true</bool>
+
 </resources>
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index d55a7c2..da1e1c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -35,28 +35,33 @@
 public class CommandQueue extends IStatusBar.Stub {
     private static final String TAG = "StatusBar.CommandQueue";
 
-    private static final int MSG_MASK = 0xffff0000;
-    private static final int INDEX_MASK = 0x0000ffff;
+    private static final int INDEX_MASK = 0xffff;
+    private static final int MSG_SHIFT  = 16;
+    private static final int MSG_MASK   = 0xffff << MSG_SHIFT;
 
-    private static final int MSG_ICON = 0x00010000;
-    private static final int OP_SET_ICON = 1;
+
+    private static final int MSG_ICON                   = 1 << MSG_SHIFT;
+    private static final int OP_SET_ICON    = 1;
     private static final int OP_REMOVE_ICON = 2;
 
-    private static final int MSG_ADD_NOTIFICATION = 0x00020000;
-    private static final int MSG_UPDATE_NOTIFICATION = 0x00030000;
-    private static final int MSG_REMOVE_NOTIFICATION = 0x00040000;
+    private static final int MSG_ADD_NOTIFICATION       = 2 << MSG_SHIFT;
+    private static final int MSG_UPDATE_NOTIFICATION    = 3 << MSG_SHIFT;
+    private static final int MSG_REMOVE_NOTIFICATION    = 4 << MSG_SHIFT;
 
-    private static final int MSG_DISABLE = 0x00050000;
+    private static final int MSG_DISABLE                = 5 << MSG_SHIFT;
 
-    private static final int MSG_SET_VISIBILITY = 0x00060000;
-    private static final int OP_EXPAND = 1;
-    private static final int OP_COLLAPSE = 2;
+    private static final int MSG_SET_VISIBILITY         = 6 << MSG_SHIFT;
+    private static final int OP_EXPAND      = 1;
+    private static final int OP_COLLAPSE    = 2;
 
-    private static final int MSG_SET_LIGHTS_ON = 0x00070000;
+    private static final int MSG_SET_LIGHTS_ON          = 7 << MSG_SHIFT;
 
-    private static final int MSG_SHOW_MENU = 0x00080000;
-    private static final int MSG_SHOW_IME_BUTTON = 0x00090000;
-    private static final int MSG_SET_HARD_KEYBOARD_STATUS = 0x000a0000;
+    private static final int MSG_SHOW_MENU              = 8 << MSG_SHIFT;
+    private static final int MSG_SHOW_IME_BUTTON        = 9 << MSG_SHIFT;
+    private static final int MSG_SET_HARD_KEYBOARD_STATUS = 10 << MSG_SHIFT;
+    
+    private static final int MSG_USER_ACTIVITY          = 11 << MSG_SHIFT;
+
 
     private StatusBarIconList mList;
     private Callbacks mCallbacks;
@@ -85,6 +90,7 @@
         public void setMenuKeyVisible(boolean visible);
         public void setImeWindowStatus(IBinder token, int vis, int backDisposition);
         public void setHardKeyboardStatus(boolean available, boolean enabled);
+        public void userActivity();
     }
 
     public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -183,6 +189,13 @@
         }
     }
 
+    public void userActivity() {
+        synchronized (mList) {
+            mHandler.removeMessages(MSG_USER_ACTIVITY);
+            mHandler.obtainMessage(MSG_USER_ACTIVITY, 0, 0, null).sendToTarget();
+        }
+    }
+
     private final class H extends Handler {
         public void handleMessage(Message msg) {
             final int what = msg.what & MSG_MASK;
@@ -249,6 +262,9 @@
                 case MSG_SET_HARD_KEYBOARD_STATUS:
                     mCallbacks.setHardKeyboardStatus(msg.arg1 != 0, msg.arg2 != 0);
                     break;
+                case MSG_USER_ACTIVITY:
+                    mCallbacks.userActivity();
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index ec169e5..7d6c57b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -16,29 +16,73 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.os.ServiceManager;
 import android.util.AttributeSet;
 import android.view.Display;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.Surface;
 import android.view.WindowManager;
 import android.widget.LinearLayout;
 import android.content.res.Configuration;
 
+import com.android.internal.statusbar.IStatusBarService;
+
 import com.android.systemui.R;
 
 public class NavigationBarView extends LinearLayout {
+    protected IStatusBarService mBarService;
     final Display mDisplay;
     View[] mRotatedViews = new View[4];
+    View mBackground;
+    Animator mLastAnimator = null;
 
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mDisplay = ((WindowManager)context.getSystemService(
                 Context.WINDOW_SERVICE)).getDefaultDisplay();
+        mBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+
+        //setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+        setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
+            @Override
+            public void onSystemUiVisibilityChange(int visibility) {
+                boolean on = (visibility == View.STATUS_BAR_VISIBLE);
+                android.util.Log.d("NavigationBarView", "LIGHTS " 
+                    + (on ? "ON" : "OUT"));
+                setLights(on);
+            }
+        });
+    }
+
+    private void setLights(final boolean on) {
+        float oldAlpha = mBackground.getAlpha();
+        android.util.Log.d("NavigationBarView", "animating alpha: " + oldAlpha + " -> "
+            + (on ? 1f : 0f));
+
+        if (mLastAnimator != null && mLastAnimator.isRunning()) mLastAnimator.cancel();
+
+        mLastAnimator = ObjectAnimator.ofFloat(mBackground, "alpha", oldAlpha, on ? 1f : 0f)
+            .setDuration(on ? 250 : 1500);
+        mLastAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator _a) {
+                mLastAnimator = null;
+            }
+        });
+        mLastAnimator.start();
     }
 
     public void onFinishInflate() {
+        mBackground = findViewById(R.id.background);
+
         mRotatedViews[Surface.ROTATION_0] = 
         mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
 
@@ -47,6 +91,13 @@
         mRotatedViews[Surface.ROTATION_270] = findViewById(R.id.rot270);
     }
 
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        // immediately bring up the lights
+        setLights(true);
+        return false; // pass it on
+    }
+
     public void reorient() {
         final int rot = mDisplay.getRotation();
         for (int i=0; i<4; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index b4adde6..e66a1a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -36,12 +36,14 @@
 import android.os.RemoteException;
 import android.os.Handler;
 import android.os.Message;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.Slog;
 import android.util.Log;
 import android.view.Display;
 import android.view.Gravity;
+import android.view.IWindowManager;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -101,6 +103,8 @@
     int mIconSize;
     Display mDisplay;
 
+    IWindowManager mWindowManager;
+
     PhoneStatusBarView mStatusBarView;
     int mPixelFormat;
     H mHandler = new H();
@@ -143,7 +147,7 @@
     private View mIntruderAlertView;
 
     // on-screen navigation buttons
-    private NavigationBarView mNavigationBarView;
+    private NavigationBarView mNavigationBarView = null;
 
     // the tracker view
     TrackingView mTrackingView;
@@ -201,6 +205,9 @@
         mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
                 .getDefaultDisplay();
 
+        mWindowManager = IWindowManager.Stub.asInterface(
+                ServiceManager.getService(Context.WINDOW_SERVICE));
+
         super.start();
 
         addNavigationBar();
@@ -229,8 +236,15 @@
         mIntruderAlertView.setVisibility(View.GONE);
         mIntruderAlertView.setClickable(true);
 
-        mNavigationBarView = 
-            (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
+        try {
+            boolean showNav = res.getBoolean(R.bool.config_showNavigationBar);
+            if (showNav) {
+                mNavigationBarView = 
+                    (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
+            }
+        } catch (Resources.NotFoundException ex) {
+            // no nav bar for you
+        }
 
         PhoneStatusBarView sb = (PhoneStatusBarView)View.inflate(context,
                 R.layout.status_bar, null);
@@ -303,12 +317,16 @@
 
     // For small-screen devices (read: phones) that lack hardware navigation buttons
     private void addNavigationBar() {
+        if (mNavigationBarView == null) return;
+        
         mNavigationBarView.reorient();
         WindowManagerImpl.getDefault().addView(
                 mNavigationBarView, getNavigationBarLayoutParams());
     }
 
     private void repositionNavigationBar() {
+        if (mNavigationBarView == null) return;
+        
         mNavigationBarView.reorient();
         WindowManagerImpl.getDefault().updateViewLayout(
                 mNavigationBarView, getNavigationBarLayoutParams());
@@ -1103,12 +1121,23 @@
     }
 
     public void setLightsOn(boolean on) {
+        Log.v(TAG, "lights " + (on ? "on" : "off"));
         if (!on) {
             // All we do for "lights out" mode on a phone is hide the status bar,
             // which the window manager does.  But we do need to hide the windowshade
             // on our own.
             animateCollapse();
         }
+        notifyLightsChanged(on);
+    }
+
+    private void notifyLightsChanged(boolean shown) {
+        try {
+            Slog.d(TAG, "lights " + (shown?"on":"out"));
+            mWindowManager.statusBarVisibilityChanged(
+                    shown ? View.STATUS_BAR_VISIBLE : View.STATUS_BAR_HIDDEN);
+        } catch (RemoteException ex) {
+        }
     }
 
     // Not supported
@@ -1519,6 +1548,12 @@
         }
     }
 
+    public void userActivity() {
+        try {
+            mBarService.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
+        } catch (RemoteException ex) { }
+    }
+
     /**
      * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
      * This was added last-minute and is inconsistent with the way the rest of the notifications
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 58c4d5a..f74fcc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -1565,6 +1565,9 @@
         return true;
     }
 
+    public void userActivity() {
+    }
+
     public class TouchOutsideListener implements View.OnTouchListener {
         private int mMsg;
         private StatusBarPanel mPanel;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 9ca153e..03afc82 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2880,6 +2880,12 @@
                 mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout);
             }
         }
+
+        if (mStatusBarService != null) {
+            try {
+                mStatusBarService.userActivity();
+            } catch (RemoteException ex) {}
+        }
     }
 
     Runnable mScreenLockTimeout = new Runnable() {
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index d97a9be..5bca7ee 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -2027,6 +2027,14 @@
             // Append additional motion samples.
             MotionSample* nextMotionSample = firstMotionSample->next;
             for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) {
+                if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) != 0 && scaleFactor != 1.0f) {
+                    for (size_t i = 0; i < motionEntry->pointerCount; i++) {
+                        scaledCoords[i] = nextMotionSample->pointerCoords[i];
+                        scaledCoords[i].scale(scaleFactor);
+                    }
+                } else {
+                    usingCoords = nextMotionSample->pointerCoords;
+                }
                 status = connection->inputPublisher.appendMotionSample(
                         nextMotionSample->eventTime, usingCoords);
                 if (status == NO_MEMORY) {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 6e76331..a334dbb 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -23,10 +23,14 @@
 import android.app.IApplicationThread;
 import android.app.IBackupAgent;
 import android.app.PendingIntent;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.FullBackup;
 import android.app.backup.RestoreSet;
 import android.app.backup.IBackupManager;
+import android.app.backup.IFullBackupRestoreObserver;
 import android.app.backup.IRestoreObserver;
 import android.app.backup.IRestoreSession;
+import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -60,6 +64,9 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.util.StringBuilderPrinter;
+
+import libcore.io.Libcore;
 
 import com.android.internal.backup.BackupConstants;
 import com.android.internal.backup.IBackupTransport;
@@ -81,10 +88,15 @@
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 class BackupManagerService extends IBackupManager.Stub {
     private static final String TAG = "BackupManagerService";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
+
+    // Name and current contents version of the full-backup manifest file
+    static final String BACKUP_MANIFEST_FILENAME = "_manifest";
+    static final int BACKUP_MANIFEST_VERSION = 1;
 
     // How often we perform a backup pass.  Privileged external callers can
     // trigger an immediate pass.
@@ -108,14 +120,20 @@
     private static final int MSG_RUN_GET_RESTORE_SETS = 6;
     private static final int MSG_TIMEOUT = 7;
     private static final int MSG_RESTORE_TIMEOUT = 8;
+    private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
+    private static final int MSG_RUN_FULL_RESTORE = 10;
 
     // Timeout interval for deciding that a bind or clear-data has taken too long
     static final long TIMEOUT_INTERVAL = 10 * 1000;
 
     // Timeout intervals for agent backup & restore operations
     static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
+    static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
     static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
 
+    // User confirmation timeout for a full backup/restore operation
+    static final long TIMEOUT_FULL_CONFIRMATION = 30 * 1000;
+
     private Context mContext;
     private PackageManager mPackageManager;
     IPackageManager mPackageManagerBinder;
@@ -138,15 +156,13 @@
     // set of backup services that have pending changes
     class BackupRequest {
         public ApplicationInfo appInfo;
-        public boolean fullBackup;
 
-        BackupRequest(ApplicationInfo app, boolean isFull) {
+        BackupRequest(ApplicationInfo app) {
             appInfo = app;
-            fullBackup = isFull;
         }
 
         public String toString() {
-            return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}";
+            return "BackupRequest{app=" + appInfo + "}";
         }
     }
     // Backups that we haven't started yet.  Keys are package names.
@@ -232,6 +248,38 @@
         }
     }
 
+    class FullParams {
+        public ParcelFileDescriptor fd;
+        public final AtomicBoolean latch;
+        public IFullBackupRestoreObserver observer;
+
+        FullParams() {
+            latch = new AtomicBoolean(false);
+        }
+    }
+
+    class FullBackupParams extends FullParams {
+        public boolean includeApks;
+        public boolean includeShared;
+        public boolean allApps;
+        public String[] packages;
+
+        FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveShared,
+                boolean doAllApps, String[] pkgList) {
+            fd = output;
+            includeApks = saveApks;
+            includeShared = saveShared;
+            allApps = doAllApps;
+            packages = pkgList;
+        }
+    }
+
+    class FullRestoreParams extends FullParams {
+        FullRestoreParams(ParcelFileDescriptor input) {
+            fd = input;
+        }
+    }
+
     // Bookkeeping of in-flight operations for timeout etc. purposes.  The operation
     // token is the index of the entry in the pending-operations list.
     static final int OP_PENDING = 0;
@@ -242,6 +290,8 @@
     final Object mCurrentOpLock = new Object();
     final Random mTokenGenerator = new Random();
 
+    final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
+
     // Where we keep our journal files and other bookkeeping
     File mBaseStateDir;
     File mDataDir;
@@ -264,6 +314,17 @@
     static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
     HashSet<String> mPendingInits = new HashSet<String>();  // transport names
 
+    // Utility: build a new random integer token
+    int generateToken() {
+        int token;
+        do {
+            synchronized (mTokenGenerator) {
+                token = mTokenGenerator.nextInt();
+            }
+        } while (token < 0);
+        return token;
+    }
+
     // ----- Asynchronous backup/restore handler thread -----
 
     private class BackupHandler extends Handler {
@@ -321,7 +382,13 @@
             }
 
             case MSG_RUN_FULL_BACKUP:
+            {
+                FullBackupParams params = (FullBackupParams)msg.obj;
+                (new PerformFullBackupTask(params.fd, params.observer, params.includeApks,
+                        params.includeShared, params.allApps, params.packages,
+                        params.latch)).run();
                 break;
+            }
 
             case MSG_RUN_RESTORE:
             {
@@ -416,6 +483,34 @@
                     }
                 }
             }
+
+            case MSG_FULL_CONFIRMATION_TIMEOUT:
+            {
+                synchronized (mFullConfirmations) {
+                    FullParams params = mFullConfirmations.get(msg.arg1);
+                    if (params != null) {
+                        Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
+
+                        // Release the waiter; timeout == completion
+                        signalFullBackupRestoreCompletion(params);
+
+                        // Remove the token from the set
+                        mFullConfirmations.delete(msg.arg1);
+
+                        // Report a timeout to the observer, if any
+                        if (params.observer != null) {
+                            try {
+                                params.observer.onTimeout();
+                            } catch (RemoteException e) {
+                                /* don't care if the app has gone away */
+                            }
+                        }
+                    } else {
+                        Slog.d(TAG, "couldn't find params for token " + msg.arg1);
+                    }
+                }
+                break;
+            }
             }
         }
     }
@@ -1253,9 +1348,11 @@
     void prepareOperationTimeout(int token, long interval) {
         if (DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
                 + " interval=" + interval);
-        mCurrentOperations.put(token, OP_PENDING);
-        Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
-        mBackupHandler.sendMessageDelayed(msg, interval);
+        synchronized (mCurrentOpLock) {
+            mCurrentOperations.put(token, OP_PENDING);
+            Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
+            mBackupHandler.sendMessageDelayed(msg, interval);
+        }
     }
 
     // ----- Back up a set of applications via a worker thread -----
@@ -1313,7 +1410,7 @@
                 if (status == BackupConstants.TRANSPORT_OK) {
                     PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
                             mPackageManager, allAgentPackages());
-                    BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
+                    BackupRequest pmRequest = new BackupRequest(new ApplicationInfo());
                     pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
                     status = processOneBackup(pmRequest,
                             IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
@@ -1407,12 +1504,10 @@
                 }
 
                 IBackupAgent agent = null;
-                int mode = (request.fullBackup)
-                        ? IApplicationThread.BACKUP_MODE_FULL
-                        : IApplicationThread.BACKUP_MODE_INCREMENTAL;
                 try {
                     mWakelock.setWorkSource(new WorkSource(request.appInfo.uid));
-                    agent = bindToAgentSynchronous(request.appInfo, mode);
+                    agent = bindToAgentSynchronous(request.appInfo,
+                            IApplicationThread.BACKUP_MODE_INCREMENTAL);
                     if (agent != null) {
                         int result = processOneBackup(request, agent, transport);
                         if (result != BackupConstants.TRANSPORT_OK) return result;
@@ -1446,7 +1541,7 @@
             ParcelFileDescriptor newState = null;
 
             PackageInfo packInfo;
-            int token = mTokenGenerator.nextInt();
+            final int token = generateToken();
             try {
                 // Look up the package info & signatures.  This is first so that if it
                 // throws an exception, there's no file setup yet that would need to
@@ -1461,12 +1556,11 @@
                 }
 
                 // In a full backup, we pass a null ParcelFileDescriptor as
-                // the saved-state "file"
-                if (!request.fullBackup) {
-                    savedState = ParcelFileDescriptor.open(savedStateName,
-                            ParcelFileDescriptor.MODE_READ_ONLY |
-                            ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
-                }
+                // the saved-state "file". This is by definition an incremental,
+                // so we build a saved state file to pass.
+                savedState = ParcelFileDescriptor.open(savedStateName,
+                        ParcelFileDescriptor.MODE_READ_ONLY |
+                        ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
 
                 backupData = ParcelFileDescriptor.open(backupDataName,
                         ParcelFileDescriptor.MODE_READ_WRITE |
@@ -1480,7 +1574,8 @@
 
                 // Initiate the target's backup pass
                 prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL);
-                agent.doBackup(savedState, backupData, newState, token, mBackupManagerBinder);
+                agent.doBackup(savedState, backupData, newState, false,
+                        token, mBackupManagerBinder);
                 boolean success = waitUntilOperationComplete(token);
 
                 if (!success) {
@@ -1552,6 +1647,224 @@
     }
 
 
+    // ----- Full backup to a file/socket -----
+
+    class PerformFullBackupTask implements Runnable {
+        ParcelFileDescriptor mOutputFile;
+        IFullBackupRestoreObserver mObserver;
+        boolean mIncludeApks;
+        boolean mIncludeShared;
+        boolean mAllApps;
+        String[] mPackages;
+        AtomicBoolean mLatchObject;
+        File mFilesDir;
+        File mManifestFile;
+
+        PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer, 
+                boolean includeApks, boolean includeShared,
+                boolean doAllApps, String[] packages, AtomicBoolean latch) {
+            mOutputFile = fd;
+            mObserver = observer;
+            mIncludeApks = includeApks;
+            mIncludeShared = includeShared;
+            mAllApps = doAllApps;
+            mPackages = packages;
+            mLatchObject = latch;
+
+            mFilesDir = new File("/data/system");
+            mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
+        }
+
+        @Override
+        public void run() {
+            final List<PackageInfo> packagesToBackup;
+
+            sendStartBackup();
+
+            // doAllApps supersedes the package set if any
+            if (mAllApps) {
+                packagesToBackup = mPackageManager.getInstalledPackages(
+                        PackageManager.GET_SIGNATURES);
+            } else {
+                packagesToBackup = new ArrayList<PackageInfo>();
+                for (String pkgName : mPackages) {
+                    try {
+                        packagesToBackup.add(mPackageManager.getPackageInfo(pkgName,
+                                PackageManager.GET_SIGNATURES));
+                    } catch (NameNotFoundException e) {
+                        Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
+                    }
+                }
+            }
+
+            // Now back up the app data via the agent mechanism
+            PackageInfo pkg = null;
+            try {
+                int N = packagesToBackup.size();
+                for (int i = 0; i < N; i++) {
+                    pkg = packagesToBackup.get(i);
+
+                    Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
+
+                    IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
+                            IApplicationThread.BACKUP_MODE_FULL);
+                    if (agent != null) {
+                        try {
+                            ApplicationInfo app = mPackageManager.getApplicationInfo(
+                                    pkg.packageName, 0);
+                            boolean sendApk = mIncludeApks
+                                    && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
+                                    && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
+                                        (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+
+                            sendOnBackupPackage(pkg.packageName);
+
+                            {
+                                BackupDataOutput output = new BackupDataOutput(
+                                        mOutputFile.getFileDescriptor());
+
+                                if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
+                                writeAppManifest(pkg, mManifestFile, sendApk);
+                                FullBackup.backupToTar(pkg.packageName, null, null,
+                                        mFilesDir.getAbsolutePath(),
+                                        mManifestFile.getAbsolutePath(),
+                                        output);
+                            }
+
+                            if (DEBUG) Slog.d(TAG, "Calling doBackup()");
+                            final int token = generateToken();
+                            prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
+                            agent.doBackup(null, mOutputFile, null, sendApk,
+                                    token, mBackupManagerBinder);
+                            boolean success = waitUntilOperationComplete(token);
+                            if (!success) {
+                                Slog.d(TAG, "Full backup failed on package " + pkg.packageName);
+                            } else {
+                                if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
+                            }
+                        } catch (NameNotFoundException e) {
+                            Slog.e(TAG, "Package exists but not app info; skipping: "
+                                    + pkg.packageName);
+                        } catch (IOException e) {
+                            Slog.e(TAG, "Error backing up " + pkg.packageName, e);
+                        }
+                    } else {
+                        Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
+                    }
+                    tearDown(pkg);
+                }
+            } catch (RemoteException e) {
+                Slog.e(TAG, "App died during full backup");
+            } finally {
+                if (pkg != null) {
+                    tearDown(pkg);
+                }
+                try {
+                    mOutputFile.close();
+                } catch (IOException e) {
+                    /* nothing we can do about this */
+                }
+                synchronized (mCurrentOpLock) {
+                    mCurrentOperations.clear();
+                }
+                synchronized (mLatchObject) {
+                    mLatchObject.set(true);
+                    mLatchObject.notifyAll();
+                }
+                sendEndBackup();
+                mWakelock.release();
+                if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
+            }
+        }
+
+        private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
+                throws IOException {
+            // Manifest format. All data are strings ending in LF:
+            //     BACKUP_MANIFEST_VERSION, currently 1
+            //
+            // Version 1:
+            //     package name
+            //     package's versionCode
+            //     boolean: "1" if archive includes .apk, "0" otherwise
+            //     number of signatures == N
+            // N*:    signature byte array in ascii format per Signature.toCharsString()
+            StringBuilder builder = new StringBuilder(4096);
+            StringBuilderPrinter printer = new StringBuilderPrinter(builder);
+
+            printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
+            printer.println(pkg.packageName);
+            printer.println(Integer.toString(pkg.versionCode));
+            printer.println(withApk ? "1" : "0");
+            if (pkg.signatures == null) {
+                printer.println("0");
+            } else {
+                printer.println(Integer.toString(pkg.signatures.length));
+                for (Signature sig : pkg.signatures) {
+                    printer.println(sig.toCharsString());
+                }
+            }
+
+            FileOutputStream outstream = new FileOutputStream(manifestFile);
+            Libcore.os.ftruncate(outstream.getFD(), 0);
+            outstream.write(builder.toString().getBytes());
+            outstream.close();
+        }
+
+        private void tearDown(PackageInfo pkg) {
+            final ApplicationInfo app = pkg.applicationInfo;
+            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
+                if (app.uid != Process.SYSTEM_UID) {
+                    if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
+                    mActivityManager.killApplicationProcess(app.processName, app.uid);
+                } else {
+                    if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
+                }
+            } catch (RemoteException e) {
+                Slog.d(TAG, "Lost app trying to shut down");
+            }
+        }
+
+        // wrappers for observer use
+        void sendStartBackup() {
+            if (mObserver != null) {
+                try {
+                    mObserver.onStartBackup();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "full backup observer went away: startBackup");
+                    mObserver = null;
+                }
+            }
+        }
+
+        void sendOnBackupPackage(String name) {
+            if (mObserver != null) {
+                try {
+                    // TODO: use a more user-friendly name string
+                    mObserver.onBackupPackage(name);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "full backup observer went away: backupPackage");
+                    mObserver = null;
+                }
+            }
+        }
+
+        void sendEndBackup() {
+            if (mObserver != null) {
+                try {
+                    mObserver.onEndBackup();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "full backup observer went away: endBackup");
+                    mObserver = null;
+                }
+            }
+        }
+    }
+
+
     // ----- Restore handling -----
 
     private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
@@ -1893,7 +2206,7 @@
             ParcelFileDescriptor backupData = null;
             ParcelFileDescriptor newState = null;
 
-            int token = mTokenGenerator.nextInt();
+            final int token = generateToken();
             try {
                 // Run the transport's restore pass
                 backupData = ParcelFileDescriptor.open(backupDataName,
@@ -1957,7 +2270,9 @@
                 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
                 try { if (newState != null) newState.close(); } catch (IOException e) {}
                 backupData = newState = null;
-                mCurrentOperations.delete(token);
+                synchronized (mCurrentOperations) {
+                    mCurrentOperations.delete(token);
+                }
 
                 // If we know a priori that we'll need to perform a full post-restore backup
                 // pass, clear the new state file data.  This means we're discarding work that
@@ -2092,7 +2407,7 @@
                 if (app.packageName.equals(packageName)) {
                     // Add the caller to the set of pending backups.  If there is
                     // one already there, then overwrite it, but no harm done.
-                    BackupRequest req = new BackupRequest(app, false);
+                    BackupRequest req = new BackupRequest(app);
                     if (mPendingBackups.put(app.packageName, req) == null) {
                         // Journal this request in case of crash.  The put()
                         // operation returned null when this package was not already
@@ -2239,10 +2554,141 @@
         }
     }
 
+    // Run a *full* backup pass for the given package, writing the resulting data stream
+    // to the supplied file descriptor.  This method is synchronous and does not return
+    // to the caller until the backup has been completed.
+    public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
+            boolean doAllApps, String[] pkgList) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
+
+        // Validate
+        if (!doAllApps) {
+            if (!includeShared) {
+                // If we're backing up shared data (sdcard or equivalent), then we can run
+                // without any supplied app names.  Otherwise, we'd be doing no work, so
+                // report the error.
+                if (pkgList == null || pkgList.length == 0) {
+                    throw new IllegalArgumentException(
+                            "Backup requested but neither shared nor any apps named");
+                }
+            }
+        }
+
+        if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
+                + " shared=" + includeShared + " all=" + doAllApps
+                + " pkgs=" + pkgList);
+
+        long oldId = Binder.clearCallingIdentity();
+        try {
+            FullBackupParams params = new FullBackupParams(fd, includeApks, includeShared,
+                    doAllApps, pkgList);
+            final int token = generateToken();
+            synchronized (mFullConfirmations) {
+                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);
+                mFullConfirmations.delete(token);
+                return;
+            }
+            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);
+
+            // 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
+            }
+        }
+    }
+
+    void waitForCompletion(FullParams params) {
+        synchronized (params.latch) {
+            while (params.latch.get() == false) {
+                try {
+                    params.latch.wait();
+                } catch (InterruptedException e) { /* never interrupted */ }
+            }
+        }
+    }
+
+    void signalFullBackupRestoreCompletion(FullParams params) {
+        synchronized (params.latch) {
+            params.latch.set(true);
+            params.latch.notifyAll();
+        }
+    }
+
+    // Confirm that the previously-requested full backup/restore operation can proceed.  This
+    // is used to require a user-facing disclosure about the operation.
+    public void acknowledgeFullBackupOrRestore(int token, boolean allow,
+            IFullBackupRestoreObserver observer) {
+        if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
+                + " allow=" + allow);
+
+        // TODO: possibly require not just this signature-only permission, but even
+        // require that the specific designated confirmation-UI app uid is the caller?
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
+
+        long oldId = Binder.clearCallingIdentity();
+        try {
+
+            FullParams params;
+            synchronized (mFullConfirmations) {
+                params = mFullConfirmations.get(token);
+                if (params != null) {
+                    mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
+                    mFullConfirmations.delete(token);
+
+                    if (allow) {
+                        params.observer = observer;
+                        final int verb = params instanceof FullBackupParams
+                        ? MSG_RUN_FULL_BACKUP
+                                : MSG_RUN_FULL_RESTORE;
+
+                        mWakelock.acquire();
+                        Message msg = mBackupHandler.obtainMessage(verb, params);
+                        mBackupHandler.sendMessage(msg);
+                    } else {
+                        Slog.w(TAG, "User rejected full backup/restore operation");
+                        // indicate completion without having actually transferred any data
+                        signalFullBackupRestoreCompletion(params);
+                    }
+                } else {
+                    Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
     // Enable/disable the backup service
     public void setBackupEnabled(boolean enable) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "setBackupEnabled");
+        "setBackupEnabled");
 
         Slog.i(TAG, "Backup enabled => " + enable);
 
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 7272e76..db831c7 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -41,6 +41,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
@@ -81,6 +82,10 @@
     private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
             "android.telephony.apn-restore";
 
+    // used in recursive route setting to add gateways for the host for which
+    // a host route was requested.
+    private static final int MAX_HOSTROUTE_CYCLE_COUNT = 10;
+
     private Tethering mTethering;
     private boolean mTetheringConfigValid = false;
 
@@ -421,7 +426,10 @@
             }
         }
 
-        mTethering = new Tethering(mContext, mHandler.getLooper());
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b);
+
+        mTethering = new Tethering(mContext, nmService, mHandler.getLooper());
         mTetheringConfigValid = (((mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] != null) ||
                                   !mTethering.isDunRequired()) &&
                                  (mTethering.getTetherableUsbRegexs().length != 0 ||
@@ -911,7 +919,7 @@
         }
         try {
             InetAddress addr = InetAddress.getByAddress(hostAddress);
-            return addHostRoute(tracker, addr);
+            return addHostRoute(tracker, addr, 0);
         } catch (UnknownHostException e) {}
         return false;
     }
@@ -924,24 +932,45 @@
      * TODO - deprecate
      * @return {@code true} on success, {@code false} on failure
      */
-    private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress) {
+    private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress, int cycleCount) {
         if (nt.getNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI) {
             return false;
         }
 
-        LinkProperties p = nt.getLinkProperties();
-        if (p == null) return false;
-        String interfaceName = p.getInterfaceName();
+        LinkProperties lp = nt.getLinkProperties();
+        if ((lp == null) || (hostAddress == null)) return false;
 
+        String interfaceName = lp.getInterfaceName();
         if (DBG) {
-            log("Requested host route to " + hostAddress + "(" + interfaceName + ")");
+            log("Requested host route to " + hostAddress + "(" + interfaceName + "), cycleCount=" +
+                    cycleCount);
         }
-        if (interfaceName != null) {
-            return NetworkUtils.addHostRoute(interfaceName, hostAddress, null);
-        } else {
+        if (interfaceName == null) {
             if (DBG) loge("addHostRoute failed due to null interface name");
             return false;
         }
+
+        RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), hostAddress);
+        InetAddress gateway = null;
+        if (bestRoute != null) {
+            gateway = bestRoute.getGateway();
+            // if the best route is ourself, don't relf-reference, just add the host route
+            if (hostAddress.equals(gateway)) gateway = null;
+        }
+        if (gateway != null) {
+            if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
+                loge("Error adding hostroute - too much recursion");
+                return false;
+            }
+            if (!addHostRoute(nt, gateway, cycleCount+1)) return false;
+        }
+        return NetworkUtils.addHostRoute(interfaceName, hostAddress, gateway);
+    }
+
+    // TODO support the removal of single host routes.  Keep a ref count of them so we
+    // aren't over-zealous
+    private boolean removeHostRoute(NetworkStateTracker nt, InetAddress hostAddress) {
+        return false;
     }
 
     /**
@@ -1393,7 +1422,7 @@
             Collection<InetAddress> dnsList = p.getDnses();
             for (InetAddress dns : dnsList) {
                 if (DBG) log("  adding " + dns);
-                NetworkUtils.addHostRoute(interfaceName, dns, null);
+                addHostRoute(nt, dns, 0);
             }
             nt.privateDnsRouteSet(true);
         }
@@ -1427,7 +1456,7 @@
             //TODO - handle non-default routes
             if (route.isDefaultRoute()) {
                 InetAddress gateway = route.getGateway();
-                if (NetworkUtils.addHostRoute(interfaceName, gateway, null) &&
+                if (addHostRoute(nt, gateway, 0) &&
                         NetworkUtils.addDefaultRoute(interfaceName, gateway)) {
                     if (DBG) {
                         NetworkInfo networkInfo = nt.getNetworkInfo();
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 8df8177..1d2072c 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -122,6 +122,11 @@
     // ================================================================================
     // From IStatusBarService
     // ================================================================================
+    public void userActivity() {
+        if (mBar != null) try {
+            mBar.userActivity();
+        } catch (RemoteException ex) {}
+    }
     public void expand() {
         enforceExpandStatusBar();
 
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index 80b0174..54555bb 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -20,6 +20,7 @@
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
 import android.app.backup.BackupAgentHelper;
+import android.app.backup.FullBackup;
 import android.app.backup.WallpaperBackupHelper;
 import android.content.Context;
 import android.os.ParcelFileDescriptor;
@@ -37,12 +38,19 @@
     private static final String TAG = "SystemBackupAgent";
 
     // These paths must match what the WallpaperManagerService uses
-    private static final String WALLPAPER_IMAGE = "/data/data/com.android.settings/files/wallpaper";
-    private static final String WALLPAPER_INFO = "/data/system/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_INFO_DIR = "/data/system";
+    private static final String WALLPAPER_INFO = WALLPAPER_INFO_DIR + "/wallpaper_info.xml";
 
     @Override
     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
             ParcelFileDescriptor newState) throws IOException {
+        if (oldState == null) {
+            runFullBackup(data);
+            return;
+        }
+
         // We only back up the data under the current "wallpaper" schema with metadata
         WallpaperManagerService wallpaper = (WallpaperManagerService)ServiceManager.getService(
                 Context.WALLPAPER_SERVICE);
@@ -57,6 +65,14 @@
         super.onBackup(oldState, data, newState);
     }
 
+    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,
+                WALLPAPER_INFO_DIR, WALLPAPER_INFO, output);
+    }
+
     @Override
     public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
             throws IOException {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index ed52dd3..586bb6b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -164,7 +164,7 @@
     static final boolean DEBUG_URI_PERMISSION = localLOGV || false;
     static final boolean DEBUG_USER_LEAVING = localLOGV || false;
     static final boolean DEBUG_RESULTS = localLOGV || false;
-    static final boolean DEBUG_BACKUP = localLOGV || false;
+    static final boolean DEBUG_BACKUP = localLOGV || true;
     static final boolean DEBUG_CONFIGURATION = localLOGV || false;
     static final boolean DEBUG_POWER = localLOGV || false;
     static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
@@ -3308,7 +3308,7 @@
         if (callerUid == Process.SYSTEM_UID) {
             synchronized (this) {
                 ProcessRecord app = getProcessRecordLocked(processName, uid);
-                if (app != null) {
+                if (app != null && app.thread != null) {
                     try {
                         app.thread.scheduleSuicide();
                     } catch (RemoteException e) {
@@ -10733,7 +10733,9 @@
             }
 
             BackupRecord r = new BackupRecord(ss, app, backupMode);
-            ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName);
+            ComponentName hostingName = (backupMode == IApplicationThread.BACKUP_MODE_INCREMENTAL)
+                    ? new ComponentName(app.packageName, app.backupAgentName)
+                    : new ComponentName("android", "FullBackupAgent");
             // startProcessLocked() returns existing proc's record if it's already running
             ProcessRecord proc = startProcessLocked(app.processName, app,
                     false, 0, "backup", hostingName, false);
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index b0400af..d5ac19e 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -73,7 +73,7 @@
  */
 final class ActivityStack {
     static final String TAG = ActivityManagerService.TAG;
-    static final boolean localLOGV = ActivityManagerService.localLOGV;
+    static final boolean localLOGV = ActivityManagerService.localLOGV || true;
     static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH;
     static final boolean DEBUG_PAUSE = ActivityManagerService.DEBUG_PAUSE;
     static final boolean DEBUG_VISBILITY = ActivityManagerService.DEBUG_VISBILITY;
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 9ff5233..316db2e 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -84,6 +84,7 @@
     private String[] mTetherableBluetoothRegexs;
     private String[] mUpstreamIfaceRegexs;
 
+    private INetworkManagementService mNMService;
     private Looper mLooper;
     private HandlerThread mThread;
 
@@ -100,21 +101,12 @@
     // with 255.255.255.0
 
     private String[] mDhcpRange;
-    private static final String DHCP_DEFAULT_RANGE1_START = "192.168.42.2";
-    private static final String DHCP_DEFAULT_RANGE1_STOP  = "192.168.42.254";
-    private static final String DHCP_DEFAULT_RANGE2_START = "192.168.43.2";
-    private static final String DHCP_DEFAULT_RANGE2_STOP  = "192.168.43.254";
-    private static final String DHCP_DEFAULT_RANGE3_START = "192.168.44.2";
-    private static final String DHCP_DEFAULT_RANGE3_STOP  = "192.168.44.254";
-    private static final String DHCP_DEFAULT_RANGE4_START = "192.168.45.2";
-    private static final String DHCP_DEFAULT_RANGE4_STOP  = "192.168.45.254";
-    private static final String DHCP_DEFAULT_RANGE5_START = "192.168.46.2";
-    private static final String DHCP_DEFAULT_RANGE5_STOP  = "192.168.46.254";
-    private static final String DHCP_DEFAULT_RANGE6_START = "192.168.47.2";
-    private static final String DHCP_DEFAULT_RANGE6_STOP  = "192.168.47.254";
-    private static final String DHCP_DEFAULT_RANGE7_START = "192.168.48.2";
-    private static final String DHCP_DEFAULT_RANGE7_STOP  = "192.168.48.254";
-
+    private static final String[] DHCP_DEFAULT_RANGE = {
+        "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
+        "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
+        "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
+        "192.168.48.2", "192.168.48.254",
+    };
 
     private String[] mDnsServers;
     private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
@@ -132,15 +124,14 @@
     private boolean mUsbMassStorageOff;  // track the status of USB Mass Storage
     private boolean mUsbConnected;       // track the status of USB connection
 
-    public Tethering(Context context, Looper looper) {
+    public Tethering(Context context, INetworkManagementService nmService, Looper looper) {
         mContext = context;
+        mNMService = nmService;
         mLooper = looper;
 
         // register for notifications from NetworkManagement Service
-        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
         try {
-            service.registerObserver(this);
+            mNMService.registerObserver(this);
         } catch (RemoteException e) {
             Log.e(TAG, "Error registering observer :" + e);
         }
@@ -173,21 +164,7 @@
         mDhcpRange = context.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_dhcp_range);
         if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) {
-            mDhcpRange = new String[14];
-            mDhcpRange[0] = DHCP_DEFAULT_RANGE1_START;
-            mDhcpRange[1] = DHCP_DEFAULT_RANGE1_STOP;
-            mDhcpRange[2] = DHCP_DEFAULT_RANGE2_START;
-            mDhcpRange[3] = DHCP_DEFAULT_RANGE2_STOP;
-            mDhcpRange[4] = DHCP_DEFAULT_RANGE3_START;
-            mDhcpRange[5] = DHCP_DEFAULT_RANGE3_STOP;
-            mDhcpRange[6] = DHCP_DEFAULT_RANGE4_START;
-            mDhcpRange[7] = DHCP_DEFAULT_RANGE4_STOP;
-            mDhcpRange[8] = DHCP_DEFAULT_RANGE5_START;
-            mDhcpRange[9] = DHCP_DEFAULT_RANGE5_STOP;
-            mDhcpRange[10] = DHCP_DEFAULT_RANGE6_START;
-            mDhcpRange[11] = DHCP_DEFAULT_RANGE6_STOP;
-            mDhcpRange[12] = DHCP_DEFAULT_RANGE7_START;
-            mDhcpRange[13] = DHCP_DEFAULT_RANGE7_STOP;
+            mDhcpRange = DHCP_DEFAULT_RANGE;
         }
         mDunRequired = false; // resample when we turn on
 
@@ -258,8 +235,6 @@
         return false;
     }
     public void interfaceAdded(String iface) {
-        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
         boolean found = false;
         boolean usb = false;
         if (isWifi(iface)) {
@@ -354,9 +329,9 @@
 
     private void sendTetherStateChangedBroadcast() {
         IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-        IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+        IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
         try {
-            if (!service.isTetheringSupported()) return;
+            if (!cm.isTetheringSupported()) return;
         } catch (RemoteException e) {
             return;
         }
@@ -503,11 +478,9 @@
 
     // used on cable insert/remove
     private void enableUsbIfaces(boolean enable) {
-        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
         String[] ifaces = new String[0];
         try {
-            ifaces = service.listInterfaces();
+            ifaces = mNMService.listInterfaces();
         } catch (Exception e) {
             Log.e(TAG, "Error listing Interfaces :" + e);
             return;
@@ -526,19 +499,17 @@
     // toggled when we enter/leave the fully tethered state
     private boolean enableUsbRndis(boolean enabled) {
         if (DEBUG) Log.d(TAG, "enableUsbRndis(" + enabled + ")");
-        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
 
         try {
             if (enabled) {
                 synchronized (this) {
-                    if (!service.isUsbRNDISStarted()) {
-                        service.startUsbRNDIS();
+                    if (!mNMService.isUsbRNDISStarted()) {
+                        mNMService.startUsbRNDIS();
                     }
                 }
             } else {
-                if (service.isUsbRNDISStarted()) {
-                    service.stopUsbRNDIS();
+                if (mNMService.isUsbRNDISStarted()) {
+                    mNMService.stopUsbRNDIS();
                 }
             }
         } catch (Exception e) {
@@ -552,13 +523,10 @@
     private boolean configureUsbIface(boolean enabled) {
         if (DEBUG) Log.d(TAG, "configureUsbIface(" + enabled + ")");
 
-        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-
         // bring toggle the interfaces
         String[] ifaces = new String[0];
         try {
-            ifaces = service.listInterfaces();
+            ifaces = mNMService.listInterfaces();
         } catch (Exception e) {
             Log.e(TAG, "Error listing Interfaces :" + e);
             return false;
@@ -567,7 +535,7 @@
             if (isUsb(iface)) {
                 InterfaceConfiguration ifcg = null;
                 try {
-                    ifcg = service.getInterfaceConfig(iface);
+                    ifcg = mNMService.getInterfaceConfig(iface);
                     if (ifcg != null) {
                         InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR);
                         ifcg.addr = new LinkAddress(addr, USB_PREFIX_LENGTH);
@@ -578,7 +546,7 @@
                         }
                         ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
                         ifcg.interfaceFlags = ifcg.interfaceFlags.replace("  "," ");
-                        service.setInterfaceConfig(iface, ifcg);
+                        mNMService.setInterfaceConfig(iface, ifcg);
                     }
                 } catch (Exception e) {
                     Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
@@ -874,11 +842,8 @@
         class TetheredState extends State {
             @Override
             public void enter() {
-                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service =
-                        INetworkManagementService.Stub.asInterface(b);
                 try {
-                    service.tetherInterface(mIfaceName);
+                    mNMService.tetherInterface(mIfaceName);
                 } catch (Exception e) {
                     setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
 
@@ -903,16 +868,13 @@
                 switch (message.what) {
                     case CMD_TETHER_UNREQUESTED:
                     case CMD_INTERFACE_DOWN:
-                        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                        INetworkManagementService service =
-                                INetworkManagementService.Stub.asInterface(b);
                         if (mMyUpstreamIfaceName != null) {
                             try {
-                                service.disableNat(mIfaceName, mMyUpstreamIfaceName);
+                                mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
                                 mMyUpstreamIfaceName = null;
                             } catch (Exception e) {
                                 try {
-                                    service.untetherInterface(mIfaceName);
+                                    mNMService.untetherInterface(mIfaceName);
                                 } catch (Exception ee) {}
 
                                 setLastErrorAndTransitionToInitialState(
@@ -921,7 +883,7 @@
                             }
                         }
                         try {
-                            service.untetherInterface(mIfaceName);
+                            mNMService.untetherInterface(mIfaceName);
                         } catch (Exception e) {
                             setLastErrorAndTransitionToInitialState(
                                     ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
@@ -944,8 +906,6 @@
                         break;
                     case CMD_TETHER_CONNECTION_CHANGED:
                         String newUpstreamIfaceName = (String)(message.obj);
-                        b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                        service = INetworkManagementService.Stub.asInterface(b);
                         if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
                                 (mMyUpstreamIfaceName != null &&
                                 mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
@@ -954,11 +914,11 @@
                         }
                         if (mMyUpstreamIfaceName != null) {
                             try {
-                                service.disableNat(mIfaceName, mMyUpstreamIfaceName);
+                                mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
                                 mMyUpstreamIfaceName = null;
                             } catch (Exception e) {
                                 try {
-                                    service.untetherInterface(mIfaceName);
+                                    mNMService.untetherInterface(mIfaceName);
                                 } catch (Exception ee) {}
 
                                 setLastErrorAndTransitionToInitialState(
@@ -968,10 +928,10 @@
                         }
                         if (newUpstreamIfaceName != null) {
                             try {
-                                service.enableNat(mIfaceName, newUpstreamIfaceName);
+                                mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
                             } catch (Exception e) {
                                 try {
-                                    service.untetherInterface(mIfaceName);
+                                    mNMService.untetherInterface(mIfaceName);
                                 } catch (Exception ee) {}
 
                                 setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR);
@@ -990,15 +950,13 @@
                         error = true;
                         // fall through
                     case CMD_TETHER_MODE_DEAD:
-                        b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                        service = INetworkManagementService.Stub.asInterface(b);
                         if (mMyUpstreamIfaceName != null) {
                             try {
-                                service.disableNat(mIfaceName, mMyUpstreamIfaceName);
+                                mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
                                 mMyUpstreamIfaceName = null;
                             } catch (Exception e) {
                                 try {
-                                    service.untetherInterface(mIfaceName);
+                                    mNMService.untetherInterface(mIfaceName);
                                 } catch (Exception ee) {}
 
                                 setLastErrorAndTransitionToInitialState(
@@ -1007,7 +965,7 @@
                             }
                         }
                         try {
-                            service.untetherInterface(mIfaceName);
+                            mNMService.untetherInterface(mIfaceName);
                         } catch (Exception e) {
                             setLastErrorAndTransitionToInitialState(
                                     ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
@@ -1138,10 +1096,10 @@
                 boolean retValue = true;
                 if (mMobileReserved) return retValue;
                 IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+                IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
                 int result = Phone.APN_REQUEST_FAILED;
                 try {
-                    result = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+                    result = cm.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
                             (mDunRequired ? Phone.FEATURE_ENABLE_DUN_ALWAYS :
                             Phone.FEATURE_ENABLE_HIPRI), new Binder());
                 } catch (Exception e) {
@@ -1165,10 +1123,9 @@
             protected boolean turnOffMobileConnection() {
                 if (mMobileReserved) {
                     IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                    IConnectivityManager service =
-                            IConnectivityManager.Stub.asInterface(b);
+                    IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
                     try {
-                        service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+                        cm.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
                                 (mDunRequired? Phone.FEATURE_ENABLE_DUN_ALWAYS :
                                              Phone.FEATURE_ENABLE_HIPRI));
                     } catch (Exception e) {
@@ -1179,28 +1136,25 @@
                 return true;
             }
             protected boolean turnOnMasterTetherSettings() {
-                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service =
-                        INetworkManagementService.Stub.asInterface(b);
                 try {
-                    service.setIpForwardingEnabled(true);
+                    mNMService.setIpForwardingEnabled(true);
                 } catch (Exception e) {
                     transitionTo(mSetIpForwardingEnabledErrorState);
                     return false;
                 }
                 try {
-                    service.startTethering(mDhcpRange);
+                    mNMService.startTethering(mDhcpRange);
                 } catch (Exception e) {
                     try {
-                        service.stopTethering();
-                        service.startTethering(mDhcpRange);
+                        mNMService.stopTethering();
+                        mNMService.startTethering(mDhcpRange);
                     } catch (Exception ee) {
                         transitionTo(mStartTetheringErrorState);
                         return false;
                     }
                 }
                 try {
-                    service.setDnsForwarders(mDnsServers);
+                    mNMService.setDnsForwarders(mDnsServers);
                 } catch (Exception e) {
                     transitionTo(mSetDnsForwardersErrorState);
                     return false;
@@ -1208,17 +1162,14 @@
                 return true;
             }
             protected boolean turnOffMasterTetherSettings() {
-                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service =
-                        INetworkManagementService.Stub.asInterface(b);
                 try {
-                    service.stopTethering();
+                    mNMService.stopTethering();
                 } catch (Exception e) {
                     transitionTo(mStopTetheringErrorState);
                     return false;
                 }
                 try {
-                    service.setIpForwardingEnabled(false);
+                    mNMService.setIpForwardingEnabled(false);
                 } catch (Exception e) {
                     transitionTo(mSetIpForwardingDisabledErrorState);
                     return false;
@@ -1241,12 +1192,9 @@
                     }
                 } catch (RemoteException e) { }
 
-                b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-
                 String[] ifaces = new String[0];
                 try {
-                    ifaces = service.listInterfaces();
+                    ifaces = mNMService.listInterfaces();
                 } catch (Exception e) {
                     Log.e(TAG, "Error listing Interfaces :" + e);
                     return null;
@@ -1258,7 +1206,7 @@
                             // verify it is active
                             InterfaceConfiguration ifcg = null;
                             try {
-                                ifcg = service.getInterfaceConfig(iface);
+                                ifcg = mNMService.getInterfaceConfig(iface);
                                 if (ifcg.isActive()) {
                                     return iface;
                                 }
@@ -1486,11 +1434,8 @@
             public void enter() {
                 Log.e(TAG, "Error in startTethering");
                 notify(TetherInterfaceSM.CMD_START_TETHERING_ERROR);
-                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service =
-                        INetworkManagementService.Stub.asInterface(b);
                 try {
-                    service.setIpForwardingEnabled(false);
+                    mNMService.setIpForwardingEnabled(false);
                 } catch (Exception e) {}
             }
         }
@@ -1500,11 +1445,8 @@
             public void enter() {
                 Log.e(TAG, "Error in stopTethering");
                 notify(TetherInterfaceSM.CMD_STOP_TETHERING_ERROR);
-                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service =
-                         INetworkManagementService.Stub.asInterface(b);
                 try {
-                    service.setIpForwardingEnabled(false);
+                    mNMService.setIpForwardingEnabled(false);
                 } catch (Exception e) {}
             }
         }
@@ -1514,14 +1456,11 @@
             public void enter() {
                 Log.e(TAG, "Error in setDnsForwarders");
                 notify(TetherInterfaceSM.CMD_SET_DNS_FORWARDERS_ERROR);
-                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service =
-                        INetworkManagementService.Stub.asInterface(b);
                 try {
-                    service.stopTethering();
+                    mNMService.stopTethering();
                 } catch (Exception e) {}
                 try {
-                    service.setIpForwardingEnabled(false);
+                    mNMService.setIpForwardingEnabled(false);
                 } catch (Exception e) {}
             }
         }
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 63ce0bd..b1ab05b 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -47,30 +47,25 @@
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.provider.Telephony.Sms.Intents;
+import android.telephony.SmsMessage;
 import android.telephony.TelephonyManager;
 import android.telephony.gsm.GsmCellLocation;
-import android.telephony.SmsMessage;
 import android.util.Log;
 import android.util.SparseIntArray;
 
 import com.android.internal.app.IBatteryStats;
-import com.android.internal.telephony.Phone;
 import com.android.internal.location.GpsNetInitiatedHandler;
 import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
-import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.SmsHeader;
-import com.android.internal.util.HexDump;
+import com.android.internal.telephony.Phone;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.StringBufferInputStream;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Date;
-import java.util.Properties;
 import java.util.Map.Entry;
+import java.util.Properties;
 import java.util.concurrent.CountDownLatch;
 
 /**
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 93d89a7..e75d96d 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -317,7 +317,8 @@
                     nextPos = pos + Math.min(limit, textLen - pos);
                 } else {
                     // For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
-                    nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit);
+                    nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit,
+                            ted.languageTable, ted.languageShiftTable);
                 }
             } else {  // Assume unicode.
                 nextPos = pos + Math.min(limit / 2, textLen - pos);
@@ -373,7 +374,8 @@
      */
 
     /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message
+     * Get an SMS-SUBMIT PDU for a destination address and a message.
+     * This method will not attempt to use any GSM national language 7 bit encodings.
      *
      * @param scAddress Service Centre address.  Null means use default.
      * @return a <code>SubmitPdu</code> containing the encoded SC
@@ -400,7 +402,8 @@
     }
 
     /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message
+     * Get an SMS-SUBMIT PDU for a destination address and a message.
+     * This method will not attempt to use any GSM national language 7 bit encodings.
      *
      * @param scAddress Service Centre address.  Null means use default.
      * @return a <code>SubmitPdu</code> containing the encoded SC
@@ -424,7 +427,8 @@
     }
 
     /**
-     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
+     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
+     * This method will not attempt to use any GSM national language 7 bit encodings.
      *
      * @param scAddress Service Centre address. null == use default
      * @param destinationAddress the address of the destination for the message
diff --git a/telephony/java/android/telephony/gsm/SmsMessage.java b/telephony/java/android/telephony/gsm/SmsMessage.java
index 6880175..4af99a6 100644
--- a/telephony/java/android/telephony/gsm/SmsMessage.java
+++ b/telephony/java/android/telephony/gsm/SmsMessage.java
@@ -297,37 +297,14 @@
      */
     @Deprecated
     public static int[] calculateLength(CharSequence messageBody, boolean use7bitOnly) {
+        SmsMessageBase.TextEncodingDetails ted =
+                com.android.internal.telephony.gsm.SmsMessage
+                        .calculateLength(messageBody, use7bitOnly);
         int ret[] = new int[4];
-
-        try {
-            // Try GSM alphabet
-            int septets = GsmAlphabet.countGsmSeptets(messageBody, !use7bitOnly);
-            ret[1] = septets;
-            if (septets > MAX_USER_DATA_SEPTETS) {
-                ret[0] = (septets + (MAX_USER_DATA_SEPTETS_WITH_HEADER - 1)) /
-                            MAX_USER_DATA_SEPTETS_WITH_HEADER;
-                ret[2] = (ret[0] * MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets;
-            } else {
-                ret[0] = 1;
-                ret[2] = MAX_USER_DATA_SEPTETS - septets;
-            }
-            ret[3] = ENCODING_7BIT;
-        } catch (EncodeException ex) {
-            // fall back to UCS-2
-            int octets = messageBody.length() * 2;
-            ret[1] = messageBody.length();
-            if (octets > MAX_USER_DATA_BYTES) {
-                // 6 is the size of the user data header
-                ret[0] = (octets + (MAX_USER_DATA_BYTES_WITH_HEADER - 1)) /
-                            MAX_USER_DATA_BYTES_WITH_HEADER;
-                ret[2] = ((ret[0] * MAX_USER_DATA_BYTES_WITH_HEADER) - octets) / 2;
-            } else {
-                ret[0] = 1;
-                ret[2] = (MAX_USER_DATA_BYTES - octets)/2;
-            }
-            ret[3] = ENCODING_16BIT;
-        }
-
+        ret[0] = ted.msgCount;
+        ret[1] = ted.codeUnitCount;
+        ret[2] = ted.codeUnitsRemaining;
+        ret[3] = ted.codeUnitSize;
         return ret;
     }
 
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index e42827f..c7c91e4 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import android.content.res.Resources;
 import android.text.TextUtils;
 import android.util.SparseIntArray;
 
@@ -23,6 +24,14 @@
 
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.telephony.SmsMessage.ENCODING_7BIT;
+import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
+import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER;
 
 /**
  * This class implements the character set mapping between
@@ -32,29 +41,51 @@
  * {@hide}
  */
 public class GsmAlphabet {
-    static final String LOG_TAG = "GSM";
+    private static final String TAG = "GSM";
 
-
+    private GsmAlphabet() { }
 
     //***** Constants
 
     /**
      * This escapes extended characters, and when present indicates that the
-     * following character should
-     * be looked up in the "extended" table
+     * following character should be looked up in the "extended" table.
      *
      * gsmToChar(GSM_EXTENDED_ESCAPE) returns 0xffff
      */
-
     public static final byte GSM_EXTENDED_ESCAPE = 0x1B;
 
+    /**
+     * User data header requires one octet for length. Count as one septet, because
+     * all combinations of header elements below will have at least one free bit
+     * when padding to the nearest septet boundary.
+     */
+    private static final int UDH_SEPTET_COST_LENGTH = 1;
 
     /**
-     * char to GSM alphabet char
-     * Returns ' ' in GSM alphabet if there's no possible match
-     * Returns GSM_EXTENDED_ESCAPE if this character is in the extended table
-     * In this case, you must call charToGsmExtended() for the value that
-     * should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string
+     * Using a non-default language locking shift table OR single shift table
+     * requires a user data header of 3 octets, or 4 septets, plus UDH length.
+     */
+    private static final int UDH_SEPTET_COST_ONE_SHIFT_TABLE = 4;
+
+    /**
+     * Using a non-default language locking shift table AND single shift table
+     * requires a user data header of 6 octets, or 7 septets, plus UDH length.
+     */
+    private static final int UDH_SEPTET_COST_TWO_SHIFT_TABLES = 7;
+
+    /**
+     * Multi-part messages require a user data header of 5 octets, or 6 septets,
+     * plus UDH length.
+     */
+    private static final int UDH_SEPTET_COST_CONCATENATED_MESSAGE = 6;
+
+    /**
+     * Converts a char to a GSM 7 bit table index.
+     * Returns ' ' in GSM alphabet if there's no possible match. Returns
+     * GSM_EXTENDED_ESCAPE if this character is in the extended table.
+     * In this case, you must call charToGsmExtended() for the value
+     * that should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string.
      */
     public static int
     charToGsm(char c) {
@@ -62,12 +93,12 @@
             return charToGsm(c, false);
         } catch (EncodeException ex) {
             // this should never happen
-            return sGsmSpaceChar;
+            return sCharsToGsmTables[0].get(' ', ' ');
         }
     }
 
     /**
-     * char to GSM alphabet char
+     * Converts a char to a GSM 7 bit table index.
      * @param throwException If true, throws EncodeException on invalid char.
      *   If false, returns GSM alphabet ' ' char.
      *
@@ -75,21 +106,20 @@
      * In this case, you must call charToGsmExtended() for the value that
      * should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string
      */
-
     public static int
     charToGsm(char c, boolean throwException) throws EncodeException {
         int ret;
 
-        ret = charToGsm.get(c, -1);
+        ret = sCharsToGsmTables[0].get(c, -1);
 
         if (ret == -1) {
-            ret = charToGsmExtended.get(c, -1);
+            ret = sCharsToShiftTables[0].get(c, -1);
 
             if (ret == -1) {
                 if (throwException) {
                     throw new EncodeException(c);
                 } else {
-                    return sGsmSpaceChar;
+                    return sCharsToGsmTables[0].get(' ', ' ');
                 }
             } else {
                 return GSM_EXTENDED_ESCAPE;
@@ -97,44 +127,42 @@
         }
 
         return ret;
-
     }
 
-
     /**
-     * char to extended GSM alphabet char
-     *
-     * Extended chars should be escaped with GSM_EXTENDED_ESCAPE
-     *
-     * Returns ' ' in GSM alphabet if there's no possible match
-     *
+     * Converts a char to an extended GSM 7 bit table index.
+     * Extended chars should be escaped with GSM_EXTENDED_ESCAPE.
+     * Returns ' ' in GSM alphabet if there's no possible match.
      */
     public static int
     charToGsmExtended(char c) {
         int ret;
 
-        ret = charToGsmExtended.get(c, -1);
+        ret = sCharsToShiftTables[0].get(c, -1);
 
         if (ret == -1) {
-            return sGsmSpaceChar;
+            return sCharsToGsmTables[0].get(' ', ' ');
         }
 
         return ret;
     }
 
     /**
-     * Converts a character in the GSM alphabet into a char
+     * Converts a character in the GSM alphabet into a char.
      *
-     * if GSM_EXTENDED_ESCAPE is passed, 0xffff is returned. In this case,
+     * If GSM_EXTENDED_ESCAPE is passed, 0xffff is returned. In this case,
      * the following character in the stream should be decoded with
-     * gsmExtendedToChar()
+     * gsmExtendedToChar().
      *
-     * If an unmappable value is passed (one greater than 127), ' ' is returned
+     * If an unmappable value is passed (one greater than 127), ' ' is returned.
      */
-
     public static char
     gsmToChar(int gsmChar) {
-        return (char)gsmToChar.get(gsmChar, ' ');
+        if (gsmChar >= 0 && gsmChar < 128) {
+            return sLanguageTables[0].charAt(gsmChar);
+        } else {
+            return ' ';
+        }
     }
 
     /**
@@ -144,20 +172,23 @@
      * extension page has yet been defined (see Note 1 in table 6.2.1.1 of
      * TS 23.038 v7.00)
      *
-     * If an unmappable value is passed , ' ' is returned
+     * If an unmappable value is passed, the character from the GSM 7 bit
+     * default table will be used (table 6.2.1.1 of TS 23.038).
      */
-
     public static char
     gsmExtendedToChar(int gsmChar) {
-        int ret;
-
-        ret = gsmExtendedToChar.get(gsmChar, -1);
-
-        if (ret == -1) {
+        if (gsmChar == GSM_EXTENDED_ESCAPE) {
             return ' ';
+        } else if (gsmChar >= 0 && gsmChar < 128) {
+            char c = sLanguageShiftTables[0].charAt(gsmChar);
+            if (c == ' ') {
+                return sLanguageTables[0].charAt(gsmChar);
+            } else {
+                return c;
+            }
+        } else {
+            return ' ';     // out of range
         }
-
-        return (char)ret;
     }
 
     /**
@@ -176,19 +207,24 @@
      * @param data The text string to encode.
      * @param header Optional header (including length byte) that precedes
      * the encoded data, padded to septet boundary.
+     * @param languageTable the 7 bit language table, or 0 for the default GSM alphabet
+     * @param languageShiftTable the 7 bit single shift language table, or 0 for the default
+     *     GSM extension table
      * @return Byte array containing header and encoded data.
+     * @throws EncodeException if String is too large to encode
      */
-    public static byte[] stringToGsm7BitPackedWithHeader(String data, byte[] header)
+    public static byte[] stringToGsm7BitPackedWithHeader(String data, byte[] header,
+            int languageTable, int languageShiftTable)
             throws EncodeException {
-
         if (header == null || header.length == 0) {
-            return stringToGsm7BitPacked(data);
+            return stringToGsm7BitPacked(data, languageTable, languageShiftTable);
         }
 
         int headerBits = (header.length + 1) * 8;
         int headerSeptets = (headerBits + 6) / 7;
 
-        byte[] ret = stringToGsm7BitPacked(data, headerSeptets, true);
+        byte[] ret = stringToGsm7BitPacked(data, headerSeptets, true, languageTable,
+                languageShiftTable);
 
         // Paste in the header
         ret[1] = (byte)header.length;
@@ -208,11 +244,16 @@
      * septets.
      *
      * @param data the data string to encode
+     * @param languageTable the 7 bit language table, or 0 for the default GSM alphabet
+     * @param languageShiftTable the 7 bit single shift language table, or 0 for the default
+     *     GSM extension table
+     * @return the encoded string
      * @throws EncodeException if String is too large to encode
      */
-    public static byte[] stringToGsm7BitPacked(String data)
+    public static byte[] stringToGsm7BitPacked(String data, int languageTable,
+            int languageShiftTable)
             throws EncodeException {
-        return stringToGsm7BitPacked(data, 0, true);
+        return stringToGsm7BitPacked(data, 0, true, languageTable, languageShiftTable);
     }
 
     /**
@@ -229,28 +270,48 @@
      *  the character data at the beginning of the array
      * @param throwException If true, throws EncodeException on invalid char.
      *   If false, replaces unencodable char with GSM alphabet space char.
+     * @param languageTable the 7 bit language table, or 0 for the default GSM alphabet
+     * @param languageShiftTable the 7 bit single shift language table, or 0 for the default
+     *     GSM extension table
+     * @return the encoded message
      *
      * @throws EncodeException if String is too large to encode
      */
     public static byte[] stringToGsm7BitPacked(String data, int startingSeptetOffset,
-            boolean throwException) throws EncodeException {
+            boolean throwException, int languageTable, int languageShiftTable)
+            throws EncodeException {
         int dataLen = data.length();
-        int septetCount = countGsmSeptets(data, throwException) + startingSeptetOffset;
+        int septetCount = countGsmSeptetsUsingTables(data, !throwException,
+                languageTable, languageShiftTable);
+        if (septetCount == -1) {
+            throw new EncodeException("countGsmSeptetsUsingTables(): unencodable char");
+        }
+        septetCount += startingSeptetOffset;
         if (septetCount > 255) {
             throw new EncodeException("Payload cannot exceed 255 septets");
         }
         int byteCount = ((septetCount * 7) + 7) / 8;
         byte[] ret = new byte[byteCount + 1];  // Include space for one byte length prefix.
+        SparseIntArray charToLanguageTable = sCharsToGsmTables[languageTable];
+        SparseIntArray charToShiftTable = sCharsToShiftTables[languageShiftTable];
         for (int i = 0, septets = startingSeptetOffset, bitOffset = startingSeptetOffset * 7;
                  i < dataLen && septets < septetCount;
                  i++, bitOffset += 7) {
             char c = data.charAt(i);
-            int v = GsmAlphabet.charToGsm(c, throwException);
-            if (v == GSM_EXTENDED_ESCAPE) {
-                v = GsmAlphabet.charToGsmExtended(c);  // Lookup the extended char.
-                packSmsChar(ret, bitOffset, GSM_EXTENDED_ESCAPE);
-                bitOffset += 7;
-                septets++;
+            int v = charToLanguageTable.get(c, -1);
+            if (v == -1) {
+                v = charToShiftTable.get(c, -1);  // Lookup the extended char.
+                if (v == -1) {
+                    if (throwException) {
+                        throw new EncodeException("stringToGsm7BitPacked(): unencodable char");
+                    } else {
+                        v = charToLanguageTable.get(' ', ' ');   // should return ASCII space
+                    }
+                } else {
+                    packSmsChar(ret, bitOffset, GSM_EXTENDED_ESCAPE);
+                    bitOffset += 7;
+                    septets++;
+                }
             }
             packSmsChar(ret, bitOffset, v);
             septets++;
@@ -262,8 +323,10 @@
     /**
      * Pack a 7-bit char into its appropriate place in a byte array
      *
+     * @param packedChars the destination byte array
      * @param bitOffset the bit offset that the septet should be packed at
      *                  (septet index * 7)
+     * @param value the 7-bit character to store
      */
     private static void
     packSmsChar(byte[] packedChars, int bitOffset, int value) {
@@ -290,7 +353,7 @@
      */
     public static String gsm7BitPackedToString(byte[] pdu, int offset,
             int lengthSeptets) {
-        return gsm7BitPackedToString(pdu, offset, lengthSeptets, 0);
+        return gsm7BitPackedToString(pdu, offset, lengthSeptets, 0, 0, 0);
     }
 
     /**
@@ -304,15 +367,37 @@
      * @param lengthSeptets string length in septets, not bytes
      * @param numPaddingBits the number of padding bits before the start of the
      *  string in the first byte
+     * @param languageTable the 7 bit language table, or 0 for the default GSM alphabet
+     * @param shiftTable the 7 bit single shift language table, or 0 for the default
+     *     GSM extension table
      * @return String representation or null on decoding exception
      */
     public static String gsm7BitPackedToString(byte[] pdu, int offset,
-            int lengthSeptets, int numPaddingBits) {
+            int lengthSeptets, int numPaddingBits, int languageTable, int shiftTable) {
         StringBuilder ret = new StringBuilder(lengthSeptets);
-        boolean prevCharWasEscape;
+
+        if (languageTable < 0 || languageTable > sLanguageTables.length) {
+            Log.w(TAG, "unknown language table " + languageTable + ", using default");
+            languageTable = 0;
+        }
+        if (shiftTable < 0 || shiftTable > sLanguageShiftTables.length) {
+            Log.w(TAG, "unknown single shift table " + shiftTable + ", using default");
+            shiftTable = 0;
+        }
 
         try {
-            prevCharWasEscape = false;
+            boolean prevCharWasEscape = false;
+            String languageTableToChar = sLanguageTables[languageTable];
+            String shiftTableToChar = sLanguageShiftTables[shiftTable];
+
+            if (languageTableToChar.isEmpty()) {
+                Log.w(TAG, "no language table for code " + languageTable + ", using default");
+                languageTableToChar = sLanguageTables[0];
+            }
+            if (shiftTableToChar.isEmpty()) {
+                Log.w(TAG, "no single shift table for code " + shiftTable + ", using default");
+                shiftTableToChar = sLanguageShiftTables[0];
+            }
 
             for (int i = 0 ; i < lengthSeptets ; i++) {
                 int bitOffset = (7 * i) + numPaddingBits;
@@ -332,16 +417,25 @@
                 }
 
                 if (prevCharWasEscape) {
-                    ret.append(GsmAlphabet.gsmExtendedToChar(gsmVal));
+                    if (gsmVal == GSM_EXTENDED_ESCAPE) {
+                        ret.append(' ');    // display ' ' for reserved double escape sequence
+                    } else {
+                        char c = shiftTableToChar.charAt(gsmVal);
+                        if (c == ' ') {
+                            ret.append(languageTableToChar.charAt(gsmVal));
+                        } else {
+                            ret.append(c);
+                        }
+                    }
                     prevCharWasEscape = false;
                 } else if (gsmVal == GSM_EXTENDED_ESCAPE) {
                     prevCharWasEscape = true;
                 } else {
-                    ret.append(GsmAlphabet.gsmToChar(gsmVal));
+                    ret.append(languageTableToChar.charAt(gsmVal));
                 }
             }
         } catch (RuntimeException ex) {
-            Log.e(LOG_TAG, "Error GSM 7 bit packed: ", ex);
+            Log.e(TAG, "Error GSM 7 bit packed: ", ex);
             return null;
         }
 
@@ -384,10 +478,13 @@
             charset = Charset.forName(characterset);
             mbcsBuffer = ByteBuffer.allocate(2);
         }
-        boolean prevWasEscape;
-        StringBuilder ret = new StringBuilder(length);
 
-        prevWasEscape = false;
+        // Always use GSM 7 bit default alphabet table for this method
+        String languageTableToChar = sLanguageTables[0];
+        String shiftTableToChar = sLanguageShiftTables[0];
+
+        StringBuilder ret = new StringBuilder(length);
+        boolean prevWasEscape = false;
         for (int i = offset ; i < offset + length ; i++) {
             // Never underestimate the pain that can be caused
             // by signed bytes
@@ -407,10 +504,16 @@
                 }
             } else {
                 if (prevWasEscape) {
-                    ret.append((char)gsmExtendedToChar.get(c, ' '));
+                    char shiftChar = shiftTableToChar.charAt(c);
+                    if (shiftChar == ' ') {
+                        // display character from main table if not present in shift table
+                        ret.append(languageTableToChar.charAt(c));
+                    } else {
+                        ret.append(shiftChar);
+                    }
                 } else {
                     if (!isMbcs || c < 0x80 || i + 1 >= offset + length) {
-                        ret.append((char)gsmToChar.get(c, ' '));
+                        ret.append(languageTableToChar.charAt(c));
                     } else {
                         // isMbcs must be true. So both mbcsBuffer and charset are initialized.
                         mbcsBuffer.clear();
@@ -427,16 +530,14 @@
     }
 
     /**
-     * Convert a string into an 8-bit unpacked GSM alphabet byte
-     * array
+     * Convert a string into an 8-bit unpacked GSM alphabet byte array.
+     * Always uses GSM default 7-bit alphabet and extension table.
      */
     public static byte[]
     stringToGsm8BitPacked(String s) {
         byte[] ret;
 
-        int septets = 0;
-
-        septets = countGsmSeptets(s);
+        int septets = countGsmSeptetsUsingTables(s, true, 0, 0);
 
         // Enough for all the septets and the length byte prefix
         ret = new byte[septets];
@@ -457,6 +558,8 @@
     public static void
     stringToGsm8BitUnpackedField(String s, byte dest[], int offset, int length) {
         int outByteIndex = offset;
+        SparseIntArray charToLanguageTable = sCharsToGsmTables[0];
+        SparseIntArray charToShiftTable = sCharsToShiftTables[0];
 
         // Septets are stored in byte-aligned octets
         for (int i = 0, sz = s.length()
@@ -465,17 +568,20 @@
         ) {
             char c = s.charAt(i);
 
-            int v = GsmAlphabet.charToGsm(c);
+            int v = charToLanguageTable.get(c, -1);
 
-            if (v == GSM_EXTENDED_ESCAPE) {
-                // make sure we can fit an escaped char
-                if (! (outByteIndex + 1 - offset < length)) {
-                    break;
+            if (v == -1) {
+                v = charToShiftTable.get(c, -1);
+                if (v == -1) {
+                    v = charToLanguageTable.get(' ', ' ');  // fall back to ASCII space
+                } else {
+                    // make sure we can fit an escaped char
+                    if (! (outByteIndex + 1 - offset < length)) {
+                        break;
+                    }
+
+                    dest[outByteIndex++] = GSM_EXTENDED_ESCAPE;
                 }
-
-                dest[outByteIndex++] = GSM_EXTENDED_ESCAPE;
-
-                v = GsmAlphabet.charToGsmExtended(c);
             }
 
             dest[outByteIndex++] = (byte)v;
@@ -503,17 +609,17 @@
 
     /**
      * Returns the count of 7-bit GSM alphabet characters
-     * needed to represent this character
+     * needed to represent this character using the default 7 bit GSM alphabet.
      * @param throwsException If true, throws EncodeException if unencodable
      * char. Otherwise, counts invalid char as 1 septet
      */
     public static int
     countGsmSeptets(char c, boolean throwsException) throws EncodeException {
-        if (charToGsm.get(c, -1) != -1) {
+        if (sCharsToGsmTables[0].get(c, -1) != -1) {
             return 1;
         }
 
-        if (charToGsmExtended.get(c, -1) != -1) {
+        if (sCharsToShiftTables[0].get(c, -1) != -1) {
             return 2;
         }
 
@@ -526,37 +632,196 @@
     }
 
     /**
-     * Returns the count of 7-bit GSM alphabet characters
-     * needed to represent this string. Counts unencodable char as 1 septet.
+     * Returns the count of 7-bit GSM alphabet characters needed
+     * to represent this string, using the specified 7-bit language table
+     * and extension table (0 for GSM default tables).
+     * @param s the Unicode string that will be encoded
+     * @param use7bitOnly allow using space in place of unencodable character if true,
+     *     otherwise, return -1 if any characters are unencodable
+     * @param languageTable the 7 bit language table, or 0 for the default GSM alphabet
+     * @param languageShiftTable the 7 bit single shift language table, or 0 for the default
+     *     GSM extension table
+     * @return the septet count for s using the specified language tables, or -1 if any
+     *     characters are unencodable and use7bitOnly is false
      */
-    public static int
-    countGsmSeptets(CharSequence s) {
-        try {
-            return countGsmSeptets(s, false);
-        } catch (EncodeException ex) {
-            // this should never happen
-            return 0;
+    public static int countGsmSeptetsUsingTables(CharSequence s, boolean use7bitOnly,
+            int languageTable, int languageShiftTable) {
+        int count = 0;
+        int sz = s.length();
+        SparseIntArray charToLanguageTable = sCharsToGsmTables[languageTable];
+        SparseIntArray charToShiftTable = sCharsToShiftTables[languageShiftTable];
+        for (int i = 0; i < sz; i++) {
+            char c = s.charAt(i);
+            if (c == GSM_EXTENDED_ESCAPE) {
+                Log.w(TAG, "countGsmSeptets() string contains Escape character, skipping.");
+                continue;
+            }
+            if (charToLanguageTable.get(c, -1) != -1) {
+                count++;
+            } else if (charToShiftTable.get(c, -1) != -1) {
+                count += 2; // escape + shift table index
+            } else if (use7bitOnly) {
+                count++;    // encode as space
+            } else {
+                return -1;  // caller must check for this case
+            }
         }
+        return count;
     }
 
     /**
      * Returns the count of 7-bit GSM alphabet characters
-     * needed to represent this string.
-     * @param throwsException If true, throws EncodeException if unencodable
-     * char. Otherwise, counts invalid char as 1 septet
+     * needed to represent this string, and the language table and
+     * language shift table used to achieve this result.
+     * For multi-part text messages, each message part may use its
+     * own language table encoding as specified in the message header
+     * for that message. However, this method will only return the
+     * optimal encoding for the message as a whole. When the individual
+     * pieces are encoded, a more optimal encoding may be chosen for each
+     * piece of the message, but the message will be split into pieces
+     * based on the encoding chosen for the message as a whole.
+     * @param s the Unicode string that will be encoded
+     * @param use7bitOnly allow using space in place of unencodable character if true,
+     *     using the language table pair with the fewest unencodable characters
+     * @return a TextEncodingDetails object containing the message and
+     *     character counts for the most efficient 7-bit encoding,
+     *     or null if there are no suitable language tables to encode the string.
      */
-    public static int
-    countGsmSeptets(CharSequence s, boolean throwsException) throws EncodeException {
-        int charIndex = 0;
-        int sz = s.length();
-        int count = 0;
-
-        while (charIndex < sz) {
-            count += countGsmSeptets(s.charAt(charIndex), throwsException);
-            charIndex++;
+    public static SmsMessageBase.TextEncodingDetails
+    countGsmSeptets(CharSequence s, boolean use7bitOnly) {
+        // fast path for common case where no national language shift tables are enabled
+        if (sEnabledSingleShiftTables.length + sEnabledLockingShiftTables.length == 0) {
+            SmsMessageBase.TextEncodingDetails ted = new SmsMessageBase.TextEncodingDetails();
+            int septets = GsmAlphabet.countGsmSeptetsUsingTables(s, use7bitOnly, 0, 0);
+            if (septets == -1) {
+                return null;
+            }
+            ted.codeUnitSize = ENCODING_7BIT;
+            ted.codeUnitCount = septets;
+            if (septets > MAX_USER_DATA_SEPTETS) {
+                ted.msgCount = (septets + (MAX_USER_DATA_SEPTETS_WITH_HEADER - 1)) /
+                        MAX_USER_DATA_SEPTETS_WITH_HEADER;
+                ted.codeUnitsRemaining = (ted.msgCount *
+                        MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets;
+            } else {
+                ted.msgCount = 1;
+                ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS - septets;
+            }
+            ted.codeUnitSize = ENCODING_7BIT;
+            return ted;
         }
 
-        return count;
+        int maxSingleShiftCode = sHighestEnabledSingleShiftCode;
+        List<LanguagePairCount> lpcList = new ArrayList<LanguagePairCount>(
+                sEnabledLockingShiftTables.length + 1);
+
+        // Always add default GSM 7-bit alphabet table
+        lpcList.add(new LanguagePairCount(0));
+        for (int i : sEnabledLockingShiftTables) {
+            // Avoid adding default table twice in case 0 is in the list of allowed tables
+            if (i != 0 && !sLanguageTables[i].isEmpty()) {
+                lpcList.add(new LanguagePairCount(i));
+            }
+        }
+
+        int sz = s.length();
+        // calculate septet count for each valid table / shift table pair
+        for (int i = 0; i < sz && !lpcList.isEmpty(); i++) {
+            char c = s.charAt(i);
+            if (c == GSM_EXTENDED_ESCAPE) {
+                Log.w(TAG, "countGsmSeptets() string contains Escape character, ignoring!");
+                continue;
+            }
+            // iterate through enabled locking shift tables
+            for (LanguagePairCount lpc : lpcList) {
+                int tableIndex = sCharsToGsmTables[lpc.languageCode].get(c, -1);
+                if (tableIndex == -1) {
+                    // iterate through single shift tables for this locking table
+                    for (int table = 0; table <= maxSingleShiftCode; table++) {
+                        if (lpc.septetCounts[table] != -1) {
+                            int shiftTableIndex = sCharsToShiftTables[table].get(c, -1);
+                            if (shiftTableIndex == -1) {
+                                if (use7bitOnly) {
+                                    // can't encode char, use space instead
+                                    lpc.septetCounts[table]++;
+                                    lpc.unencodableCounts[table]++;
+                                } else {
+                                    // can't encode char, remove language pair from list
+                                    lpc.septetCounts[table] = -1;
+                                }
+                            } else {
+                                // encode as Escape + index into shift table
+                                lpc.septetCounts[table] += 2;
+                            }
+                        }
+                    }
+                } else {
+                    // encode as index into locking shift table for all pairs
+                    for (int table = 0; table <= maxSingleShiftCode; table++) {
+                        if (lpc.septetCounts[table] != -1) {
+                            lpc.septetCounts[table]++;
+                        }
+                    }
+                }
+            }
+        }
+
+        // find the least cost encoding (lowest message count and most code units remaining)
+        SmsMessageBase.TextEncodingDetails ted = new SmsMessageBase.TextEncodingDetails();
+        ted.msgCount = Integer.MAX_VALUE;
+        ted.codeUnitSize = ENCODING_7BIT;
+        int minUnencodableCount = Integer.MAX_VALUE;
+        for (LanguagePairCount lpc : lpcList) {
+            for (int shiftTable = 0; shiftTable <= maxSingleShiftCode; shiftTable++) {
+                int septets = lpc.septetCounts[shiftTable];
+                if (septets == -1) {
+                    continue;
+                }
+                int udhLength;
+                if (lpc.languageCode != 0 && shiftTable != 0) {
+                    udhLength = UDH_SEPTET_COST_LENGTH + UDH_SEPTET_COST_TWO_SHIFT_TABLES;
+                } else if (lpc.languageCode != 0 || shiftTable != 0) {
+                    udhLength = UDH_SEPTET_COST_LENGTH + UDH_SEPTET_COST_ONE_SHIFT_TABLE;
+                } else {
+                    udhLength = 0;
+                }
+                int msgCount;
+                int septetsRemaining;
+                if (septets + udhLength > MAX_USER_DATA_SEPTETS) {
+                    if (udhLength == 0) {
+                        udhLength = UDH_SEPTET_COST_LENGTH;
+                    }
+                    udhLength += UDH_SEPTET_COST_CONCATENATED_MESSAGE;
+                    int septetsPerMessage = MAX_USER_DATA_SEPTETS - udhLength;
+                    msgCount = (septets + septetsPerMessage - 1) / septetsPerMessage;
+                    septetsRemaining = (msgCount * septetsPerMessage) - septets;
+                } else {
+                    msgCount = 1;
+                    septetsRemaining = MAX_USER_DATA_SEPTETS - udhLength - septets;
+                }
+                // for 7-bit only mode, use language pair with the least unencodable chars
+                int unencodableCount = lpc.unencodableCounts[shiftTable];
+                if (use7bitOnly && unencodableCount > minUnencodableCount) {
+                    continue;
+                }
+                if ((use7bitOnly && unencodableCount < minUnencodableCount)
+                        || msgCount < ted.msgCount || (msgCount == ted.msgCount
+                        && septetsRemaining > ted.codeUnitsRemaining)) {
+                    minUnencodableCount = unencodableCount;
+                    ted.msgCount = msgCount;
+                    ted.codeUnitCount = septets;
+                    ted.codeUnitsRemaining = septetsRemaining;
+                    ted.languageTable = lpc.languageCode;
+                    ted.languageShiftTable = shiftTable;
+                }
+            }
+        }
+
+        if (ted.msgCount == Integer.MAX_VALUE) {
+            return null;
+        }
+
+        return ted;
     }
 
     /**
@@ -569,16 +834,31 @@
      * @param start index of where to start counting septets
      * @param limit maximum septets to include,
      *   e.g. <code>MAX_USER_DATA_SEPTETS</code>
+     * @param langTable the 7 bit character table to use (0 for default GSM 7-bit alphabet)
+     * @param langShiftTable the 7 bit shift table to use (0 for default GSM extension table)
      * @return index of first character that won't fit, or the length
      *   of the entire string if everything fits
      */
     public static int
-    findGsmSeptetLimitIndex(String s, int start, int limit) {
+    findGsmSeptetLimitIndex(String s, int start, int limit, int langTable, int langShiftTable) {
         int accumulator = 0;
         int size = s.length();
 
+        SparseIntArray charToLangTable = sCharsToGsmTables[langTable];
+        SparseIntArray charToLangShiftTable = sCharsToShiftTables[langShiftTable];
         for (int i = start; i < size; i++) {
-            accumulator += countGsmSeptets(s.charAt(i));
+            int encodedSeptet = charToLangTable.get(s.charAt(i), -1);
+            if (encodedSeptet == -1) {
+                encodedSeptet = charToLangShiftTable.get(s.charAt(i), -1);
+                if (encodedSeptet == -1) {
+                    // char not found, assume we're replacing with space
+                    accumulator++;
+                } else {
+                    accumulator += 2;  // escape character + shift table index
+                }
+            } else {
+                accumulator++;
+            }
             if (accumulator > limit) {
                 return i;
             }
@@ -586,178 +866,488 @@
         return size;
     }
 
-    // Set in the static initializer
-    private static int sGsmSpaceChar;
+    /**
+     * Modify the array of enabled national language single shift tables for SMS
+     * encoding. This is used for unit testing, but could also be used to
+     * modify the enabled encodings based on the active MCC/MNC, for example.
+     *
+     * @param tables the new list of enabled single shift tables
+     */
+    static synchronized void setEnabledSingleShiftTables(int[] tables) {
+        sEnabledSingleShiftTables = tables;
 
-    private static final SparseIntArray charToGsm = new SparseIntArray();
-    private static final SparseIntArray gsmToChar = new SparseIntArray();
-    private static final SparseIntArray charToGsmExtended = new SparseIntArray();
-    private static final SparseIntArray gsmExtendedToChar = new SparseIntArray();
-
-    static {
-        int i = 0;
-
-        charToGsm.put('@', i++);
-        charToGsm.put('\u00a3', i++);
-        charToGsm.put('$', i++);
-        charToGsm.put('\u00a5', i++);
-        charToGsm.put('\u00e8', i++);
-        charToGsm.put('\u00e9', i++);
-        charToGsm.put('\u00f9', i++);
-        charToGsm.put('\u00ec', i++);
-        charToGsm.put('\u00f2', i++);
-        charToGsm.put('\u00c7', i++);
-        charToGsm.put('\n', i++);
-        charToGsm.put('\u00d8', i++);
-        charToGsm.put('\u00f8', i++);
-        charToGsm.put('\r', i++);
-        charToGsm.put('\u00c5', i++);
-        charToGsm.put('\u00e5', i++);
-
-        charToGsm.put('\u0394', i++);
-        charToGsm.put('_', i++);
-        charToGsm.put('\u03a6', i++);
-        charToGsm.put('\u0393', i++);
-        charToGsm.put('\u039b', i++);
-        charToGsm.put('\u03a9', i++);
-        charToGsm.put('\u03a0', i++);
-        charToGsm.put('\u03a8', i++);
-        charToGsm.put('\u03a3', i++);
-        charToGsm.put('\u0398', i++);
-        charToGsm.put('\u039e', i++);
-        charToGsm.put('\uffff', i++);
-        charToGsm.put('\u00c6', i++);
-        charToGsm.put('\u00e6', i++);
-        charToGsm.put('\u00df', i++);
-        charToGsm.put('\u00c9', i++);
-
-        charToGsm.put(' ', i++);
-        charToGsm.put('!', i++);
-        charToGsm.put('"', i++);
-        charToGsm.put('#', i++);
-        charToGsm.put('\u00a4', i++);
-        charToGsm.put('%', i++);
-        charToGsm.put('&', i++);
-        charToGsm.put('\'', i++);
-        charToGsm.put('(', i++);
-        charToGsm.put(')', i++);
-        charToGsm.put('*', i++);
-        charToGsm.put('+', i++);
-        charToGsm.put(',', i++);
-        charToGsm.put('-', i++);
-        charToGsm.put('.', i++);
-        charToGsm.put('/', i++);
-
-        charToGsm.put('0', i++);
-        charToGsm.put('1', i++);
-        charToGsm.put('2', i++);
-        charToGsm.put('3', i++);
-        charToGsm.put('4', i++);
-        charToGsm.put('5', i++);
-        charToGsm.put('6', i++);
-        charToGsm.put('7', i++);
-        charToGsm.put('8', i++);
-        charToGsm.put('9', i++);
-        charToGsm.put(':', i++);
-        charToGsm.put(';', i++);
-        charToGsm.put('<', i++);
-        charToGsm.put('=', i++);
-        charToGsm.put('>', i++);
-        charToGsm.put('?', i++);
-
-        charToGsm.put('\u00a1', i++);
-        charToGsm.put('A', i++);
-        charToGsm.put('B', i++);
-        charToGsm.put('C', i++);
-        charToGsm.put('D', i++);
-        charToGsm.put('E', i++);
-        charToGsm.put('F', i++);
-        charToGsm.put('G', i++);
-        charToGsm.put('H', i++);
-        charToGsm.put('I', i++);
-        charToGsm.put('J', i++);
-        charToGsm.put('K', i++);
-        charToGsm.put('L', i++);
-        charToGsm.put('M', i++);
-        charToGsm.put('N', i++);
-        charToGsm.put('O', i++);
-
-        charToGsm.put('P', i++);
-        charToGsm.put('Q', i++);
-        charToGsm.put('R', i++);
-        charToGsm.put('S', i++);
-        charToGsm.put('T', i++);
-        charToGsm.put('U', i++);
-        charToGsm.put('V', i++);
-        charToGsm.put('W', i++);
-        charToGsm.put('X', i++);
-        charToGsm.put('Y', i++);
-        charToGsm.put('Z', i++);
-        charToGsm.put('\u00c4', i++);
-        charToGsm.put('\u00d6', i++);
-        charToGsm.put('\u00d1', i++);
-        charToGsm.put('\u00dc', i++);
-        charToGsm.put('\u00a7', i++);
-
-        charToGsm.put('\u00bf', i++);
-        charToGsm.put('a', i++);
-        charToGsm.put('b', i++);
-        charToGsm.put('c', i++);
-        charToGsm.put('d', i++);
-        charToGsm.put('e', i++);
-        charToGsm.put('f', i++);
-        charToGsm.put('g', i++);
-        charToGsm.put('h', i++);
-        charToGsm.put('i', i++);
-        charToGsm.put('j', i++);
-        charToGsm.put('k', i++);
-        charToGsm.put('l', i++);
-        charToGsm.put('m', i++);
-        charToGsm.put('n', i++);
-        charToGsm.put('o', i++);
-
-        charToGsm.put('p', i++);
-        charToGsm.put('q', i++);
-        charToGsm.put('r', i++);
-        charToGsm.put('s', i++);
-        charToGsm.put('t', i++);
-        charToGsm.put('u', i++);
-        charToGsm.put('v', i++);
-        charToGsm.put('w', i++);
-        charToGsm.put('x', i++);
-        charToGsm.put('y', i++);
-        charToGsm.put('z', i++);
-        charToGsm.put('\u00e4', i++);
-        charToGsm.put('\u00f6', i++);
-        charToGsm.put('\u00f1', i++);
-        charToGsm.put('\u00fc', i++);
-        charToGsm.put('\u00e0', i++);
-
-
-        charToGsmExtended.put('\f', 10);
-        charToGsmExtended.put('^', 20);
-        charToGsmExtended.put('{', 40);
-        charToGsmExtended.put('}', 41);
-        charToGsmExtended.put('\\', 47);
-        charToGsmExtended.put('[', 60);
-        charToGsmExtended.put('~', 61);
-        charToGsmExtended.put(']', 62);
-        charToGsmExtended.put('|', 64);
-        charToGsmExtended.put('\u20ac', 101);
-
-        int size = charToGsm.size();
-        for (int j=0; j<size; j++) {
-            gsmToChar.put(charToGsm.valueAt(j), charToGsm.keyAt(j));
+        if (tables.length > 0) {
+            sHighestEnabledSingleShiftCode = tables[tables.length - 1];
+        } else {
+            sHighestEnabledSingleShiftCode = 0;
         }
-
-        size = charToGsmExtended.size();
-        for (int j=0; j<size; j++) {
-            gsmExtendedToChar.put(charToGsmExtended.valueAt(j), charToGsmExtended.keyAt(j));
-        }
-
-
-        sGsmSpaceChar = charToGsm.get(' ');
     }
 
+    /**
+     * Modify the array of enabled national language locking shift tables for SMS
+     * encoding. This is used for unit testing, but could also be used to
+     * modify the enabled encodings based on the active MCC/MNC, for example.
+     *
+     * @param tables the new list of enabled locking shift tables
+     */
+    static synchronized void setEnabledLockingShiftTables(int[] tables) {
+        sEnabledLockingShiftTables = tables;
+    }
 
+    /**
+     * Return the array of enabled national language single shift tables for SMS
+     * encoding. This is used for unit testing. The returned array is not a copy, so
+     * the caller should be careful not to modify it.
+     *
+     * @return the list of enabled single shift tables
+     */
+    static synchronized int[] getEnabledSingleShiftTables() {
+        return sEnabledSingleShiftTables;
+    }
+
+    /**
+     * Return the array of enabled national language locking shift tables for SMS
+     * encoding. This is used for unit testing. The returned array is not a copy, so
+     * the caller should be careful not to modify it.
+     *
+     * @return the list of enabled locking shift tables
+     */
+    static synchronized int[] getEnabledLockingShiftTables() {
+        return sEnabledLockingShiftTables;
+    }
+
+    /** Reverse mapping from Unicode characters to indexes into language tables. */
+    private static final SparseIntArray[] sCharsToGsmTables;
+
+    /** Reverse mapping from Unicode characters to indexes into language shift tables. */
+    private static final SparseIntArray[] sCharsToShiftTables;
+
+    /** OEM configured list of enabled national language single shift tables for encoding. */
+    private static int[] sEnabledSingleShiftTables;
+
+    /** OEM configured list of enabled national language locking shift tables for encoding. */
+    private static int[] sEnabledLockingShiftTables;
+
+    /** Highest language code to include in array of single shift counters. */
+    private static int sHighestEnabledSingleShiftCode;
+
+    /**
+     * Septet counter for a specific locking shift table and all of
+     * the single shift tables that it can be paired with.
+     */
+    private static class LanguagePairCount {
+        final int languageCode;
+        final int[] septetCounts;
+        final int[] unencodableCounts;
+        LanguagePairCount(int code) {
+            this.languageCode = code;
+            int maxSingleShiftCode = sHighestEnabledSingleShiftCode;
+            septetCounts = new int[maxSingleShiftCode + 1];
+            unencodableCounts = new int[maxSingleShiftCode + 1];
+            // set counters for disabled single shift tables to -1
+            // (GSM default extension table index 0 is always enabled)
+            for (int i = 1, tableOffset = 0; i <= maxSingleShiftCode; i++) {
+                if (sEnabledSingleShiftTables[tableOffset] == i) {
+                    tableOffset++;
+                } else {
+                    septetCounts[i] = -1;   // disabled
+                }
+            }
+            // exclude Turkish locking + Turkish single shift table and
+            // Portuguese locking + Spanish single shift table (these
+            // combinations will never be optimal for any input).
+            if (code == 1 && maxSingleShiftCode >= 1) {
+                septetCounts[1] = -1;   // Turkish + Turkish
+            } else if (code == 3 && maxSingleShiftCode >= 2) {
+                septetCounts[2] = -1;   // Portuguese + Spanish
+            }
+        }
+    }
+
+    /**
+     * GSM default 7 bit alphabet plus national language locking shift character tables.
+     * Comment lines above strings indicate the lower four bits of the table position.
+     */
+    private static final String[] sLanguageTables = {
+        /* 3GPP TS 23.038 V9.1.1 section 6.2.1 - GSM 7 bit Default Alphabet
+         01.....23.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....0.....1 */
+        "@\u00a3$\u00a5\u00e8\u00e9\u00f9\u00ec\u00f2\u00c7\n\u00d8\u00f8\r\u00c5\u00e5\u0394_"
+            // 2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....
+            + "\u03a6\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u039e\uffff\u00c6\u00e6\u00df"
+            // F.....012.34.....56789ABCDEF0123456789ABCDEF0.....123456789ABCDEF0123456789A
+            + "\u00c9 !\"#\u00a4%&'()*+,-./0123456789:;<=>?\u00a1ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+            // B.....C.....D.....E.....F.....0.....123456789ABCDEF0123456789AB.....C.....D.....
+            + "\u00c4\u00d6\u00d1\u00dc\u00a7\u00bfabcdefghijklmnopqrstuvwxyz\u00e4\u00f6\u00f1"
+            // E.....F.....
+            + "\u00fc\u00e0",
+
+        /* A.3.1 Turkish National Language Locking Shift Table
+         01.....23.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....0.....1 */
+        "@\u00a3$\u00a5\u20ac\u00e9\u00f9\u0131\u00f2\u00c7\n\u011e\u011f\r\u00c5\u00e5\u0394_"
+            // 2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....
+            + "\u03a6\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u039e\uffff\u015e\u015f\u00df"
+            // F.....012.34.....56789ABCDEF0123456789ABCDEF0.....123456789ABCDEF0123456789A
+            + "\u00c9 !\"#\u00a4%&'()*+,-./0123456789:;<=>?\u0130ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+            // B.....C.....D.....E.....F.....0.....123456789ABCDEF0123456789AB.....C.....D.....
+            + "\u00c4\u00d6\u00d1\u00dc\u00a7\u00e7abcdefghijklmnopqrstuvwxyz\u00e4\u00f6\u00f1"
+            // E.....F.....
+            + "\u00fc\u00e0",
+
+        /* A.3.2 Void (no locking shift table for Spanish) */
+        "",
+
+        /* A.3.3 Portuguese National Language Locking Shift Table
+         01.....23.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....0.....1 */
+        "@\u00a3$\u00a5\u00ea\u00e9\u00fa\u00ed\u00f3\u00e7\n\u00d4\u00f4\r\u00c1\u00e1\u0394_"
+            // 2.....3.....4.....5.....67.8.....9.....AB.....C.....D.....E.....F.....012.34.....
+            + "\u00aa\u00c7\u00c0\u221e^\\\u20ac\u00d3|\uffff\u00c2\u00e2\u00ca\u00c9 !\"#\u00ba"
+            // 56789ABCDEF0123456789ABCDEF0.....123456789ABCDEF0123456789AB.....C.....D.....E.....
+            + "%&'()*+,-./0123456789:;<=>?\u00cdABCDEFGHIJKLMNOPQRSTUVWXYZ\u00c3\u00d5\u00da\u00dc"
+            // F.....0123456789ABCDEF0123456789AB.....C.....DE.....F.....
+            + "\u00a7~abcdefghijklmnopqrstuvwxyz\u00e3\u00f5`\u00fc\u00e0",
+
+        /* A.3.4 Bengali National Language Locking Shift Table
+         0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.EF.....0..... */
+        "\u0981\u0982\u0983\u0985\u0986\u0987\u0988\u0989\u098a\u098b\n\u098c \r \u098f\u0990"
+            // 123.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+            + "  \u0993\u0994\u0995\u0996\u0997\u0998\u0999\u099a\uffff\u099b\u099c\u099d\u099e"
+            // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....0123456789ABC
+            + " !\u099f\u09a0\u09a1\u09a2\u09a3\u09a4)(\u09a5\u09a6,\u09a7.\u09a80123456789:; "
+            // D.....E.....F0.....1.....2.....3.....4.....56.....789A.....B.....C.....D.....
+            + "\u09aa\u09ab?\u09ac\u09ad\u09ae\u09af\u09b0 \u09b2   \u09b6\u09b7\u09b8\u09b9"
+            // E.....F.....0.....1.....2.....3.....4.....5.....6.....789.....A.....BCD.....E.....
+            + "\u09bc\u09bd\u09be\u09bf\u09c0\u09c1\u09c2\u09c3\u09c4  \u09c7\u09c8  \u09cb\u09cc"
+            // F.....0.....123456789ABCDEF0123456789AB.....C.....D.....E.....F.....
+            + "\u09cd\u09ceabcdefghijklmnopqrstuvwxyz\u09d7\u09dc\u09dd\u09f0\u09f1",
+
+        /* A.3.5 Gujarati National Language Locking Shift Table
+         0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.EF.....0.....*/
+        "\u0a81\u0a82\u0a83\u0a85\u0a86\u0a87\u0a88\u0a89\u0a8a\u0a8b\n\u0a8c\u0a8d\r \u0a8f\u0a90"
+            // 1.....23.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....
+            + "\u0a91 \u0a93\u0a94\u0a95\u0a96\u0a97\u0a98\u0a99\u0a9a\uffff\u0a9b\u0a9c\u0a9d"
+            // F.....012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....0123456789AB
+            + "\u0a9e !\u0a9f\u0aa0\u0aa1\u0aa2\u0aa3\u0aa4)(\u0aa5\u0aa6,\u0aa7.\u0aa80123456789:;"
+            // CD.....E.....F0.....1.....2.....3.....4.....56.....7.....89.....A.....B.....C.....
+            + " \u0aaa\u0aab?\u0aac\u0aad\u0aae\u0aaf\u0ab0 \u0ab2\u0ab3 \u0ab5\u0ab6\u0ab7\u0ab8"
+            // D.....E.....F.....0.....1.....2.....3.....4.....5.....6.....7.....89.....A.....
+            + "\u0ab9\u0abc\u0abd\u0abe\u0abf\u0ac0\u0ac1\u0ac2\u0ac3\u0ac4\u0ac5 \u0ac7\u0ac8"
+            // B.....CD.....E.....F.....0.....123456789ABCDEF0123456789AB.....C.....D.....E.....
+            + "\u0ac9 \u0acb\u0acc\u0acd\u0ad0abcdefghijklmnopqrstuvwxyz\u0ae0\u0ae1\u0ae2\u0ae3"
+            // F.....
+            + "\u0af1",
+
+        /* A.3.6 Hindi National Language Locking Shift Table
+         0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....*/
+        "\u0901\u0902\u0903\u0905\u0906\u0907\u0908\u0909\u090a\u090b\n\u090c\u090d\r\u090e\u090f"
+            // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....
+            + "\u0910\u0911\u0912\u0913\u0914\u0915\u0916\u0917\u0918\u0919\u091a\uffff\u091b\u091c"
+            // E.....F.....012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....012345
+            + "\u091d\u091e !\u091f\u0920\u0921\u0922\u0923\u0924)(\u0925\u0926,\u0927.\u0928012345"
+            // 6789ABC.....D.....E.....F0.....1.....2.....3.....4.....5.....6.....7.....8.....
+            + "6789:;\u0929\u092a\u092b?\u092c\u092d\u092e\u092f\u0930\u0931\u0932\u0933\u0934"
+            // 9.....A.....B.....C.....D.....E.....F.....0.....1.....2.....3.....4.....5.....6.....
+            + "\u0935\u0936\u0937\u0938\u0939\u093c\u093d\u093e\u093f\u0940\u0941\u0942\u0943\u0944"
+            // 7.....8.....9.....A.....B.....C.....D.....E.....F.....0.....123456789ABCDEF012345678
+            + "\u0945\u0946\u0947\u0948\u0949\u094a\u094b\u094c\u094d\u0950abcdefghijklmnopqrstuvwx"
+            // 9AB.....C.....D.....E.....F.....
+            + "yz\u0972\u097b\u097c\u097e\u097f",
+
+        /* A.3.7 Kannada National Language Locking Shift Table
+           NOTE: TS 23.038 V9.1.1 shows code 0x24 as \u0caa, corrected to \u0ca1 (typo)
+         01.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.E.....F.....0.....1 */
+        " \u0c82\u0c83\u0c85\u0c86\u0c87\u0c88\u0c89\u0c8a\u0c8b\n\u0c8c \r\u0c8e\u0c8f\u0c90 "
+            // 2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+            + "\u0c92\u0c93\u0c94\u0c95\u0c96\u0c97\u0c98\u0c99\u0c9a\uffff\u0c9b\u0c9c\u0c9d\u0c9e"
+            // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....0123456789ABC
+            + " !\u0c9f\u0ca0\u0ca1\u0ca2\u0ca3\u0ca4)(\u0ca5\u0ca6,\u0ca7.\u0ca80123456789:; "
+            // D.....E.....F0.....1.....2.....3.....4.....5.....6.....7.....89.....A.....B.....
+            + "\u0caa\u0cab?\u0cac\u0cad\u0cae\u0caf\u0cb0\u0cb1\u0cb2\u0cb3 \u0cb5\u0cb6\u0cb7"
+            // C.....D.....E.....F.....0.....1.....2.....3.....4.....5.....6.....78.....9.....
+            + "\u0cb8\u0cb9\u0cbc\u0cbd\u0cbe\u0cbf\u0cc0\u0cc1\u0cc2\u0cc3\u0cc4 \u0cc6\u0cc7"
+            // A.....BC.....D.....E.....F.....0.....123456789ABCDEF0123456789AB.....C.....D.....
+            + "\u0cc8 \u0cca\u0ccb\u0ccc\u0ccd\u0cd5abcdefghijklmnopqrstuvwxyz\u0cd6\u0ce0\u0ce1"
+            // E.....F.....
+            + "\u0ce2\u0ce3",
+
+        /* A.3.8 Malayalam National Language Locking Shift Table
+         01.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.E.....F.....0.....1 */
+        " \u0d02\u0d03\u0d05\u0d06\u0d07\u0d08\u0d09\u0d0a\u0d0b\n\u0d0c \r\u0d0e\u0d0f\u0d10 "
+            // 2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+            + "\u0d12\u0d13\u0d14\u0d15\u0d16\u0d17\u0d18\u0d19\u0d1a\uffff\u0d1b\u0d1c\u0d1d\u0d1e"
+            // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....0123456789ABC
+            + " !\u0d1f\u0d20\u0d21\u0d22\u0d23\u0d24)(\u0d25\u0d26,\u0d27.\u0d280123456789:; "
+            // D.....E.....F0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....
+            + "\u0d2a\u0d2b?\u0d2c\u0d2d\u0d2e\u0d2f\u0d30\u0d31\u0d32\u0d33\u0d34\u0d35\u0d36"
+            // B.....C.....D.....EF.....0.....1.....2.....3.....4.....5.....6.....78.....9.....
+            + "\u0d37\u0d38\u0d39 \u0d3d\u0d3e\u0d3f\u0d40\u0d41\u0d42\u0d43\u0d44 \u0d46\u0d47"
+            // A.....BC.....D.....E.....F.....0.....123456789ABCDEF0123456789AB.....C.....D.....
+            + "\u0d48 \u0d4a\u0d4b\u0d4c\u0d4d\u0d57abcdefghijklmnopqrstuvwxyz\u0d60\u0d61\u0d62"
+            // E.....F.....
+            + "\u0d63\u0d79",
+
+        /* A.3.9 Oriya National Language Locking Shift Table
+         0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.EF.....0.....12 */
+        "\u0b01\u0b02\u0b03\u0b05\u0b06\u0b07\u0b08\u0b09\u0b0a\u0b0b\n\u0b0c \r \u0b0f\u0b10  "
+            // 3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....01
+            + "\u0b13\u0b14\u0b15\u0b16\u0b17\u0b18\u0b19\u0b1a\uffff\u0b1b\u0b1c\u0b1d\u0b1e !"
+            // 2.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....0123456789ABCD.....
+            + "\u0b1f\u0b20\u0b21\u0b22\u0b23\u0b24)(\u0b25\u0b26,\u0b27.\u0b280123456789:; \u0b2a"
+            // E.....F0.....1.....2.....3.....4.....56.....7.....89.....A.....B.....C.....D.....
+            + "\u0b2b?\u0b2c\u0b2d\u0b2e\u0b2f\u0b30 \u0b32\u0b33 \u0b35\u0b36\u0b37\u0b38\u0b39"
+            // E.....F.....0.....1.....2.....3.....4.....5.....6.....789.....A.....BCD.....E.....
+            + "\u0b3c\u0b3d\u0b3e\u0b3f\u0b40\u0b41\u0b42\u0b43\u0b44  \u0b47\u0b48  \u0b4b\u0b4c"
+            // F.....0.....123456789ABCDEF0123456789AB.....C.....D.....E.....F.....
+            + "\u0b4d\u0b56abcdefghijklmnopqrstuvwxyz\u0b57\u0b60\u0b61\u0b62\u0b63",
+
+        /* A.3.10 Punjabi National Language Locking Shift Table
+         0.....1.....2.....3.....4.....5.....6.....7.....8.....9A.BCD.EF.....0.....123.....4.....*/
+        "\u0a01\u0a02\u0a03\u0a05\u0a06\u0a07\u0a08\u0a09\u0a0a \n  \r \u0a0f\u0a10  \u0a13\u0a14"
+            // 5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....012.....3.....
+            + "\u0a15\u0a16\u0a17\u0a18\u0a19\u0a1a\uffff\u0a1b\u0a1c\u0a1d\u0a1e !\u0a1f\u0a20"
+            // 4.....5.....6.....7.....89A.....B.....CD.....EF.....0123456789ABCD.....E.....F0.....
+            + "\u0a21\u0a22\u0a23\u0a24)(\u0a25\u0a26,\u0a27.\u0a280123456789:; \u0a2a\u0a2b?\u0a2c"
+            // 1.....2.....3.....4.....56.....7.....89.....A.....BC.....D.....E.....F0.....1.....
+            + "\u0a2d\u0a2e\u0a2f\u0a30 \u0a32\u0a33 \u0a35\u0a36 \u0a38\u0a39\u0a3c \u0a3e\u0a3f"
+            // 2.....3.....4.....56789.....A.....BCD.....E.....F.....0.....123456789ABCDEF012345678
+            + "\u0a40\u0a41\u0a42    \u0a47\u0a48  \u0a4b\u0a4c\u0a4d\u0a51abcdefghijklmnopqrstuvwx"
+            // 9AB.....C.....D.....E.....F.....
+            + "yz\u0a70\u0a71\u0a72\u0a73\u0a74",
+
+        /* A.3.11 Tamil National Language Locking Shift Table
+         01.....2.....3.....4.....5.....6.....7.....8.....9A.BCD.E.....F.....0.....12.....3..... */
+        " \u0b82\u0b83\u0b85\u0b86\u0b87\u0b88\u0b89\u0b8a \n  \r\u0b8e\u0b8f\u0b90 \u0b92\u0b93"
+            // 4.....5.....6789.....A.....B.....CD.....EF.....012.....3456.....7.....89ABCDEF.....
+            + "\u0b94\u0b95   \u0b99\u0b9a\uffff \u0b9c \u0b9e !\u0b9f   \u0ba3\u0ba4)(  , .\u0ba8"
+            // 0123456789ABC.....D.....EF012.....3.....4.....5.....6.....7.....8.....9.....A.....
+            + "0123456789:;\u0ba9\u0baa ?  \u0bae\u0baf\u0bb0\u0bb1\u0bb2\u0bb3\u0bb4\u0bb5\u0bb6"
+            // B.....C.....D.....EF0.....1.....2.....3.....4.....5678.....9.....A.....BC.....D.....
+            + "\u0bb7\u0bb8\u0bb9  \u0bbe\u0bbf\u0bc0\u0bc1\u0bc2   \u0bc6\u0bc7\u0bc8 \u0bca\u0bcb"
+            // E.....F.....0.....123456789ABCDEF0123456789AB.....C.....D.....E.....F.....
+            + "\u0bcc\u0bcd\u0bd0abcdefghijklmnopqrstuvwxyz\u0bd7\u0bf0\u0bf1\u0bf2\u0bf9",
+
+        /* A.3.12 Telugu National Language Locking Shift Table
+         0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.E.....F.....0.....*/
+        "\u0c01\u0c02\u0c03\u0c05\u0c06\u0c07\u0c08\u0c09\u0c0a\u0c0b\n\u0c0c \r\u0c0e\u0c0f\u0c10"
+            // 12.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....
+            + " \u0c12\u0c13\u0c14\u0c15\u0c16\u0c17\u0c18\u0c19\u0c1a\uffff\u0c1b\u0c1c\u0c1d"
+            // F.....012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....0123456789AB
+            + "\u0c1e !\u0c1f\u0c20\u0c21\u0c22\u0c23\u0c24)(\u0c25\u0c26,\u0c27.\u0c280123456789:;"
+            // CD.....E.....F0.....1.....2.....3.....4.....5.....6.....7.....89.....A.....B.....
+            + " \u0c2a\u0c2b?\u0c2c\u0c2d\u0c2e\u0c2f\u0c30\u0c31\u0c32\u0c33 \u0c35\u0c36\u0c37"
+            // C.....D.....EF.....0.....1.....2.....3.....4.....5.....6.....78.....9.....A.....B
+            + "\u0c38\u0c39 \u0c3d\u0c3e\u0c3f\u0c40\u0c41\u0c42\u0c43\u0c44 \u0c46\u0c47\u0c48 "
+            // C.....D.....E.....F.....0.....123456789ABCDEF0123456789AB.....C.....D.....E.....
+            + "\u0c4a\u0c4b\u0c4c\u0c4d\u0c55abcdefghijklmnopqrstuvwxyz\u0c56\u0c60\u0c61\u0c62"
+            // F.....
+            + "\u0c63",
+
+        /* A.3.13 Urdu National Language Locking Shift Table
+         0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....*/
+        "\u0627\u0622\u0628\u067b\u0680\u067e\u06a6\u062a\u06c2\u067f\n\u0679\u067d\r\u067a\u067c"
+            // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....
+            + "\u062b\u062c\u0681\u0684\u0683\u0685\u0686\u0687\u062d\u062e\u062f\uffff\u068c\u0688"
+            // E.....F.....012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....012345
+            + "\u0689\u068a !\u068f\u068d\u0630\u0631\u0691\u0693)(\u0699\u0632,\u0696.\u0698012345"
+            // 6789ABC.....D.....E.....F0.....1.....2.....3.....4.....5.....6.....7.....8.....
+            + "6789:;\u069a\u0633\u0634?\u0635\u0636\u0637\u0638\u0639\u0641\u0642\u06a9\u06aa"
+            // 9.....A.....B.....C.....D.....E.....F.....0.....1.....2.....3.....4.....5.....6.....
+            + "\u06ab\u06af\u06b3\u06b1\u0644\u0645\u0646\u06ba\u06bb\u06bc\u0648\u06c4\u06d5\u06c1"
+            // 7.....8.....9.....A.....B.....C.....D.....E.....F.....0.....123456789ABCDEF012345678
+            + "\u06be\u0621\u06cc\u06d0\u06d2\u064d\u0650\u064f\u0657\u0654abcdefghijklmnopqrstuvwx"
+            // 9AB.....C.....D.....E.....F.....
+            + "yz\u0655\u0651\u0653\u0656\u0670"
+    };
+
+    /**
+     * GSM default extension table plus national language single shift character tables.
+     */
+    private static final String[] sLanguageShiftTables = new String[]{
+        /* 6.2.1.1 GSM 7 bit Default Alphabet Extension Table
+         0123456789A.....BCDEF0123456789ABCDEF0123456789ABCDEF.0123456789ABCDEF0123456789ABCDEF */
+        "          \u000c         ^                   {}     \\            [~] |               "
+            // 0123456789ABCDEF012345.....6789ABCDEF0123456789ABCDEF
+            + "                     \u20ac                          ",
+
+        /* A.2.1 Turkish National Language Single Shift Table
+         0123456789A.....BCDEF0123456789ABCDEF0123456789ABCDEF.0123456789ABCDEF01234567.....8 */
+        "          \u000c         ^                   {}     \\            [~] |      \u011e "
+            // 9.....ABCDEF0123.....456789ABCDEF0123.....45.....67.....89.....ABCDEF0123.....
+            + "\u0130         \u015e               \u00e7 \u20ac \u011f \u0131         \u015f"
+            // 456789ABCDEF
+            + "            ",
+
+        /* A.2.2 Spanish National Language Single Shift Table
+         0123456789.....A.....BCDEF0123456789ABCDEF0123456789ABCDEF.0123456789ABCDEF01.....23 */
+        "         \u00e7\u000c         ^                   {}     \\            [~] |\u00c1  "
+            // 456789.....ABCDEF.....012345.....6789ABCDEF01.....2345.....6789.....ABCDEF.....012
+            + "     \u00cd     \u00d3     \u00da           \u00e1   \u20ac   \u00ed     \u00f3   "
+            // 345.....6789ABCDEF
+            + "  \u00fa          ",
+
+        /* A.2.3 Portuguese National Language Single Shift Table
+         012345.....6789.....A.....B.....C.....DE.....F.....012.....3.....45.....6.....7.....8....*/
+        "     \u00ea   \u00e7\u000c\u00d4\u00f4 \u00c1\u00e1  \u03a6\u0393^\u03a9\u03a0\u03a8\u03a3"
+            // 9.....ABCDEF.....0123456789ABCDEF.0123456789ABCDEF01.....23456789.....ABCDE
+            + "\u0398     \u00ca        {}     \\            [~] |\u00c0       \u00cd     "
+            // F.....012345.....6789AB.....C.....DEF01.....2345.....6789.....ABCDEF.....01234
+            + "\u00d3     \u00da     \u00c3\u00d5    \u00c2   \u20ac   \u00ed     \u00f3     "
+            // 5.....6789AB.....C.....DEF.....
+            + "\u00fa     \u00e3\u00f5  \u00e2",
+
+        /* A.2.4 Bengali National Language Single Shift Table
+         01.....23.....4.....5.6.....789A.....BCDEF0123.....45.....6789.....A.....BC.....D..... */
+        "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+ -/<=>\u00a1^\u00a1_#*\u09e6\u09e7 \u09e8\u09e9"
+            // E.....F.....0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....
+            + "\u09ea\u09eb\u09ec\u09ed\u09ee\u09ef\u09df\u09e0\u09e1\u09e2{}\u09e3\u09f2\u09f3"
+            // D.....E.....F.0.....1.....2.....3.....4.....56789ABCDEF0123456789ABCDEF
+            + "\u09f4\u09f5\\\u09f6\u09f7\u09f8\u09f9\u09fa       [~] |ABCDEFGHIJKLMNO"
+            // 0123456789ABCDEF012345.....6789ABCDEF0123456789ABCDEF
+            + "PQRSTUVWXYZ          \u20ac                          ",
+
+        /* A.2.5 Gujarati National Language Single Shift Table
+         01.....23.....4.....5.6.....789A.....BCDEF0123.....45.....6789.....A.....BC.....D..... */
+        "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+ -/<=>\u00a1^\u00a1_#*\u0964\u0965 \u0ae6\u0ae7"
+            // E.....F.....0.....1.....2.....3.....4.....5.....6789ABCDEF.0123456789ABCDEF
+            + "\u0ae8\u0ae9\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef  {}     \\            [~] "
+            // 0123456789ABCDEF0123456789ABCDEF012345.....6789ABCDEF0123456789ABCDEF
+            + "|ABCDEFGHIJKLMNOPQRSTUVWXYZ          \u20ac                          ",
+
+        /* A.2.6 Hindi National Language Single Shift Table
+         01.....23.....4.....5.6.....789A.....BCDEF0123.....45.....6789.....A.....BC.....D..... */
+        "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+ -/<=>\u00a1^\u00a1_#*\u0964\u0965 \u0966\u0967"
+            // E.....F.....0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....
+            + "\u0968\u0969\u096a\u096b\u096c\u096d\u096e\u096f\u0951\u0952{}\u0953\u0954\u0958"
+            // D.....E.....F.0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....
+            + "\u0959\u095a\\\u095b\u095c\u095d\u095e\u095f\u0960\u0961\u0962\u0963\u0970\u0971"
+            // BCDEF0123456789ABCDEF0123456789ABCDEF012345.....6789ABCDEF0123456789ABCDEF
+            + " [~] |ABCDEFGHIJKLMNOPQRSTUVWXYZ          \u20ac                          ",
+
+        /* A.2.7 Kannada National Language Single Shift Table
+         01.....23.....4.....5.6.....789A.....BCDEF0123.....45.....6789.....A.....BC.....D..... */
+        "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+ -/<=>\u00a1^\u00a1_#*\u0964\u0965 \u0ce6\u0ce7"
+            // E.....F.....0.....1.....2.....3.....4.....5.....6.....7.....89A.....BCDEF.01234567
+            + "\u0ce8\u0ce9\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u0cde\u0cf1{}\u0cf2    \\        "
+            // 89ABCDEF0123456789ABCDEF0123456789ABCDEF012345.....6789ABCDEF0123456789ABCDEF
+            + "    [~] |ABCDEFGHIJKLMNOPQRSTUVWXYZ          \u20ac                          ",
+
+        /* A.2.8 Malayalam National Language Single Shift Table
+         01.....23.....4.....5.6.....789A.....BCDEF0123.....45.....6789.....A.....BC.....D..... */
+        "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+ -/<=>\u00a1^\u00a1_#*\u0964\u0965 \u0d66\u0d67"
+            // E.....F.....0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....
+            + "\u0d68\u0d69\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0d70\u0d71{}\u0d72\u0d73\u0d74"
+            // D.....E.....F.0.....1.....2.....3.....4.....56789ABCDEF0123456789ABCDEF0123456789A
+            + "\u0d75\u0d7a\\\u0d7b\u0d7c\u0d7d\u0d7e\u0d7f       [~] |ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+            // BCDEF012345.....6789ABCDEF0123456789ABCDEF
+            + "          \u20ac                          ",
+
+        /* A.2.9 Oriya National Language Single Shift Table
+         01.....23.....4.....5.6.....789A.....BCDEF0123.....45.....6789.....A.....BC.....D..... */
+        "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+ -/<=>\u00a1^\u00a1_#*\u0964\u0965 \u0b66\u0b67"
+            // E.....F.....0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....DE
+            + "\u0b68\u0b69\u0b6a\u0b6b\u0b6c\u0b6d\u0b6e\u0b6f\u0b5c\u0b5d{}\u0b5f\u0b70\u0b71  "
+            // F.0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF012345.....6789ABCDEF0123456789A
+            + "\\            [~] |ABCDEFGHIJKLMNOPQRSTUVWXYZ          \u20ac                     "
+            // BCDEF
+            + "     ",
+
+        /* A.2.10 Punjabi National Language Single Shift Table
+         01.....23.....4.....5.6.....789A.....BCDEF0123.....45.....6789.....A.....BC.....D..... */
+        "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+ -/<=>\u00a1^\u00a1_#*\u0964\u0965 \u0a66\u0a67"
+            // E.....F.....0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....
+            + "\u0a68\u0a69\u0a6a\u0a6b\u0a6c\u0a6d\u0a6e\u0a6f\u0a59\u0a5a{}\u0a5b\u0a5c\u0a5e"
+            // D.....EF.0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF012345.....6789ABCDEF01
+            + "\u0a75 \\            [~] |ABCDEFGHIJKLMNOPQRSTUVWXYZ          \u20ac            "
+            // 23456789ABCDEF
+            + "              ",
+
+        /* A.2.11 Tamil National Language Single Shift Table
+           NOTE: TS 23.038 V9.1.1 shows code 0x24 as \u0bef, corrected to \u0bee (typo)
+         01.....23.....4.....5.6.....789A.....BCDEF0123.....45.....6789.....A.....BC.....D..... */
+        "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+ -/<=>\u00a1^\u00a1_#*\u0964\u0965 \u0be6\u0be7"
+            // E.....F.....0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....
+            + "\u0be8\u0be9\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0bf3\u0bf4{}\u0bf5\u0bf6\u0bf7"
+            // D.....E.....F.0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF012345.....6789ABC
+            + "\u0bf8\u0bfa\\            [~] |ABCDEFGHIJKLMNOPQRSTUVWXYZ          \u20ac       "
+            // DEF0123456789ABCDEF
+            + "                   ",
+
+        /* A.2.12 Telugu National Language Single Shift Table
+           NOTE: TS 23.038 V9.1.1 shows code 0x22-0x23 as \u06cc\u06cd, corrected to \u0c6c\u0c6d
+         01.....23.....4.....5.6.....789A.....BCDEF0123.....45.....6789ABC.....D.....E.....F..... */
+        "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+ -/<=>\u00a1^\u00a1_#*   \u0c66\u0c67\u0c68\u0c69"
+            // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....E.....F.
+            + "\u0c6a\u0c6b\u0c6c\u0c6d\u0c6e\u0c6f\u0c58\u0c59{}\u0c78\u0c79\u0c7a\u0c7b\u0c7c\\"
+            // 0.....1.....2.....3456789ABCDEF0123456789ABCDEF0123456789ABCDEF012345.....6789ABCD
+            + "\u0c7d\u0c7e\u0c7f         [~] |ABCDEFGHIJKLMNOPQRSTUVWXYZ          \u20ac        "
+            // EF0123456789ABCDEF
+            + "                  ",
+
+        /* A.2.13 Urdu National Language Single Shift Table
+         01.....23.....4.....5.6.....789A.....BCDEF0123.....45.....6789.....A.....BC.....D..... */
+        "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+ -/<=>\u00a1^\u00a1_#*\u0600\u0601 \u06f0\u06f1"
+            // E.....F.....0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....
+            + "\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u060c\u060d{}\u060e\u060f\u0610"
+            // D.....E.....F.0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....
+            + "\u0611\u0612\\\u0613\u0614\u061b\u061f\u0640\u0652\u0658\u066b\u066c\u0672\u0673"
+            // B.....CDEF.....0123456789ABCDEF0123456789ABCDEF012345.....6789ABCDEF0123456789ABCDEF
+            + "\u06cd[~]\u06d4|ABCDEFGHIJKLMNOPQRSTUVWXYZ          \u20ac                          "
+    };
+
+    static {
+        Resources r = Resources.getSystem();
+        // See comments in frameworks/base/core/res/res/values/config.xml for allowed values
+        sEnabledSingleShiftTables = r.getIntArray(R.array.config_sms_enabled_single_shift_tables);
+        sEnabledLockingShiftTables = r.getIntArray(R.array.config_sms_enabled_locking_shift_tables);
+        int numTables = sLanguageTables.length;
+        int numShiftTables = sLanguageShiftTables.length;
+        if (numTables != numShiftTables) {
+            Log.e(TAG, "Error: language tables array length " + numTables +
+                    " != shift tables array length " + numShiftTables);
+        }
+
+        if (sEnabledSingleShiftTables.length > 0) {
+            sHighestEnabledSingleShiftCode =
+                    sEnabledSingleShiftTables[sEnabledSingleShiftTables.length-1];
+        } else {
+            sHighestEnabledSingleShiftCode = 0;
+        }
+
+        sCharsToGsmTables = new SparseIntArray[numTables];
+        for (int i = 0; i < numTables; i++) {
+            String table = sLanguageTables[i];
+
+            int tableLen = table.length();
+            if (tableLen != 0 && tableLen != 128) {
+                Log.e(TAG, "Error: language tables index " + i +
+                        " length " + tableLen + " (expected 128 or 0)");
+            }
+
+            SparseIntArray charToGsmTable = new SparseIntArray(tableLen);
+            sCharsToGsmTables[i] = charToGsmTable;
+            for (int j = 0; j < tableLen; j++) {
+                char c = table.charAt(j);
+                charToGsmTable.put(c, j);
+            }
+        }
+
+        sCharsToShiftTables = new SparseIntArray[numTables];
+        for (int i = 0; i < numShiftTables; i++) {
+            String shiftTable = sLanguageShiftTables[i];
+
+            int shiftTableLen = shiftTable.length();
+            if (shiftTableLen != 0 && shiftTableLen != 128) {
+                Log.e(TAG, "Error: language shift tables index " + i +
+                        " length " + shiftTableLen + " (expected 128 or 0)");
+            }
+
+            SparseIntArray charToShiftTable = new SparseIntArray(shiftTableLen);
+            sCharsToShiftTables[i] = charToShiftTable;
+            for (int j = 0; j < shiftTableLen; j++) {
+                char c = shiftTable.charAt(j);
+                if (c != ' ') {
+                    charToShiftTable.put(c, j);
+                }
+            }
+        }
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java
index dbfc0d4..f186c07 100644
--- a/telephony/java/com/android/internal/telephony/IccCard.java
+++ b/telephony/java/com/android/internal/telephony/IccCard.java
@@ -28,6 +28,8 @@
 
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.CommandsInterface.RadioState;
+import com.android.internal.telephony.gsm.SIMRecords;
+
 import android.os.SystemProperties;
 
 /**
@@ -430,8 +432,14 @@
             broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
                   INTENT_VALUE_LOCKED_NETWORK);
         }
+
+        /*
+         * TODO: We need to try to remove this, maybe if the RIL sends up a RIL_UNSOL_SIM_REFRESH?
+         */
         if (oldState != State.READY && newState == State.READY && LTE_AVAILABLE_ON_CDMA) {
-            mPhone.mSIMRecords.onSimReady();
+            if (mPhone.mIccRecords instanceof SIMRecords) {
+                ((SIMRecords)mPhone.mIccRecords).onSimReady();
+            }
         }
     }
 
diff --git a/telephony/java/com/android/internal/telephony/IccRecords.java b/telephony/java/com/android/internal/telephony/IccRecords.java
index b8d9e3c..10a3b69 100644
--- a/telephony/java/com/android/internal/telephony/IccRecords.java
+++ b/telephony/java/com/android/internal/telephony/IccRecords.java
@@ -21,9 +21,6 @@
 import android.os.Message;
 import android.os.Registrant;
 import android.os.RegistrantList;
-import android.util.Log;
-
-import java.util.ArrayList;
 
 /**
  * {@hide}
@@ -79,6 +76,11 @@
         this.phone = p;
     }
 
+    /**
+     * Call when the IccRecords object is no longer going to be used.
+     */
+    public abstract void dispose();
+
     protected abstract void onRadioOffOrNotAvailable();
 
     //***** Public Methods
@@ -99,6 +101,17 @@
         recordsLoadedRegistrants.remove(h);
     }
 
+    /**
+     * Get the International Mobile Subscriber ID (IMSI) on a SIM
+     * for GSM, UMTS and like networks. Default is null if IMSI is
+     * not supported or unavailable.
+     *
+     * @return null if SIM is not yet ready or unavailable
+     */
+    public String getIMSI() {
+        return null;
+    }
+
     public String getMsisdnNumber() {
         return msisdn;
     }
@@ -220,6 +233,7 @@
     }
 
     //***** Overridden from Handler
+    @Override
     public abstract void handleMessage(Message msg);
 
     protected abstract void onRecordLoaded();
@@ -232,8 +246,51 @@
      * and TS 51.011 10.3.11 for details.
      *
      * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
+     * Generally used for GSM/UMTS and the like SIMs.
      */
-    protected abstract int getDisplayRule(String plmn);
+    public abstract int getDisplayRule(String plmn);
 
+    /**
+     * Return true if "Restriction of menu options for manual PLMN selection"
+     * bit is set or EF_CSP data is unavailable, return false otherwise.
+     * Generally used for GSM/UMTS and the like SIMs.
+     */
+    public boolean isCspPlmnEnabled() {
+        return false;
+    }
+
+    /**
+     * Returns the 5 or 6 digit MCC/MNC of the operator that
+     * provided the SIM card. Returns null of SIM is not yet ready
+     * or is not valid for the type of IccCard. Generally used for
+     * GSM/UMTS and the like SIMS
+     */
+    public String getOperatorNumeric() {
+        return null;
+    }
+
+    /**
+     * Get the current Voice call forwarding flag for GSM/UMTS and the like SIMs
+     *
+     * @return true if enabled
+     */
+    public boolean getVoiceCallForwardingFlag() {
+        return false;
+    }
+
+    /**
+     * Set the voice call forwarding flag for GSM/UMTS and the like SIMs
+     *
+     * @param line to enable/disable
+     * @param enable
+     */
+    public void setVoiceCallForwardingFlag(int line, boolean enable) {
+    }
+
+    /**
+     * Write string to log file
+     *
+     * @param s is the string to write
+     */
     protected abstract void log(String s);
 }
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 5a77da7..cb0c26a 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -118,8 +118,8 @@
     int mCallRingDelay;
     public boolean mIsTheCurrentActivePhone = true;
     boolean mIsVoiceCapable = true;
-    public SIMRecords mSIMRecords;
-    public SimCard mSimCard;
+    public IccRecords mIccRecords;
+    public IccCard mIccCard;
     public SMSDispatcher mSMS;
 
     /**
@@ -681,6 +681,31 @@
         return null;
     }
 
+    @Override
+    public IccCard getIccCard() {
+        return mIccCard;
+    }
+
+    @Override
+    public String getIccSerialNumber() {
+        return mIccRecords.iccid;
+    }
+
+    @Override
+    public boolean getIccRecordsLoaded() {
+        return mIccRecords.getRecordsLoaded();
+    }
+
+    @Override
+    public boolean getMessageWaitingIndicator() {
+        return mIccRecords.getVoiceMessageWaiting();
+    }
+
+    @Override
+    public boolean getCallForwardingIndicator() {
+        return mIccRecords.getVoiceCallForwardingFlag();
+    }
+
     /**
      *  Query the status of the CDMA roaming preference
      */
diff --git a/telephony/java/com/android/internal/telephony/SmsHeader.java b/telephony/java/com/android/internal/telephony/SmsHeader.java
index 7a65162..9492e0e 100644
--- a/telephony/java/com/android/internal/telephony/SmsHeader.java
+++ b/telephony/java/com/android/internal/telephony/SmsHeader.java
@@ -66,6 +66,8 @@
     public static final int ELT_ID_HYPERLINK_FORMAT_ELEMENT           = 0x21;
     public static final int ELT_ID_REPLY_ADDRESS_ELEMENT              = 0x22;
     public static final int ELT_ID_ENHANCED_VOICE_MAIL_INFORMATION    = 0x23;
+    public static final int ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT     = 0x24;
+    public static final int ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT    = 0x25;
 
     public static final int PORT_WAP_PUSH = 2948;
     public static final int PORT_WAP_WSP  = 9200;
@@ -96,6 +98,12 @@
     public ConcatRef concatRef;
     public ArrayList<MiscElt> miscEltList = new ArrayList<MiscElt>();
 
+    /** 7 bit national language locking shift table, or 0 for GSM default 7 bit alphabet. */
+    public int languageTable;
+
+    /** 7 bit national language single shift table, or 0 for GSM default 7 bit extension table. */
+    public int languageShiftTable;
+
     public SmsHeader() {}
 
     /**
@@ -157,6 +165,12 @@
                 portAddrs.areEightBits = false;
                 smsHeader.portAddrs = portAddrs;
                 break;
+            case ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT:
+                smsHeader.languageShiftTable = inStream.read();
+                break;
+            case ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT:
+                smsHeader.languageTable = inStream.read();
+                break;
             default:
                 MiscElt miscElt = new MiscElt();
                 miscElt.id = id;
@@ -212,6 +226,16 @@
                 outStream.write(portAddrs.origPort & 0x00FF);
             }
         }
+        if (smsHeader.languageShiftTable != 0) {
+            outStream.write(ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT);
+            outStream.write(1);
+            outStream.write(smsHeader.languageShiftTable);
+        }
+        if (smsHeader.languageTable != 0) {
+            outStream.write(ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT);
+            outStream.write(1);
+            outStream.write(smsHeader.languageTable);
+        }
         for (MiscElt miscElt : smsHeader.miscEltList) {
             outStream.write(miscElt.id);
             outStream.write(miscElt.data.length);
@@ -243,6 +267,12 @@
             builder.append(", areEightBits=" + portAddrs.areEightBits);
             builder.append(" }");
         }
+        if (languageShiftTable != 0) {
+            builder.append(", languageShiftTable=" + languageShiftTable);
+        }
+        if (languageTable != 0) {
+            builder.append(", languageTable=" + languageTable);
+        }
         for (MiscElt miscElt : miscEltList) {
             builder.append(", MiscElt ");
             builder.append("{ id=" + miscElt.id);
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index cbd8606..fcd038c 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -117,6 +117,16 @@
          */
         public int codeUnitSize;
 
+        /**
+         * The GSM national language table to use, or 0 for the default 7-bit alphabet.
+         */
+        public int languageTable;
+
+        /**
+         * The GSM national language shift table to use, or 0 for the default 7-bit extension table.
+         */
+        public int languageShiftTable;
+
         @Override
         public String toString() {
             return "TextEncodingDetails " +
@@ -124,6 +134,8 @@
                     ", codeUnitCount=" + codeUnitCount +
                     ", codeUnitsRemaining=" + codeUnitsRemaining +
                     ", codeUnitSize=" + codeUnitSize +
+                    ", languageTable=" + languageTable +
+                    ", languageShiftTable=" + languageShiftTable +
                     " }";
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/cat/ResponseData.java b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
index 677d66b..95f0399 100644
--- a/telephony/java/com/android/internal/telephony/cat/ResponseData.java
+++ b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
@@ -111,7 +111,7 @@
                     int size = mInData.length();
 
                     byte[] tempData = GsmAlphabet
-                            .stringToGsm7BitPacked(mInData);
+                            .stringToGsm7BitPacked(mInData, 0, 0);
                     data = new byte[size];
                     // Since stringToGsm7BitPacked() set byte 0 in the
                     // returned byte array to the count of septets used...
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
index e45141a..586e6b3 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
@@ -48,58 +48,30 @@
     // Constructors
     public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier) {
         this(context, ci, notifier, false);
-        log("CDMALTEPhone Constructors");
     }
 
     public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
             boolean unitTestMode) {
         super(context, ci, notifier, false);
-
-        mSIMRecords = new SIMRecords(this);
-        mSimCard = new SimCard(this, LOG_TAG, DBG);
     }
 
     @Override
-    protected void initSST() {
+    protected void initSstIcc() {
         mSST = new CdmaLteServiceStateTracker(this);
-    }
-
-    public void dispose() {
-        synchronized (PhoneProxy.lockForRadioTechnologyChange) {
-            super.dispose();
-            mSIMRecords.dispose();
-            mSimCard.dispose();
-        }
-    }
-
-    @Override
-    public void removeReferences() {
-        super.removeReferences();
-        this.mSIMRecords = null;
-        this.mSimCard = null;
-    }
-
-    @Override
-    public ServiceStateTracker getServiceStateTracker() {
-        return mSST;
-    }
-
-    public IccCard getIccCard() {
-        return mSimCard;
-    }
-
-    @Override
-    public String getIccSerialNumber() {
-        return mSIMRecords.iccid;
+        mIccRecords = new SIMRecords(this);
+        mIccCard = new SimCard(this, LOG_TAG, DBG);
     }
 
     @Override
     public DataState getDataConnectionState(String apnType) {
+        // TODO: Remove instanceof if possible.
         boolean isCdmaDataConnectionTracker = false;
         if (mDataConnectionTracker instanceof CdmaDataConnectionTracker) {
+            log("getDataConnectionState isCdmaDataConnectionTracker");
             isCdmaDataConnectionTracker = true;
+        } else {
+            log("getDataConnectionState NOT CdmaDataConnectionTracker");
         }
-        log("getDataConnectionState");
         DataState ret = DataState.DISCONNECTED;
 
         if (!isCdmaDataConnectionTracker && (SystemProperties.get("adb.connected", "").length()
@@ -145,28 +117,29 @@
             }
         }
 
+        log("getDataConnectionState apnType=" + apnType + " ret=" + ret);
         return ret;
     }
 
     public boolean updateCurrentCarrierInProvider() {
-        if (mSIMRecords != null) {
+        if (mIccRecords != null) {
             try {
                 Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
                 ContentValues map = new ContentValues();
-                map.put(Telephony.Carriers.NUMERIC, mSIMRecords.getSIMOperatorNumeric());
+                map.put(Telephony.Carriers.NUMERIC, mIccRecords.getOperatorNumeric());
+                log("updateCurrentCarrierInProvider insert uri=" + uri);
                 mContext.getContentResolver().insert(uri, map);
                 return true;
             } catch (SQLException e) {
-                Log.e(LOG_TAG, "[CDMALTEPhone] Can't store current operator", e);
+                Log.e(LOG_TAG, "[CDMALTEPhone] Can't store current operator ret false", e);
             }
+        } else {
+            log("updateCurrentCarrierInProvider mIccRecords == null ret false");
         }
         return false;
     }
 
-    public String getActiveApn(String apnType) {
-        return mDataConnectionTracker.getActiveApnString(apnType);
-    }
-
+    @Override
     protected void log(String s) {
         if (DBG)
             Log.d(LOG_TAG, "[CDMALTEPhone] " + s);
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 152ffd5..4fa369c3 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -49,6 +49,7 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.IccRecords;
 import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.IccException;
@@ -99,8 +100,6 @@
     // Instance Variables
     CdmaCallTracker mCT;
     CdmaServiceStateTracker mSST;
-    RuimRecords mRuimRecords;
-    RuimCard mRuimCard;
     ArrayList <CdmaMmiCode> mPendingMmis = new ArrayList<CdmaMmiCode>();
     RuimPhoneBookInterfaceManager mRuimPhoneBookInterfaceManager;
     RuimSmsInterfaceManager mRuimSmsInterfaceManager;
@@ -142,19 +141,21 @@
     // Constructors
     public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier) {
         super(notifier, context, ci, false);
-        initSST();
+        initSstIcc();
         init(context, notifier);
     }
 
     public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
             boolean unitTestMode) {
         super(notifier, context, ci, unitTestMode);
-        initSST();
+        initSstIcc();
         init(context, notifier);
     }
 
-    protected void initSST() {
+    protected void initSstIcc() {
         mSST = new CdmaServiceStateTracker(this);
+        mIccRecords = new RuimRecords(this);
+        mIccCard = new RuimCard(this, LOG_TAG, DBG);
     }
 
     protected void init(Context context, PhoneNotifier notifier) {
@@ -162,18 +163,16 @@
         mCT = new CdmaCallTracker(this);
         mSMS = new CdmaSMSDispatcher(this);
         mIccFileHandler = new RuimFileHandler(this);
-        mRuimRecords = new RuimRecords(this);
         mDataConnectionTracker = new CdmaDataConnectionTracker (this);
-        mRuimCard = new RuimCard(this);
         mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this);
         mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this, mSMS);
         mSubInfo = new PhoneSubInfo(this);
         mEriManager = new EriManager(this, context, EriManager.ERI_FROM_XML);
-        mCcatService = CatService.getInstance(mCM, mRuimRecords, mContext,
-                mIccFileHandler, mRuimCard);
+        mCcatService = CatService.getInstance(mCM, mIccRecords, mContext,
+                mIccFileHandler, mIccCard);
 
         mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
-        mRuimRecords.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null);
+        mIccRecords.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null);
         mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         mCM.registerForOn(this, EVENT_RADIO_ON, null);
         mCM.setOnSuppServiceNotification(this, EVENT_SSN, null);
@@ -222,9 +221,10 @@
     public void dispose() {
         synchronized(PhoneProxy.lockForRadioTechnologyChange) {
             super.dispose();
+            log("dispose");
 
             //Unregister from all former registered events
-            mRuimRecords.unregisterForRecordsLoaded(this); //EVENT_RUIM_RECORDS_LOADED
+            mIccRecords.unregisterForRecordsLoaded(this); //EVENT_RUIM_RECORDS_LOADED
             mCM.unregisterForAvailable(this); //EVENT_RADIO_AVAILABLE
             mCM.unregisterForOffOrNotAvailable(this); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE
             mCM.unregisterForOn(this); //EVENT_RADIO_ON
@@ -241,8 +241,8 @@
             mSST.dispose();
             mSMS.dispose();
             mIccFileHandler.dispose(); // instance of RuimFileHandler
-            mRuimRecords.dispose();
-            mRuimCard.dispose();
+            mIccRecords.dispose();
+            mIccCard.dispose();
             mRuimPhoneBookInterfaceManager.dispose();
             mRuimSmsInterfaceManager.dispose();
             mSubInfo.dispose();
@@ -252,13 +252,14 @@
     }
 
     public void removeReferences() {
+            log("removeReferences");
             this.mRuimPhoneBookInterfaceManager = null;
             this.mRuimSmsInterfaceManager = null;
             this.mSMS = null;
             this.mSubInfo = null;
-            this.mRuimRecords = null;
+            this.mIccRecords = null;
             this.mIccFileHandler = null;
-            this.mRuimCard = null;
+            this.mIccCard = null;
             this.mDataConnectionTracker = null;
             this.mCT = null;
             this.mSST = null;
@@ -547,14 +548,6 @@
         Log.e(LOG_TAG, "setLine1Number: not possible in CDMA");
     }
 
-    public IccCard getIccCard() {
-        return mRuimCard;
-    }
-
-    public String getIccSerialNumber() {
-        return mRuimRecords.iccid;
-    }
-
     public void setCallWaiting(boolean enable, Message onComplete) {
         Log.e(LOG_TAG, "method setCallWaiting is NOT supported in CDMA!");
     }
@@ -657,6 +650,7 @@
             }
         }
 
+        log("getDataConnectionState apnType=" + apnType + " ret=" + ret);
         return ret;
     }
 
@@ -733,7 +727,7 @@
         Message resp;
         mVmNumber = voiceMailNumber;
         resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
-        mRuimRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
+        mIccRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
     }
 
     public String getVoiceMailNumber() {
@@ -755,7 +749,7 @@
      * @hide
      */
     public int getVoiceMessageCount() {
-        int voicemailCount =  mRuimRecords.getVoiceMessageCount();
+        int voicemailCount =  mIccRecords.getVoiceMessageCount();
         // If mRuimRecords.getVoiceMessageCount returns zero, then there is possibility
         // that phone was power cycled and would have lost the voicemail count.
         // So get the count from preferences.
@@ -780,10 +774,6 @@
         return ret;
     }
 
-    public boolean getIccRecordsLoaded() {
-        return mRuimRecords.getRecordsLoaded();
-    }
-
     public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) {
         Log.e(LOG_TAG, "getCallForwardingOption: not possible in CDMA");
     }
@@ -864,13 +854,13 @@
     /*package*/ void
     updateMessageWaitingIndicator(boolean mwi) {
         // this also calls notifyMessageWaitingIndicator()
-        mRuimRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0);
+        mIccRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0);
     }
 
     /* This function is overloaded to send number of voicemails instead of sending true/false */
     /*package*/ void
     updateMessageWaitingIndicator(int mwi) {
-        mRuimRecords.setVoiceMessageWaiting(1, mwi);
+        mIccRecords.setVoiceMessageWaiting(1, mwi);
     }
 
     @Override
@@ -1417,6 +1407,7 @@
                 Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
                 ContentValues map = new ContentValues();
                 map.put(Telephony.Carriers.NUMERIC, operatorNumeric);
+                log("updateCurrentCarrierInProvider insert uri=" + uri);
                 getContext().getContentResolver().insert(uri, map);
 
                 // Updates MCC MNC device configuration information
@@ -1429,4 +1420,9 @@
         }
         return false;
     }
+
+    protected void log(String s) {
+        if (DBG)
+            Log.d(LOG_TAG, "[CDMAPhone] " + s);
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index f325812..375d0d1 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -98,7 +98,7 @@
 
         p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null);
         p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
-        p.mRuimRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
+        p.mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
         p.mCM.registerForNVReady(this, EVENT_NV_READY, null);
         p.mCM.registerForDataNetworkStateChanged (this, EVENT_DATA_STATE_CHANGED, null);
         p.mCT.registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null);
@@ -124,7 +124,7 @@
         // Unregister from all events
         mPhone.mCM.unregisterForAvailable(this);
         mPhone.mCM.unregisterForOffOrNotAvailable(this);
-        mCdmaPhone.mRuimRecords.unregisterForRecordsLoaded(this);
+        mCdmaPhone.mIccRecords.unregisterForRecordsLoaded(this);
         mPhone.mCM.unregisterForNVReady(this);
         mPhone.mCM.unregisterForDataNetworkStateChanged(this);
         mCdmaPhone.mCT.unregisterForVoiceCallEnded(this);
@@ -183,7 +183,7 @@
                     (psState == ServiceState.STATE_IN_SERVICE ||
                             mAutoAttachOnCreation) &&
                     (mPhone.mCM.getNvState() == CommandsInterface.RadioState.NV_READY ||
-                            mCdmaPhone.mRuimRecords.getRecordsLoaded()) &&
+                            mCdmaPhone.mIccRecords.getRecordsLoaded()) &&
                     (mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed() ||
                             mPhone.getState() == Phone.State.IDLE) &&
                     !roaming &&
@@ -197,7 +197,7 @@
                 reason += " - psState= " + psState;
             }
             if (!(mPhone.mCM.getNvState() == CommandsInterface.RadioState.NV_READY ||
-                    mCdmaPhone.mRuimRecords.getRecordsLoaded())) {
+                    mCdmaPhone.mIccRecords.getRecordsLoaded())) {
                 reason += " - radioState= " + mPhone.mCM.getNvState() + " - RUIM not loaded";
             }
             if (!(mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed() ||
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index afebebe..35a98edc 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -214,7 +214,7 @@
         cm.unregisterForNVReady(this);
         cm.unregisterForCdmaOtaProvision(this);
         phone.unregisterForEriFileLoaded(this);
-        phone.mRuimRecords.unregisterForRecordsLoaded(this);
+        phone.mIccRecords.unregisterForRecordsLoaded(this);
         cm.unSetOnSignalStrengthUpdate(this);
         cm.unSetOnNITZTime(this);
         cr.unregisterContentObserver(mAutoTimeObserver);
@@ -260,7 +260,7 @@
             // unlocked. At this stage, the radio is already powered on.
             isSubscriptionFromRuim = true;
             if (mNeedToRegForRuimLoaded) {
-                phone.mRuimRecords.registerForRecordsLoaded(this,
+                phone.mIccRecords.registerForRecordsLoaded(this,
                         EVENT_RUIM_RECORDS_LOADED, null);
                 mNeedToRegForRuimLoaded = false;
             }
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimCard.java b/telephony/java/com/android/internal/telephony/cdma/RuimCard.java
index 734badd..cfe7df7 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimCard.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimCard.java
@@ -25,8 +25,8 @@
  */
 public final class RuimCard extends IccCard {
 
-    RuimCard(CDMAPhone phone) {
-        super(phone, "CDMA", true);
+    RuimCard(CDMAPhone phone, String LOG_TAG, boolean dbg) {
+        super(phone, LOG_TAG, dbg);
         mPhone.mCM.registerForRUIMLockedOrAbsent(mHandler, EVENT_ICC_LOCKED_OR_ABSENT, null);
         mPhone.mCM.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         mPhone.mCM.registerForRUIMReady(mHandler, EVENT_ICC_READY, null);
@@ -43,7 +43,7 @@
 
     @Override
     public String getServiceProviderName () {
-        return ((CDMAPhone)mPhone).mRuimRecords.getServiceProviderName();
+        return mPhone.mIccRecords.getServiceProviderName();
     }
  }
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java
index 6e12f24a..ce33066 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java
@@ -32,7 +32,7 @@
 
     public RuimPhoneBookInterfaceManager(CDMAPhone phone) {
         super(phone);
-        adnCache = phone.mRuimRecords.getAdnCache();
+        adnCache = phone.mIccRecords.getAdnCache();
         //NOTE service "simphonebook" added by IccSmsInterfaceManagerProxy
     }
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
index ab664d2..ee63ede 100755
--- a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
@@ -98,6 +98,7 @@
 
     }
 
+    @Override
     public void dispose() {
         //Unregister for all events
         phone.mCM.unregisterForRUIMReady(this);
@@ -334,7 +335,7 @@
         }
         recordsLoadedRegistrants.notifyRegistrants(
             new AsyncResult(null, null, null));
-        ((CDMAPhone) phone).mRuimCard.broadcastIccStateChangedIntent(
+        phone.mIccCard.broadcastIccStateChangedIntent(
                 RuimCard.INTENT_VALUE_ICC_LOADED, null);
     }
 
@@ -343,7 +344,7 @@
           READY is sent before IMSI ready
         */
 
-        ((CDMAPhone) phone).mRuimCard.broadcastIccStateChangedIntent(
+        phone.mIccCard.broadcastIccStateChangedIntent(
                 RuimCard.INTENT_VALUE_ICC_READY, null);
 
         fetchRuimRecords();
@@ -368,8 +369,13 @@
         // Further records that can be inserted are Operator/OEM dependent
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * No Display rule for RUIMs yet.
+     */
     @Override
-    protected int getDisplayRule(String plmn) {
+    public int getDisplayRule(String plmn) {
         // TODO together with spn
         return 0;
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 7a45e15..e17d98d 100755
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -501,7 +501,7 @@
              * stringToGsm7BitPacked, and potentially directly support
              * access to the main bitwise stream from encode/decode.
              */
-            byte[] fullData = GsmAlphabet.stringToGsm7BitPacked(msg, septetOffset, !force);
+            byte[] fullData = GsmAlphabet.stringToGsm7BitPacked(msg, septetOffset, !force, 0, 0);
             Gsm7bitCodingResult result = new Gsm7bitCodingResult();
             result.data = new byte[fullData.length - 1];
             System.arraycopy(fullData, 1, result.data, 0, fullData.length - 1);
@@ -976,7 +976,8 @@
         int offsetSeptets = (offsetBits + 6) / 7;
         numFields -= offsetSeptets;
         int paddingBits = (offsetSeptets * 7) - offsetBits;
-        String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits);
+        String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits,
+                0, 0);
         if (result == null) {
             throw new CodingException("7bit GSM decoding failed");
         }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index b137ca7..3ccc03d 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -142,19 +142,18 @@
         mSST = new GsmServiceStateTracker (this);
         mSMS = new GsmSMSDispatcher(this);
         mIccFileHandler = new SIMFileHandler(this);
-        mSIMRecords = new SIMRecords(this);
+        mIccRecords = new SIMRecords(this);
         mDataConnectionTracker = new GsmDataConnectionTracker (this);
-        mSimCard = new SimCard(this);
+        mIccCard = new SimCard(this);
         if (!unitTestMode) {
             mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);
             mSimSmsIntManager = new SimSmsInterfaceManager(this, mSMS);
             mSubInfo = new PhoneSubInfo(this);
         }
-        mStkService = CatService.getInstance(mCM, mSIMRecords, mContext,
-                (SIMFileHandler)mIccFileHandler, mSimCard);
+        mStkService = CatService.getInstance(mCM, mIccRecords, mContext, mIccFileHandler, mIccCard);
 
         mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
-        mSIMRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
+        mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
         mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         mCM.registerForOn(this, EVENT_RADIO_ON, null);
         mCM.setOnUSSD(this, EVENT_USSD, null);
@@ -206,7 +205,7 @@
 
             //Unregister from all former registered events
             mCM.unregisterForAvailable(this); //EVENT_RADIO_AVAILABLE
-            mSIMRecords.unregisterForRecordsLoaded(this); //EVENT_SIM_RECORDS_LOADED
+            mIccRecords.unregisterForRecordsLoaded(this); //EVENT_SIM_RECORDS_LOADED
             mCM.unregisterForOffOrNotAvailable(this); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE
             mCM.unregisterForOn(this); //EVENT_RADIO_ON
             mSST.unregisterForNetworkAttached(this); //EVENT_REGISTERED_TO_NETWORK
@@ -221,8 +220,8 @@
             mDataConnectionTracker.dispose();
             mSST.dispose();
             mIccFileHandler.dispose(); // instance of SimFileHandler
-            mSIMRecords.dispose();
-            mSimCard.dispose();
+            mIccRecords.dispose();
+            mIccCard.dispose();
             mSimPhoneBookIntManager.dispose();
             mSimSmsIntManager.dispose();
             mSubInfo.dispose();
@@ -236,9 +235,9 @@
             this.mSimSmsIntManager = null;
             this.mSMS = null;
             this.mSubInfo = null;
-            this.mSIMRecords = null;
+            this.mIccRecords = null;
             this.mIccFileHandler = null;
-            this.mSimCard = null;
+            this.mIccCard = null;
             this.mDataConnectionTracker = null;
             this.mCT = null;
             this.mSST = null;
@@ -274,14 +273,6 @@
         return mSST.mSignalStrength;
     }
 
-    public boolean getMessageWaitingIndicator() {
-        return mSIMRecords.getVoiceMessageWaiting();
-    }
-
-    public boolean getCallForwardingIndicator() {
-        return mSIMRecords.getVoiceCallForwardingFlag();
-    }
-
     public CallTracker getCallTracker() {
         return mCT;
     }
@@ -419,7 +410,7 @@
     /*package*/ void
     updateMessageWaitingIndicator(boolean mwi) {
         // this also calls notifyMessageWaitingIndicator()
-        mSIMRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0);
+        mIccRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0);
     }
 
     public void
@@ -820,7 +811,7 @@
 
     public String getVoiceMailNumber() {
         // Read from the SIM. If its null, try reading from the shared preference area.
-        String number = mSIMRecords.getVoiceMailNumber();
+        String number = mIccRecords.getVoiceMailNumber();
         if (TextUtils.isEmpty(number)) {
             SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
             number = sp.getString(VM_NUMBER, null);
@@ -843,7 +834,7 @@
     public String getVoiceMailAlphaTag() {
         String ret;
 
-        ret = mSIMRecords.getVoiceMailAlphaTag();
+        ret = mIccRecords.getVoiceMailAlphaTag();
 
         if (ret == null || ret.length() == 0) {
             return mContext.getText(
@@ -872,23 +863,19 @@
     }
 
     public String getSubscriberId() {
-        return mSIMRecords.imsi;
-    }
-
-    public String getIccSerialNumber() {
-        return mSIMRecords.iccid;
+        return mIccRecords.getIMSI();
     }
 
     public String getLine1Number() {
-        return mSIMRecords.getMsisdnNumber();
+        return mIccRecords.getMsisdnNumber();
     }
 
     public String getLine1AlphaTag() {
-        return mSIMRecords.getMsisdnAlphaTag();
+        return mIccRecords.getMsisdnAlphaTag();
     }
 
     public void setLine1Number(String alphaTag, String number, Message onComplete) {
-        mSIMRecords.setMsisdnNumber(alphaTag, number, onComplete);
+        mIccRecords.setMsisdnNumber(alphaTag, number, onComplete);
     }
 
     public void setVoiceMailNumber(String alphaTag,
@@ -898,7 +885,7 @@
         Message resp;
         mVmNumber = voiceMailNumber;
         resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
-        mSIMRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
+        mIccRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
     }
 
     private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) {
@@ -988,15 +975,6 @@
         mCM.setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
     }
 
-    public boolean
-    getIccRecordsLoaded() {
-        return mSIMRecords.getRecordsLoaded();
-    }
-
-    public IccCard getIccCard() {
-        return mSimCard;
-    }
-
     public void
     getAvailableNetworks(Message response) {
         mCM.getAvailableNetworks(response);
@@ -1276,7 +1254,7 @@
             case EVENT_SET_CALL_FORWARD_DONE:
                 ar = (AsyncResult)msg.obj;
                 if (ar.exception == null) {
-                    mSIMRecords.setVoiceCallForwardingFlag(1, msg.arg1 == 1);
+                    mIccRecords.setVoiceCallForwardingFlag(1, msg.arg1 == 1);
                 }
                 onComplete = (Message) ar.userObj;
                 if (onComplete != null) {
@@ -1340,11 +1318,11 @@
      * @return true for success; false otherwise.
      */
     boolean updateCurrentCarrierInProvider() {
-        if (mSIMRecords != null) {
+        if (mIccRecords != null) {
             try {
                 Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
                 ContentValues map = new ContentValues();
-                map.put(Telephony.Carriers.NUMERIC, mSIMRecords.getSIMOperatorNumeric());
+                map.put(Telephony.Carriers.NUMERIC, mIccRecords.getOperatorNumeric());
                 mContext.getContentResolver().insert(uri, map);
                 return true;
             } catch (SQLException e) {
@@ -1409,11 +1387,11 @@
         if (infos == null || infos.length == 0) {
             // Assume the default is not active
             // Set unconditional CFF in SIM to false
-            mSIMRecords.setVoiceCallForwardingFlag(1, false);
+            mIccRecords.setVoiceCallForwardingFlag(1, false);
         } else {
             for (int i = 0, s = infos.length; i < s; i++) {
                 if ((infos[i].serviceClass & SERVICE_CLASS_VOICE) != 0) {
-                    mSIMRecords.setVoiceCallForwardingFlag(1, (infos[i].status == 1));
+                    mIccRecords.setVoiceCallForwardingFlag(1, (infos[i].status == 1));
                     // should only have the one
                     break;
                 }
@@ -1462,6 +1440,6 @@
     }
 
     public boolean isCspPlmnEnabled() {
-        return mSIMRecords.isCspPlmnEnabled();
+        return mIccRecords.isCspPlmnEnabled();
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 3d7fc54..84b2932 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -143,7 +143,7 @@
 
         p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null);
         p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
-        p.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
+        p.mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
         p.mCM.registerForDataNetworkStateChanged (this, EVENT_DATA_STATE_CHANGED, null);
         p.getCallTracker().registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null);
         p.getCallTracker().registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null);
@@ -179,7 +179,7 @@
         //Unregister for all events
         mPhone.mCM.unregisterForAvailable(this);
         mPhone.mCM.unregisterForOffOrNotAvailable(this);
-        mPhone.mSIMRecords.unregisterForRecordsLoaded(this);
+        mPhone.mIccRecords.unregisterForRecordsLoaded(this);
         mPhone.mCM.unregisterForDataNetworkStateChanged(this);
         mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
         mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
@@ -582,7 +582,7 @@
 
         boolean allowed =
                     gprsState == ServiceState.STATE_IN_SERVICE &&
-                    mPhone.mSIMRecords.getRecordsLoaded() &&
+                    mPhone.mIccRecords.getRecordsLoaded() &&
                     mPhone.getState() == Phone.State.IDLE &&
                     mInternalDataEnabled &&
                     (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) &&
@@ -593,7 +593,7 @@
             if (!(gprsState == ServiceState.STATE_IN_SERVICE)) {
                 reason += " - gprs= " + gprsState;
             }
-            if (!mPhone.mSIMRecords.getRecordsLoaded()) reason += " - SIM not loaded";
+            if (!mPhone.mIccRecords.getRecordsLoaded()) reason += " - SIM not loaded";
             if (mPhone.getState() != Phone.State.IDLE) {
                 reason += " - PhoneState= " + mPhone.getState();
             }
@@ -1510,7 +1510,7 @@
             log("onRadioAvailable: We're on the simulator; assuming data is connected");
         }
 
-        if (mPhone.mSIMRecords.getRecordsLoaded()) {
+        if (mPhone.mIccRecords.getRecordsLoaded()) {
             notifyDataAvailability(null);
         }
 
@@ -1757,7 +1757,7 @@
      */
     private void createAllApnList() {
         mAllApns = new ArrayList<ApnSetting>();
-        String operator = mPhone.mSIMRecords.getSIMOperatorNumeric();
+        String operator = mPhone.mIccRecords.getOperatorNumeric();
         if (operator != null) {
             String selection = "numeric = '" + operator + "'";
             if (DBG) log("createAllApnList: selection=" + selection);
@@ -1860,7 +1860,7 @@
             return apnList;
         }
 
-        String operator = mPhone.mSIMRecords.getSIMOperatorNumeric();
+        String operator = mPhone.mIccRecords.getOperatorNumeric();
         if (requestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
             if (canSetPreferApn && mPreferredApn != null) {
                 if (DBG) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 2962e0f..680b3cd 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -765,7 +765,7 @@
                         // invalid length
                         handlePasswordError(com.android.internal.R.string.invalidPin);
                     } else if (sc.equals(SC_PIN) &&
-                               phone.mSimCard.getState() == SimCard.State.PUK_REQUIRED ) {
+                               phone.mIccCard.getState() == SimCard.State.PUK_REQUIRED ) {
                         // Sim is puk-locked
                         handlePasswordError(com.android.internal.R.string.needPuk);
                     } else {
@@ -885,7 +885,7 @@
                 */
                 if ((ar.exception == null) && (msg.arg1 == 1)) {
                     boolean cffEnabled = (msg.arg2 == 1);
-                    phone.mSIMRecords.setVoiceCallForwardingFlag(1, cffEnabled);
+                    phone.mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled);
                 }
 
                 onSetComplete(ar);
@@ -1203,7 +1203,7 @@
                 (info.serviceClass & serviceClassMask)
                         == CommandsInterface.SERVICE_CLASS_VOICE) {
             boolean cffEnabled = (info.status == 1);
-            phone.mSIMRecords.setVoiceCallForwardingFlag(1, cffEnabled);
+            phone.mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled);
         }
 
         return TextUtils.replace(template, sources, destinations);
@@ -1228,7 +1228,7 @@
                 sb.append(context.getText(com.android.internal.R.string.serviceDisabled));
 
                 // Set unconditional CFF in SIM to false
-                phone.mSIMRecords.setVoiceCallForwardingFlag(1, false);
+                phone.mIccRecords.setVoiceCallForwardingFlag(1, false);
             } else {
 
                 SpannableStringBuilder tb = new SpannableStringBuilder();
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 43d6b23..b021967 100755
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -69,9 +69,8 @@
         String pduString = (String) ar.result;
         SmsMessage sms = SmsMessage.newFromCDS(pduString);
 
-        int tpStatus = sms.getStatus();
-
         if (sms != null) {
+            int tpStatus = sms.getStatus();
             int messageRef = sms.messageRef;
             for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
                 SmsTracker tracker = deliveryPendingList.get(i);
@@ -201,6 +200,7 @@
 
         mRemainingMessages = msgCount;
 
+        TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
         for (int i = 0; i < msgCount; i++) {
             TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false);
             if (encoding != details.codeUnitSize
@@ -208,6 +208,7 @@
                             || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) {
                 encoding = details.codeUnitSize;
             }
+            encodingForParts[i] = details;
         }
 
         for (int i = 0; i < msgCount; i++) {
@@ -224,6 +225,10 @@
             concatRef.isEightBits = true;
             SmsHeader smsHeader = new SmsHeader();
             smsHeader.concatRef = concatRef;
+            if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) {
+                smsHeader.languageTable = encodingForParts[i].languageTable;
+                smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
+            }
 
             PendingIntent sentIntent = null;
             if (sentIntents != null && sentIntents.size() > i) {
@@ -237,7 +242,7 @@
 
             SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
                     parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
-                    encoding);
+                    encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
 
             sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent);
         }
@@ -292,6 +297,7 @@
 
         mRemainingMessages = msgCount;
 
+        TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
         for (int i = 0; i < msgCount; i++) {
             TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false);
             if (encoding != details.codeUnitSize
@@ -299,6 +305,7 @@
                             || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) {
                 encoding = details.codeUnitSize;
             }
+            encodingForParts[i] = details;
         }
 
         for (int i = 0; i < msgCount; i++) {
@@ -309,6 +316,10 @@
             concatRef.isEightBits = false;
             SmsHeader smsHeader = new SmsHeader();
             smsHeader.concatRef = concatRef;
+            if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) {
+                smsHeader.languageTable = encodingForParts[i].languageTable;
+                smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
+            }
 
             PendingIntent sentIntent = null;
             if (sentIntents != null && sentIntents.size() > i) {
@@ -322,7 +333,7 @@
 
             SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
                     parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
-                    encoding);
+                    encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
 
             HashMap<String, Object> map = new HashMap<String, Object>();
             map.put("smsc", pdus.encodedScAddress);
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 29a1ed5..4352831 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -246,7 +246,7 @@
         cm.unregisterForVoiceNetworkStateChanged(this);
         cm.unregisterForSIMReady(this);
 
-        phone.mSIMRecords.unregisterForRecordsLoaded(this);
+        phone.mIccRecords.unregisterForRecordsLoaded(this);
         cm.unSetOnSignalStrengthUpdate(this);
         cm.unSetOnRestrictedStateChanged(this);
         cm.unSetOnNITZTime(this);
@@ -280,7 +280,7 @@
                 // it has been unlocked. At this stage, the radio is already
                 // powered on.
                 if (mNeedToRegForSimLoaded) {
-                    phone.mSIMRecords.registerForRecordsLoaded(this,
+                    phone.mIccRecords.registerForRecordsLoaded(this,
                             EVENT_SIM_RECORDS_LOADED, null);
                     mNeedToRegForSimLoaded = false;
                 }
@@ -486,8 +486,8 @@
     }
 
     protected void updateSpnDisplay() {
-        int rule = phone.mSIMRecords.getDisplayRule(ss.getOperatorNumeric());
-        String spn = phone.mSIMRecords.getServiceProviderName();
+        int rule = phone.mIccRecords.getDisplayRule(ss.getOperatorNumeric());
+        String spn = phone.mIccRecords.getServiceProviderName();
         String plmn = ss.getOperatorAlphaLong();
 
         // For emergency calls only, pass the EmergencyCallsOnly string via EXTRA_PLMN
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index 7fb250f..e0323e3 100755
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -59,8 +59,8 @@
 
     // ***** Cached SIM State; cleared on channel close
 
-    String imsi;
-    boolean callForwardingEnabled;
+    private String imsi;
+    private boolean callForwardingEnabled;
 
 
     /**
@@ -191,6 +191,7 @@
 
     }
 
+    @Override
     public void dispose() {
         //Unregister for all events
         phone.mCM.unregisterForSIMReady(this);
@@ -231,7 +232,10 @@
 
     //***** Public Methods
 
-    /** Returns null if SIM is not yet ready */
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public String getIMSI() {
         return imsi;
     }
@@ -404,10 +408,18 @@
         }
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public boolean getVoiceCallForwardingFlag() {
         return callForwardingEnabled;
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public void setVoiceCallForwardingFlag(int line, boolean enable) {
 
         if (line != 1) return; // only line 1 is supported
@@ -468,12 +480,13 @@
         }
     }
 
-    /** Returns the 5 or 6 digit MCC/MNC of the operator that
-     *  provided the SIM card. Returns null of SIM is not yet ready
+    /**
+     * {@inheritDoc}
      */
-    public String getSIMOperatorNumeric() {
+    @Override
+    public String getOperatorNumeric() {
         if (imsi == null) {
-            Log.d(LOG_TAG, "getSIMOperatorNumeric: IMSI == null");
+            Log.d(LOG_TAG, "getOperatorNumeric: IMSI == null");
             return null;
         }
         if (mncLength == UNINITIALIZED || mncLength == UNKNOWN) {
@@ -484,7 +497,7 @@
         // STOPSHIP: to be removed
         if (SystemProperties.getInt(com.android.internal.telephony.TelephonyProperties
                 .PROPERTY_NETWORK_LTE_ON_CDMA, 0) == 1) {
-            Log.e(LOG_TAG, "getSIMOperatorNumeric: STOPSHIP bad numeric operators in lte");
+            Log.e(LOG_TAG, "getOperatorNumeric: STOPSHIP bad numeric operators in lte");
             return SystemProperties.get("ro.cdma.home.operator.numeric", "310004");
         }
         // Length = length of MCC + length of MNC
@@ -559,7 +572,7 @@
                     // finally have both the imsi and the mncLength and can parse the imsi properly
                     MccTable.updateMccMncConfiguration(phone, imsi.substring(0, 3 + mncLength));
                 }
-                phone.mSimCard.broadcastIccStateChangedIntent(
+                phone.mIccCard.broadcastIccStateChangedIntent(
                         SimCard.INTENT_VALUE_ICC_IMSI, null);
             break;
 
@@ -1221,7 +1234,7 @@
     protected void onAllRecordsLoaded() {
         Log.d(LOG_TAG, "SIMRecords: record load complete");
 
-        String operator = getSIMOperatorNumeric();
+        String operator = getOperatorNumeric();
 
         // Some fields require more than one SIM record to set
 
@@ -1240,7 +1253,7 @@
 
         recordsLoadedRegistrants.notifyRegistrants(
             new AsyncResult(null, null, null));
-        phone.mSimCard.broadcastIccStateChangedIntent(
+        phone.mIccCard.broadcastIccStateChangedIntent(
                 SimCard.INTENT_VALUE_ICC_LOADED, null);
     }
 
@@ -1265,7 +1278,7 @@
         /* broadcast intent SIM_READY here so that we can make sure
           READY is sent before IMSI ready
         */
-        phone.mSimCard.broadcastIccStateChangedIntent(
+        phone.mIccCard.broadcastIccStateChangedIntent(
                 SimCard.INTENT_VALUE_ICC_READY, null);
 
         fetchSimRecords();
@@ -1362,7 +1375,8 @@
      *
      * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
      */
-    protected int getDisplayRule(String plmn) {
+    @Override
+    public int getDisplayRule(String plmn) {
         int rule;
         if (spn == null || spnDisplayCondition == -1) {
             // EF_SPN was not found on the SIM, or not yet loaded.  Just show ONS.
@@ -1389,7 +1403,7 @@
     private boolean isOnMatchingPlmn(String plmn) {
         if (plmn == null) return false;
 
-        if (plmn.equals(getSIMOperatorNumeric())) {
+        if (plmn.equals(getOperatorNumeric())) {
             return true;
         }
 
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimCard.java b/telephony/java/com/android/internal/telephony/gsm/SimCard.java
index 781746c..09df49f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimCard.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimCard.java
@@ -62,7 +62,7 @@
 
     @Override
     public String getServiceProviderName () {
-        return ((GSMPhone)mPhone).mSIMRecords.getServiceProviderName();
+        return mPhone.mIccRecords.getServiceProviderName();
     }
 
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java
index feb508a..377f8f0 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java
@@ -32,7 +32,7 @@
 
     public SimPhoneBookInterfaceManager(GSMPhone phone) {
         super(phone);
-        adnCache = phone.mSIMRecords.getAdnCache();
+        adnCache = phone.mIccRecords.getAdnCache();
         //NOTE service "simphonebook" added by IccSmsInterfaceManagerProxy
     }
 
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index e960091..3784e7c 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -37,7 +37,6 @@
 import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
 import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
 import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
-import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER;
 import static android.telephony.SmsMessage.MessageClass;
 
 /**
@@ -213,9 +212,7 @@
      */
     public static int getTPLayerLengthForPDU(String pdu) {
         int len = pdu.length() / 2;
-        int smscLen = 0;
-
-        smscLen = Integer.parseInt(pdu.substring(0, 2), 16);
+        int smscLen = Integer.parseInt(pdu.substring(0, 2), 16);
 
         return len - smscLen - 1;
     }
@@ -233,7 +230,7 @@
             String destinationAddress, String message,
             boolean statusReportRequested, byte[] header) {
         return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, header,
-                ENCODING_UNKNOWN);
+                ENCODING_UNKNOWN, 0, 0);
     }
 
 
@@ -243,6 +240,8 @@
      *
      * @param scAddress Service Centre address.  Null means use default.
      * @param encoding Encoding defined by constants in android.telephony.SmsMessage.ENCODING_*
+     * @param languageTable
+     * @param languageShiftTable
      * @return a <code>SubmitPdu</code> containing the encoded SC
      *         address, if applicable, and the encoded message.
      *         Returns null on encode error.
@@ -250,7 +249,8 @@
      */
     public static SubmitPdu getSubmitPdu(String scAddress,
             String destinationAddress, String message,
-            boolean statusReportRequested, byte[] header, int encoding) {
+            boolean statusReportRequested, byte[] header, int encoding,
+            int languageTable, int languageShiftTable) {
 
         // Perform null parameter checks.
         if (message == null || destinationAddress == null) {
@@ -271,7 +271,8 @@
         }
         try {
             if (encoding == ENCODING_7BIT) {
-                userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header);
+                userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header,
+                        languageTable, languageShiftTable);
             } else { //assume UCS-2
                 try {
                     userData = encodeUCS2(message, header);
@@ -685,70 +686,19 @@
             return userDataHeader;
         }
 
-/*
-        XXX Not sure what this one is supposed to be doing, and no one is using
-        it.
-        String getUserDataGSM8bit() {
-            // System.out.println("remainder of pdu:" +
-            // HexDump.dumpHexString(pdu, cur, pdu.length - cur));
-            int count = pdu[cur++] & 0xff;
-            int size = pdu[cur++];
-
-            // skip over header for now
-            cur += size;
-
-            if (pdu[cur - 1] == 0x01) {
-                int tid = pdu[cur++] & 0xff;
-                int type = pdu[cur++] & 0xff;
-
-                size = pdu[cur++] & 0xff;
-
-                int i = cur;
-
-                while (pdu[i++] != '\0') {
-                }
-
-                int length = i - cur;
-                String mimeType = new String(pdu, cur, length);
-
-                cur += length;
-
-                if (false) {
-                    System.out.println("tid = 0x" + HexDump.toHexString(tid));
-                    System.out.println("type = 0x" + HexDump.toHexString(type));
-                    System.out.println("header size = " + size);
-                    System.out.println("mimeType = " + mimeType);
-                    System.out.println("remainder of header:" +
-                     HexDump.dumpHexString(pdu, cur, (size - mimeType.length())));
-                }
-
-                cur += size - mimeType.length();
-
-                // System.out.println("data count = " + count + " cur = " + cur
-                // + " :" + HexDump.dumpHexString(pdu, cur, pdu.length - cur));
-
-                MMSMessage msg = MMSMessage.parseEncoding(mContext, pdu, cur,
-                        pdu.length - cur);
-            } else {
-                System.out.println(new String(pdu, cur, pdu.length - cur - 1));
-            }
-
-            return IccUtils.bytesToHexString(pdu);
-        }
-*/
-
         /**
-         * Interprets the user data payload as pack GSM 7bit characters, and
+         * Interprets the user data payload as packed GSM 7bit characters, and
          * decodes them into a String.
          *
          * @param septetCount the number of septets in the user data payload
          * @return a String with the decoded characters
          */
-        String getUserDataGSM7Bit(int septetCount) {
+        String getUserDataGSM7Bit(int septetCount, int languageTable,
+                int languageShiftTable) {
             String ret;
 
             ret = GsmAlphabet.gsm7BitPackedToString(pdu, cur, septetCount,
-                    mUserDataSeptetPadding);
+                    mUserDataSeptetPadding, languageTable, languageShiftTable);
 
             cur += (septetCount * 7) / 8;
 
@@ -811,21 +761,9 @@
      */
     public static TextEncodingDetails calculateLength(CharSequence msgBody,
             boolean use7bitOnly) {
-        TextEncodingDetails ted = new TextEncodingDetails();
-        try {
-            int septets = GsmAlphabet.countGsmSeptets(msgBody, !use7bitOnly);
-            ted.codeUnitCount = septets;
-            if (septets > MAX_USER_DATA_SEPTETS) {
-                ted.msgCount = (septets + (MAX_USER_DATA_SEPTETS_WITH_HEADER - 1)) /
-                        MAX_USER_DATA_SEPTETS_WITH_HEADER;
-                ted.codeUnitsRemaining = (ted.msgCount *
-                        MAX_USER_DATA_SEPTETS_WITH_HEADER) - septets;
-            } else {
-                ted.msgCount = 1;
-                ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS - septets;
-            }
-            ted.codeUnitSize = ENCODING_7BIT;
-        } catch (EncodeException ex) {
+        TextEncodingDetails ted = GsmAlphabet.countGsmSeptets(msgBody, use7bitOnly);
+        if (ted == null) {
+            ted = new TextEncodingDetails();
             int octets = msgBody.length() * 2;
             ted.codeUnitCount = msgBody.length();
             if (octets > MAX_USER_DATA_BYTES) {
@@ -866,7 +804,7 @@
     /** {@inheritDoc} */
     @Override
     public boolean isMWIClearMessage() {
-        if (isMwi && (mwiSense == false)) {
+        if (isMwi && !mwiSense) {
             return true;
         }
 
@@ -877,7 +815,7 @@
     /** {@inheritDoc} */
     @Override
     public boolean isMWISetMessage() {
-        if (isMwi && (mwiSense == true)) {
+        if (isMwi && mwiSense) {
             return true;
         }
 
@@ -1160,7 +1098,9 @@
             break;
 
         case ENCODING_7BIT:
-            messageBody = p.getUserDataGSM7Bit(count);
+            messageBody = p.getUserDataGSM7Bit(count,
+                    hasUserDataHeader ? userDataHeader.languageTable : 0,
+                    hasUserDataHeader ? userDataHeader.languageShiftTable : 0);
             break;
 
         case ENCODING_16BIT:
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmAlphabetTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmAlphabetTest.java
index 7011aeb..f9dc3a9 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmAlphabetTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmAlphabetTest.java
@@ -22,7 +22,6 @@
 
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
 
 public class GsmAlphabetTest extends TestCase {
 
@@ -40,15 +39,16 @@
 
         String message = "aaaaaaaaaabbbbbbbbbbcccccccccc";
         byte[] userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message,
-                SmsHeader.toByteArray(header));
-        int septetCount = GsmAlphabet.countGsmSeptets(message, false);
+                SmsHeader.toByteArray(header), 0, 0);
+        int septetCount = GsmAlphabet.countGsmSeptetsUsingTables(message, true, 0, 0);
         String parsedMessage = GsmAlphabet.gsm7BitPackedToString(
-                userData, SmsHeader.toByteArray(header).length+2, septetCount, 1);
+                userData, SmsHeader.toByteArray(header).length+2, septetCount, 1, 0, 0);
         assertEquals(message, parsedMessage);
     }
 
     // TODO: This method should *really* be a series of individual test methods.
-    @LargeTest
+    // However, it's a SmallTest because it executes quickly.
+    @SmallTest
     public void testBasic() throws Exception {
         // '@' maps to char 0
         assertEquals(0, GsmAlphabet.charToGsm('@'));
@@ -99,7 +99,7 @@
 
         assertEquals('@', GsmAlphabet.gsmToChar(0));
 
-        // `a (a with grave accent) maps to last GSM charater
+        // `a (a with grave accent) maps to last GSM character
         assertEquals('\u00e0', GsmAlphabet.gsmToChar(0x7f));
 
         assertEquals('\uffff',
@@ -118,8 +118,12 @@
         assertEquals(' ', GsmAlphabet.gsmExtendedToChar(
                 GsmAlphabet.GSM_EXTENDED_ESCAPE));
 
-        // Unmappable
-        assertEquals(' ', GsmAlphabet.gsmExtendedToChar(0));
+        // Reserved for extension to extension table (mapped to space)
+        assertEquals(' ', GsmAlphabet.gsmExtendedToChar(GsmAlphabet.GSM_EXTENDED_ESCAPE));
+
+        // Unmappable (mapped to character in default or national locking shift table)
+        assertEquals('@', GsmAlphabet.gsmExtendedToChar(0));
+        assertEquals('\u00e0', GsmAlphabet.gsmExtendedToChar(0x7f));
 
         //
         // stringTo7BitPacked, gsm7BitPackedToString
@@ -130,7 +134,7 @@
 
         // Check all alignment cases
         for (int i = 0; i < 9; i++, testString.append('@')) {
-            packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString());
+            packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
             assertEquals(testString.toString(),
                     GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
         }
@@ -151,7 +155,7 @@
             assertEquals(1, GsmAlphabet.countGsmSeptets(c));
         }
 
-        packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString());
+        packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
         assertEquals(testString.toString(),
                 GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
 
@@ -166,7 +170,7 @@
 
         }
 
-        packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString());
+        packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
         assertEquals(testString.toString(),
                 GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
 
@@ -177,7 +181,7 @@
             testString.append('@');
         }
 
-        packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString());
+        packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
         assertEquals(testString.toString(),
                 GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
 
@@ -185,7 +189,7 @@
         testString.append('@');
 
         try {
-            GsmAlphabet.stringToGsm7BitPacked(testString.toString());
+            GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
             fail("expected exception");
         } catch (EncodeException ex) {
             // exception expected
@@ -198,7 +202,7 @@
             testString.append('{');
         }
 
-        packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString());
+        packed = GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
         assertEquals(testString.toString(),
                 GsmAlphabet.gsm7BitPackedToString(packed, 1, 0xff & packed[0]));
 
@@ -206,17 +210,29 @@
         testString.append('{');
 
         try {
-            GsmAlphabet.stringToGsm7BitPacked(testString.toString());
+            GsmAlphabet.stringToGsm7BitPacked(testString.toString(), 0, 0);
             fail("expected exception");
         } catch (EncodeException ex) {
             // exception expected
         }
 
+        // Reserved for extension to extension table (mapped to space)
+        packed = new byte[]{(byte)(0x1b | 0x80), 0x1b >> 1};
+        assertEquals(" ", GsmAlphabet.gsm7BitPackedToString(packed, 0, 2));
+
+        // Unmappable (mapped to character in default alphabet table)
+        packed[0] = 0x1b;
+        packed[1] = 0x00;
+        assertEquals("@", GsmAlphabet.gsm7BitPackedToString(packed, 0, 2));
+        packed[0] = (byte)(0x1b | 0x80);
+        packed[1] = (byte)(0x7f >> 1);
+        assertEquals("\u00e0", GsmAlphabet.gsm7BitPackedToString(packed, 0, 2));
+
         //
         // 8 bit unpacked format
         //
         // Note: we compare hex strings here
-        // because Assert doesnt have array-comparisons
+        // because Assert doesn't have array comparisons
 
         byte unpacked[];
 
@@ -308,6 +324,17 @@
 
         assertEquals("a",
                 GsmAlphabet.gsm8BitUnpackedToString(unpacked, 1, unpacked.length - 1));
+
+        // Reserved for extension to extension table (mapped to space)
+        unpacked[0] = 0x1b;
+        unpacked[1] = 0x1b;
+        assertEquals(" ", GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, 2));
+
+        // Unmappable (mapped to character in default or national locking shift table)
+        unpacked[1] = 0x00;
+        assertEquals("@", GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, 2));
+        unpacked[1] = 0x7f;
+        assertEquals("\u00e0", GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, 2));
     }
 
     @SmallTest
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
index 215c6ce..41a719e 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.telephony;
 
-import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.gsm.SmsMessage;
 import com.android.internal.util.HexDump;
 
@@ -209,8 +207,38 @@
                 sms.getMessageBody());
     }
 
+    // GSM 7 bit tables in String form, Escape (0x1B) replaced with '@'
+    private static final String[] sBasicTables = {
+        // GSM 7 bit default alphabet
+        "@\u00a3$\u00a5\u00e8\u00e9\u00f9\u00ec\u00f2\u00c7\n\u00d8\u00f8\r\u00c5\u00e5\u0394_"
+            + "\u03a6\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u039e@\u00c6\u00e6\u00df\u00c9"
+            + " !\"#\u00a4%&'()*+,-./0123456789:;<=>?\u00a1ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00c4\u00d6"
+            + "\u00d1\u00dc\u00a7\u00bfabcdefghijklmnopqrstuvwxyz\u00e4\u00f6\u00f1\u00fc\u00e0",
+
+        // Turkish locking shift table
+        "@\u00a3$\u00a5\u20ac\u00e9\u00f9\u0131\u00f2\u00c7\n\u011e\u011f\r\u00c5\u00e5\u0394_"
+            + "\u03a6\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u039e@\u015e\u015f\u00df\u00c9"
+            + " !\"#\u00a4%&'()*+,-./0123456789:;<=>?\u0130ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00c4\u00d6"
+            + "\u00d1\u00dc\u00a7\u00e7abcdefghijklmnopqrstuvwxyz\u00e4\u00f6\u00f1\u00fc\u00e0",
+
+        // no locking shift table defined for Spanish
+        "",
+
+        // Portuguese locking shift table
+        "@\u00a3$\u00a5\u00ea\u00e9\u00fa\u00ed\u00f3\u00e7\n\u00d4\u00f4\r\u00c1\u00e1\u0394_"
+            + "\u00aa\u00c7\u00c0\u221e^\\\u20ac\u00d3|@\u00c2\u00e2\u00ca\u00c9 !\"#\u00ba%&'()"
+            + "*+,-./0123456789:;<=>?\u00cdABCDEFGHIJKLMNOPQRSTUVWXYZ\u00c3\u00d5\u00da\u00dc"
+            + "\u00a7~abcdefghijklmnopqrstuvwxyz\u00e3\u00f5`\u00fc\u00e0"
+    };
+
     @SmallTest
     public void testDecode() throws Exception {
+        decodeSingle(0);    // default table
+        decodeSingle(1);    // Turkish locking shift table
+        decodeSingle(3);    // Portuguese locking shift table
+    }
+
+    private void decodeSingle(int language) throws Exception {
         byte[] septets = new byte[(7 * 128 + 7) / 8];
 
         int bitOffset = 0;
@@ -236,15 +264,168 @@
             bitOffset += 7;
         }
 
-        String decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, 128);
-        byte[] reEncoded = GsmAlphabet.stringToGsm7BitPacked(decoded);
+        String decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, 128, 0, language, 0);
+        byte[] reEncoded = GsmAlphabet.stringToGsm7BitPacked(decoded, language, 0);
+
+        assertEquals(sBasicTables[language], decoded);
 
         // reEncoded has the count septets byte at the front
-        assertEquals(reEncoded.length, septets.length + 1);
+        assertEquals(septets.length + 1, reEncoded.length);
 
         for (int i = 0; i < septets.length; i++) {
-            assertEquals(reEncoded[i + 1], septets[i]);
+            assertEquals(septets[i], reEncoded[i + 1]);
         }
     }
 
+    private static final int GSM_ESCAPE_CHARACTER = 0x1b;
+
+    private static final String[] sExtendedTables = {
+        // GSM 7 bit default alphabet extension table
+        "\f^{}\\[~]|\u20ac",
+
+        // Turkish single shift extension table
+        "\f^{}\\[~]|\u011e\u0130\u015e\u00e7\u20ac\u011f\u0131\u015f",
+
+        // Spanish single shift extension table
+        "\u00e7\f^{}\\[~]|\u00c1\u00cd\u00d3\u00da\u00e1\u20ac\u00ed\u00f3\u00fa",
+
+        // Portuguese single shift extension table
+        "\u00ea\u00e7\f\u00d4\u00f4\u00c1\u00e1\u03a6\u0393^\u03a9\u03a0\u03a8\u03a3\u0398\u00ca"
+            + "{}\\[~]|\u00c0\u00cd\u00d3\u00da\u00c3\u00d5\u00c2\u20ac\u00ed\u00f3\u00fa\u00e3"
+            + "\u00f5\u00e2"
+    };
+
+    private static final int[][] sExtendedTableIndexes = {
+        {0x0a, 0x14, 0x28, 0x29, 0x2f, 0x3c, 0x3d, 0x3e, 0x40, 0x65},
+        {0x0a, 0x14, 0x28, 0x29, 0x2f, 0x3c, 0x3d, 0x3e, 0x40, 0x47, 0x49, 0x53, 0x63,
+                0x65, 0x67, 0x69, 0x73},
+        {0x09, 0x0a, 0x14, 0x28, 0x29, 0x2f, 0x3c, 0x3d, 0x3e, 0x40, 0x41, 0x49, 0x4f,
+                0x55, 0x61, 0x65, 0x69, 0x6f, 0x75},
+        {0x05, 0x09, 0x0a, 0x0b, 0x0c, 0x0e, 0x0f, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                0x18, 0x19, 0x1f, 0x28, 0x29, 0x2f, 0x3c, 0x3d, 0x3e, 0x40, 0x41, 0x49,
+                0x4f, 0x55, 0x5b, 0x5c, 0x61, 0x65, 0x69, 0x6f, 0x75, 0x7b, 0x7c, 0x7f}
+    };
+
+    @SmallTest
+    public void testDecodeExtended() throws Exception {
+        for (int language = 0; language < 3; language++) {
+            int[] tableIndex = sExtendedTableIndexes[language];
+            int numSeptets = tableIndex.length * 2;  // two septets per extended char
+            byte[] septets = new byte[(7 * numSeptets + 7) / 8];
+
+            int bitOffset = 0;
+
+            for (int v : tableIndex) {
+                // escape character
+                int byteOffset = bitOffset / 8;
+                int shift = bitOffset % 8;
+
+                septets[byteOffset] |= GSM_ESCAPE_CHARACTER << shift;
+
+                if (shift > 1) {
+                    septets[byteOffset + 1] = (byte) (GSM_ESCAPE_CHARACTER >> (8 - shift));
+                }
+
+                bitOffset += 7;
+
+                // extended table index
+                byteOffset = bitOffset / 8;
+                shift = bitOffset % 8;
+
+                septets[byteOffset] |= v << shift;
+
+                if (shift > 1) {
+                    septets[byteOffset + 1] = (byte) (v >> (8 - shift));
+                }
+
+                bitOffset += 7;
+            }
+
+            String decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, numSeptets, 0,
+                    0, language);
+            byte[] reEncoded = GsmAlphabet.stringToGsm7BitPacked(decoded, 0, language);
+
+            assertEquals(sExtendedTables[language], decoded);
+
+            // reEncoded has the count septets byte at the front
+            assertEquals(septets.length + 1, reEncoded.length);
+
+            for (int i = 0; i < septets.length; i++) {
+                assertEquals(septets[i], reEncoded[i + 1]);
+            }
+        }
+    }
+
+    @SmallTest
+    public void testDecodeExtendedFallback() throws Exception {
+        // verify that unmapped characters in extension table fall back to locking shift table
+        for (int language = 0; language < 3; language++) {
+            int[] tableIndex = sExtendedTableIndexes[language];
+            int numChars = 128 - tableIndex.length;
+            int numSeptets = numChars * 2;  // two septets per extended char
+            byte[] septets = new byte[(7 * numSeptets + 7) / 8];
+
+            int tableOffset = 0;
+            int bitOffset = 0;
+
+            StringBuilder defaultTable = new StringBuilder(128);
+            StringBuilder turkishTable = new StringBuilder(128);
+            StringBuilder portugueseTable = new StringBuilder(128);
+
+            for (char c = 0; c < 128; c++) {
+                // skip characters that are present in the current extension table
+                if (tableOffset < tableIndex.length && tableIndex[tableOffset] == c) {
+                    tableOffset++;
+                    continue;
+                }
+
+                // escape character
+                int byteOffset = bitOffset / 8;
+                int shift = bitOffset % 8;
+
+                septets[byteOffset] |= GSM_ESCAPE_CHARACTER << shift;
+
+                if (shift > 1) {
+                    septets[byteOffset + 1] = (byte) (GSM_ESCAPE_CHARACTER >> (8 - shift));
+                }
+
+                bitOffset += 7;
+
+                // extended table index
+                byteOffset = bitOffset / 8;
+                shift = bitOffset % 8;
+
+                septets[byteOffset] |= c << shift;
+
+                if (shift > 1) {
+                    septets[byteOffset + 1] = (byte) (c >> (8 - shift));
+                }
+
+                bitOffset += 7;
+
+                if (c == GsmAlphabet.GSM_EXTENDED_ESCAPE) {
+                    // double Escape maps to space character
+                    defaultTable.append(' ');
+                    turkishTable.append(' ');
+                    portugueseTable.append(' ');
+                } else {
+                    // other unmapped chars map to the default or locking shift table
+                    defaultTable.append(sBasicTables[0].charAt(c));
+                    turkishTable.append(sBasicTables[1].charAt(c));
+                    portugueseTable.append(sBasicTables[3].charAt(c));
+                }
+            }
+
+            String decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, numSeptets, 0,
+                    0, language);
+
+            assertEquals(defaultTable.toString(), decoded);
+
+            decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, numSeptets, 0, 1, language);
+            assertEquals(turkishTable.toString(), decoded);
+
+            decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, numSeptets, 0, 3, language);
+            assertEquals(portugueseTable.toString(), decoded);
+        }
+    }
 }
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java
index b214887..170bd9b 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java
@@ -19,21 +19,57 @@
 import android.telephony.SmsMessage;
 import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
 
+import java.util.Random;
+
+import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
+import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
 import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
+import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER;
 
+/**
+ * Test cases to verify selection of the optimal 7 bit encoding tables
+ * (for all combinations of enabled national language tables) for messages
+ * containing Turkish, Spanish, Portuguese, Greek, and other symbols
+ * present in the GSM default and national language tables defined in
+ * 3GPP TS 23.038. Also verifies correct SMS encoding for CDMA, which only
+ * supports the GSM 7 bit default alphabet, ASCII 8 bit, and UCS-2.
+ * Tests both encoding variations: unsupported characters mapped to space,
+ * and unsupported characters force entire message to UCS-2.
+ */
 public class SmsMessageBodyTest extends AndroidTestCase {
+    private static final String TAG = "SmsMessageBodyTest";
 
+    // ASCII chars in the GSM 7 bit default alphabet
     private static final String sAsciiChars = "@$_ !\"#%&'()*+,-./0123456789" +
             ":;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n\r";
-    private static final String sGsmBasicChars = "\u00a3\u00a5\u00e8\u00e9" +
-            "\u00f9\u00ec\u00f2\u00c7\u00d8\u00f8\u00c5\u00e5\u0394\u03a6" +
-            "\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u00c6\u00e6" +
-            "\u00df\u00c9\u00a4\u00a1\u00c4\u00d6\u00d1\u00dc\u00a7\u00bf" +
-            "\u00e4\u00f6\u00f1\u00fc\u00e0";
-    private static final String sGsmExtendedAsciiChars = "{|}\\[~]^\f";
+
+    // Unicode chars in the GSM 7 bit default alphabet and both locking shift tables
+    private static final String sGsmDefaultChars = "\u00a3\u00a5\u00e9\u00c7\u0394\u00c9" +
+            "\u00dc\u00a7\u00fc\u00e0";
+
+    // Unicode chars in the GSM 7 bit default table and Turkish locking shift tables
+    private static final String sGsmDefaultAndTurkishTables = "\u00f9\u00f2\u00c5\u00e5\u00df" +
+            "\u00a4\u00c4\u00d6\u00d1\u00e4\u00f6\u00f1";
+
+    // Unicode chars in the GSM 7 bit default table but not the locking shift tables
+    private static final String sGsmDefaultTableOnly = "\u00e8\u00ec\u00d8\u00f8\u00c6\u00e6" +
+            "\u00a1\u00bf";
+
+    // ASCII chars in the GSM default extension table
+    private static final String sGsmExtendedAsciiChars = "{}[]\f";
+
+    // chars in GSM default extension table and Portuguese locking shift table
+    private static final String sGsmExtendedPortugueseLocking = "^\\|~";
+
+    // Euro currency symbol
     private static final String sGsmExtendedEuroSymbol = "\u20ac";
+
+    // CJK ideographs, Hiragana, Katakana, full width letters, Cyrillic, etc.
     private static final String sUnicodeChars = "\u4e00\u4e01\u4e02\u4e03" +
             "\u4e04\u4e05\u4e06\u4e07\u4e08\u4e09\u4e0a\u4e0b\u4e0c\u4e0d" +
             "\u4e0e\u4e0f\u3041\u3042\u3043\u3044\u3045\u3046\u3047\u3048" +
@@ -43,6 +79,86 @@
             "\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408" +
             "\u00a2\u00a9\u00ae\u2122";
 
+    // chars in Turkish single shift and locking shift tables
+    private static final String sTurkishChars = "\u0131\u011e\u011f\u015e\u015f\u0130";
+
+    // chars in Spanish single shift table and Portuguese single and locking shift tables
+    private static final String sPortugueseAndSpanishChars = "\u00c1\u00e1\u00cd\u00ed"
+            + "\u00d3\u00f3\u00da\u00fa";
+
+    // chars in all national language tables but not in the standard GSM alphabets
+    private static final String sNationalLanguageTablesOnly = "\u00e7";
+
+    // chars in Portuguese single shift and locking shift tables
+    private static final String sPortugueseChars = "\u00ea\u00d4\u00f4\u00c0\u00c2\u00e2"
+            + "\u00ca\u00c3\u00d5\u00e3\u00f5";
+
+    // chars in Portuguese locking shift table only
+    private static final String sPortugueseLockingShiftChars = "\u00aa\u221e\u00ba`";
+
+    // Greek letters in GSM alphabet missing from Portuguese locking and single shift tables
+    private static final String sGreekLettersNotInPortugueseTables = "\u039b\u039e";
+
+    // Greek letters in GSM alphabet and Portuguese single shift (but not locking shift) table
+    private static final String sGreekLettersInPortugueseShiftTable =
+            "\u03a6\u0393\u03a9\u03a0\u03a8\u03a3\u0398";
+
+    // List of classes of characters in SMS tables
+    private static final String[] sCharacterClasses = {
+            sGsmExtendedAsciiChars,
+            sGsmExtendedPortugueseLocking,
+            sGsmDefaultChars,
+            sGsmDefaultAndTurkishTables,
+            sGsmDefaultTableOnly,
+            sGsmExtendedEuroSymbol,
+            sUnicodeChars,
+            sTurkishChars,
+            sPortugueseChars,
+            sPortugueseLockingShiftChars,
+            sPortugueseAndSpanishChars,
+            sGreekLettersNotInPortugueseTables,
+            sGreekLettersInPortugueseShiftTable,
+            sNationalLanguageTablesOnly,
+            sAsciiChars
+    };
+
+    private static final int sNumCharacterClasses = sCharacterClasses.length;
+
+    // For each character class, whether it is present in a particular char table.
+    // First three entries are locking shift tables, followed by four single shift tables
+    private static final boolean[][] sCharClassPresenceInTables = {
+            // ASCII chars in all GSM extension tables
+            {false, false, false, true, true, true, true},
+            // ASCII chars in all GSM extension tables and Portuguese locking shift table
+            {false, false, true, true, true, true, true},
+            // non-ASCII chars in GSM default alphabet and all locking tables
+            {true, true, true, false, false, false, false},
+            // non-ASCII chars in GSM default alphabet and Turkish locking shift table
+            {true, true, false, false, false, false, false},
+            // non-ASCII chars in GSM default alphabet table only
+            {true, false, false, false, false, false, false},
+            // Euro symbol is present in several tables
+            {false, true, true, true, true, true, true},
+            // Unicode characters not present in any 7 bit tables
+            {false, false, false, false, false, false, false},
+            // Characters specific to Turkish language
+            {false, true, false, false, true, false, false},
+            // Characters in Portuguese single shift and locking shift tables
+            {false, false, true, false, false, false, true},
+            // Characters in Portuguese locking shift table only
+            {false, false, true, false, false, false, false},
+            // Chars in Spanish single shift and Portuguese single and locking shift tables
+            {false, false, true, false, false, true, true},
+            // Greek letters in GSM default alphabet missing from Portuguese tables
+            {true, true, false, false, false, false, false},
+            // Greek letters in GSM alphabet and Portuguese single shift table
+            {true, true, false, false, false, false, true},
+            // Chars in all national language tables but not the standard GSM tables
+            {false, true, true, false, true, true, true},
+            // ASCII chars in GSM default alphabet
+            {true, true, true, false, false, false, false}
+    };
+
     private static final int sTestLengthCount = 12;
 
     private static final int[] sSeptetTestLengths =
@@ -60,11 +176,92 @@
     private static final int[] sUnicodeUnitsRemaining =
             { 70,  69,  68, 35,   1,   0,  63,  34,   1,   0,  66,  41};
 
+    // Combinations of enabled GSM national language single shift tables
+    private static final int[][] sEnabledSingleShiftTables = {
+            {},         // GSM default alphabet only
+            {1},        // Turkish (single shift only)
+            {1},        // Turkish (single and locking shift)
+            {2},        // Spanish
+            {3},        // Portuguese (single shift only)
+            {3},        // Portuguese (single and locking shift)
+            {1, 2},     // Turkish + Spanish (single shift only)
+            {1, 2},     // Turkish + Spanish (single and locking shift)
+            {1, 3},     // Turkish + Portuguese (single shift only)
+            {1, 3},     // Turkish + Portuguese (single and locking shift)
+            {2, 3},     // Spanish + Portuguese (single shift only)
+            {2, 3},     // Spanish + Portuguese (single and locking shift)
+            {1, 2, 3},  // Turkish, Spanish, Portuguese (single shift only)
+            {1, 2, 3},  // Turkish, Spanish, Portuguese (single and locking shift)
+            {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13} // all language tables
+    };
+
+    // Combinations of enabled GSM national language locking shift tables
+    private static final int[][] sEnabledLockingShiftTables = {
+            {},         // GSM default alphabet only
+            {},         // Turkish (single shift only)
+            {1},        // Turkish (single and locking shift)
+            {},         // Spanish (no locking shift table)
+            {},         // Portuguese (single shift only)
+            {3},        // Portuguese (single and locking shift)
+            {},         // Turkish + Spanish (single shift only)
+            {1},        // Turkish + Spanish (single and locking shift)
+            {},         // Turkish + Portuguese (single shift only)
+            {1, 3},     // Turkish + Portuguese (single and locking shift)
+            {},         // Spanish + Portuguese (single shift only)
+            {3},        // Spanish + Portuguese (single and locking shift)
+            {},         // Turkish, Spanish, Portuguese (single shift only)
+            {1, 3},     // Turkish, Spanish, Portuguese (single and locking shift)
+            {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13} // all language tables
+    };
+
+    // LanguagePair counter indexes to check for each entry above
+    private static final int[][] sLanguagePairIndexesByEnabledIndex = {
+            {0},                            // default tables only
+            {0, 1},                         // Turkish (single shift only)
+            {0, 1, 4, 5},                   // Turkish (single and locking shift)
+            {0, 2},                         // Spanish
+            {0, 3},                         // Portuguese (single shift only)
+            {0, 3, 8, 11},                  // Portuguese (single and locking shift)
+            {0, 1, 2},                      // Turkish + Spanish (single shift only)
+            {0, 1, 2, 4, 5, 6},             // Turkish + Spanish (single and locking shift)
+            {0, 1, 3},                      // Turkish + Portuguese (single shift only)
+            {0, 1, 3, 4, 5, 7, 8, 9, 11},   // Turkish + Portuguese (single and locking shift)
+            {0, 2, 3},                      // Spanish + Portuguese (single shift only)
+            {0, 2, 3, 8, 10, 11},           // Spanish + Portuguese (single and locking shift)
+            {0, 1, 2, 3},                   // all languages (single shift only)
+            {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, // all languages (single and locking shift)
+            {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}  // all languages (no Indic chars in test)
+    };
+
+    /**
+     * User data header requires one octet for length. Count as one septet, because
+     * all combinations of header elements below will have at least one free bit
+     * when padding to the nearest septet boundary.
+     */
+    private static final int UDH_SEPTET_COST_LENGTH = 1;
+
+    /**
+     * Using a non-default language locking shift table OR single shift table
+     * requires a user data header of 3 octets, or 4 septets, plus UDH length.
+     */
+    private static final int UDH_SEPTET_COST_ONE_SHIFT_TABLE = 4;
+
+    /**
+     * Using a non-default language locking shift table AND single shift table
+     * requires a user data header of 6 octets, or 7 septets, plus UDH length.
+     */
+    private static final int UDH_SEPTET_COST_TWO_SHIFT_TABLES = 7;
+
+    /**
+     * Multi-part messages require a user data header of 5 octets, or 6 septets,
+     * plus UDH length.
+     */
+    private static final int UDH_SEPTET_COST_CONCATENATED_MESSAGE = 6;
 
     @SmallTest
     public void testCalcLengthAscii() throws Exception {
         StringBuilder sb = new StringBuilder(320);
-        int[] values = {0, 0, 0, SmsMessage.ENCODING_7BIT};
+        int[] values = {0, 0, 0, SmsMessage.ENCODING_7BIT, 0, 0};
         int startPos = 0;
         int asciiCharsLen = sAsciiChars.length();
 
@@ -94,20 +291,10 @@
     }
 
     @SmallTest
-    public void testCalcLength7bitGsm() throws Exception {
-        // TODO
-    }
-
-    @SmallTest
-    public void testCalcLength7bitGsmExtended() throws Exception {
-        // TODO
-    }
-
-    @SmallTest
     public void testCalcLengthUnicode() throws Exception {
         StringBuilder sb = new StringBuilder(160);
-        int[] values = {0, 0, 0, SmsMessage.ENCODING_16BIT};
-        int[] values7bit = {1, 0, 0, SmsMessage.ENCODING_7BIT};
+        int[] values = {0, 0, 0, SmsMessage.ENCODING_16BIT, 0, 0};
+        int[] values7bit = {1, 0, 0, SmsMessage.ENCODING_7BIT, 0, 0};
         int startPos = 0;
         int unicodeCharsLen = sUnicodeChars.length();
 
@@ -139,6 +326,229 @@
         }
     }
 
+    private static class LanguagePair {
+        // index is 2 for Portuguese locking shift because there is no Spanish locking shift table
+        private final int langTableIndex;
+        private final int langShiftTableIndex;
+        int length;
+        int missingChars7bit;
+
+        LanguagePair(int langTable, int langShiftTable) {
+            langTableIndex = langTable;
+            langShiftTableIndex = langShiftTable;
+        }
+
+        void clear() {
+            length = 0;
+            missingChars7bit = 0;
+        }
+
+        void addChar(boolean[] charClassTableRow) {
+            if (charClassTableRow[langTableIndex]) {
+                length++;
+            } else if (charClassTableRow[3 + langShiftTableIndex]) {
+                length += 2;
+            } else {
+                length++;    // use ' ' for unmapped char in 7 bit only mode
+                missingChars7bit++;
+            }
+        }
+    }
+
+    private static class CounterHelper {
+        LanguagePair[] mCounters;
+        int[] mStatsCounters;
+        int mUnicodeCounter;
+
+        CounterHelper() {
+            mCounters = new LanguagePair[12];
+            mStatsCounters = new int[12];
+            for (int i = 0; i < 12; i++) {
+                mCounters[i] = new LanguagePair(i/4, i%4);
+            }
+        }
+
+        void clear() {
+            // Note: don't clear stats counters
+            for (int i = 0; i < 12; i++) {
+                mCounters[i].clear();
+            }
+        }
+
+        void addChar(int charClass) {
+            boolean[] charClassTableRow = sCharClassPresenceInTables[charClass];
+            for (int i = 0; i < 12; i++) {
+                mCounters[i].addChar(charClassTableRow);
+            }
+        }
+
+        void fillData(int enabledLangsIndex, boolean use7bitOnly, int[] values, int length) {
+            int[] languagePairs = sLanguagePairIndexesByEnabledIndex[enabledLangsIndex];
+            int minNumSeptets = Integer.MAX_VALUE;
+            int minNumSeptetsWithHeader = Integer.MAX_VALUE;
+            int minNumMissingChars = Integer.MAX_VALUE;
+            int langIndex = -1;
+            int langShiftIndex = -1;
+            for (int i : languagePairs) {
+                LanguagePair pair = mCounters[i];
+                int udhLength = 0;
+                if (i != 0) {
+                    udhLength = UDH_SEPTET_COST_LENGTH;
+                    if (i < 4 || i % 4 == 0) {
+                        udhLength += UDH_SEPTET_COST_ONE_SHIFT_TABLE;
+                    } else {
+                        udhLength += UDH_SEPTET_COST_TWO_SHIFT_TABLES;
+                    }
+                }
+                int numSeptetsWithHeader;
+                if (pair.length > (MAX_USER_DATA_SEPTETS - udhLength)) {
+                    if (udhLength == 0) {
+                        udhLength = UDH_SEPTET_COST_LENGTH;
+                    }
+                    udhLength += UDH_SEPTET_COST_CONCATENATED_MESSAGE;
+                    int septetsPerPart = MAX_USER_DATA_SEPTETS - udhLength;
+                    int msgCount = (pair.length + septetsPerPart - 1) / septetsPerPart;
+                    numSeptetsWithHeader = udhLength * msgCount + pair.length;
+                } else {
+                    numSeptetsWithHeader = udhLength + pair.length;
+                }
+
+                if (use7bitOnly) {
+                    if (pair.missingChars7bit < minNumMissingChars || (pair.missingChars7bit ==
+                            minNumMissingChars && numSeptetsWithHeader < minNumSeptetsWithHeader)) {
+                        minNumSeptets = pair.length;
+                        minNumSeptetsWithHeader = numSeptetsWithHeader;
+                        minNumMissingChars = pair.missingChars7bit;
+                        langIndex = pair.langTableIndex;
+                        langShiftIndex = pair.langShiftTableIndex;
+                    }
+                } else {
+                    if (pair.missingChars7bit == 0 && numSeptetsWithHeader < minNumSeptetsWithHeader) {
+                        minNumSeptets = pair.length;
+                        minNumSeptetsWithHeader = numSeptetsWithHeader;
+                        langIndex = pair.langTableIndex;
+                        langShiftIndex = pair.langShiftTableIndex;
+                    }
+                }
+            }
+            if (langIndex == -1) {
+                // nothing matches, use values for Unicode
+                int byteCount = length * 2;
+                if (byteCount > MAX_USER_DATA_BYTES) {
+                    values[0] = (byteCount + MAX_USER_DATA_BYTES_WITH_HEADER - 1) /
+                            MAX_USER_DATA_BYTES_WITH_HEADER;
+                    values[2] = ((values[0] * MAX_USER_DATA_BYTES_WITH_HEADER) - byteCount) / 2;
+                } else {
+                    values[0] = 1;
+                    values[2] = (MAX_USER_DATA_BYTES - byteCount) / 2;
+                }
+                values[1] = length;
+                values[3] = SmsMessage.ENCODING_16BIT;
+                values[4] = 0;
+                values[5] = 0;
+                mUnicodeCounter++;
+            } else {
+                int udhLength = 0;
+                if (langIndex != 0 || langShiftIndex != 0) {
+                    udhLength = UDH_SEPTET_COST_LENGTH;
+                    if (langIndex == 0 || langShiftIndex == 0) {
+                        udhLength += UDH_SEPTET_COST_ONE_SHIFT_TABLE;
+                    } else {
+                        udhLength += UDH_SEPTET_COST_TWO_SHIFT_TABLES;
+                    }
+                }
+                int msgCount;
+                if (minNumSeptets > (MAX_USER_DATA_SEPTETS - udhLength)) {
+                    if (udhLength == 0) {
+                        udhLength = UDH_SEPTET_COST_LENGTH;
+                    }
+                    udhLength += UDH_SEPTET_COST_CONCATENATED_MESSAGE;
+                    int septetsPerPart = MAX_USER_DATA_SEPTETS - udhLength;
+                    msgCount = (minNumSeptets + septetsPerPart - 1) / septetsPerPart;
+                } else {
+                    msgCount = 1;
+                }
+                values[0] = msgCount;
+                values[1] = minNumSeptets;
+                values[2] = (values[0] * (MAX_USER_DATA_SEPTETS - udhLength)) - minNumSeptets;
+                values[3] = SmsMessage.ENCODING_7BIT;
+                values[4] = (langIndex == 2 ? 3 : langIndex); // Portuguese is code 3, index 2
+                values[5] = langShiftIndex;
+                assertEquals("minNumSeptetsWithHeader", minNumSeptetsWithHeader,
+                        udhLength * msgCount + minNumSeptets);
+                mStatsCounters[langIndex * 4 + langShiftIndex]++;
+            }
+        }
+
+        void printStats() {
+            Log.d(TAG, "Unicode selection count: " + mUnicodeCounter);
+            for (int i = 0; i < 12; i++) {
+                Log.d(TAG, "Language pair index " + i + " count: " + mStatsCounters[i]);
+            }
+        }
+    }
+
+    @LargeTest
+    public void testCalcLengthMixed7bit() throws Exception {
+        StringBuilder sb = new StringBuilder(320);
+        CounterHelper ch = new CounterHelper();
+        Random r = new Random(0x4321);  // use the same seed for reproducibility
+        int[] expectedValues = new int[6];
+        int[] origLockingShiftTables = GsmAlphabet.getEnabledLockingShiftTables();
+        int[] origSingleShiftTables = GsmAlphabet.getEnabledSingleShiftTables();
+        int enabledLanguagesTestCases = sEnabledSingleShiftTables.length;
+        long startTime = System.currentTimeMillis();
+
+        // Repeat for 10 test runs
+        for (int run = 0; run < 10; run++) {
+            sb.setLength(0);
+            ch.clear();
+            int unicodeOnlyCount = 0;
+
+            // Test incrementally from 1 to 320 character random messages
+            for (int i = 1; i < 320; i++) {
+                // 1% chance to add from each special character class, else add an ASCII char
+                int charClass = r.nextInt(100);
+                if (charClass >= sNumCharacterClasses) {
+                    charClass = sNumCharacterClasses - 1;   // last class is ASCII
+                }
+                int classLength = sCharacterClasses[charClass].length();
+                char nextChar = sCharacterClasses[charClass].charAt(r.nextInt(classLength));
+                sb.append(nextChar);
+                ch.addChar(charClass);
+
+//                if (i % 20 == 0) {
+//                    Log.d(TAG, "test string: " + sb);
+//                }
+
+                // Test string against all combinations of enabled languages
+                boolean unicodeOnly = true;
+                for (int j = 0; j < enabledLanguagesTestCases; j++) {
+                    GsmAlphabet.setEnabledSingleShiftTables(sEnabledSingleShiftTables[j]);
+                    GsmAlphabet.setEnabledLockingShiftTables(sEnabledLockingShiftTables[j]);
+                    ch.fillData(j, false, expectedValues, i);
+                    if (expectedValues[3] == SmsMessage.ENCODING_7BIT) {
+                        unicodeOnly = false;
+                    }
+                    callGsmLengthMethods(sb, false, expectedValues);
+                    // test 7 bit only mode
+                    ch.fillData(j, true, expectedValues, i);
+                    callGsmLengthMethods(sb, true, expectedValues);
+                }
+                // after 10 iterations with a Unicode-only string, skip to next test string
+                // so we can spend more time testing strings that do encode into 7 bits.
+                if (unicodeOnly && ++unicodeOnlyCount == 10) {
+//                    Log.d(TAG, "Unicode only: skipping to next test string");
+                    break;
+                }
+            }
+        }
+        ch.printStats();
+        Log.d(TAG, "Completed in " + (System.currentTimeMillis() - startTime) + " ms");
+        GsmAlphabet.setEnabledLockingShiftTables(origLockingShiftTables);
+        GsmAlphabet.setEnabledSingleShiftTables(origSingleShiftTables);
+    }
+
     private void callGsmLengthMethods(CharSequence msgBody, boolean use7bitOnly,
             int[] expectedValues)
     {
@@ -164,6 +574,8 @@
         assertEquals("codeUnitCount",      expectedValues[1], ted.codeUnitCount);
         assertEquals("codeUnitsRemaining", expectedValues[2], ted.codeUnitsRemaining);
         assertEquals("codeUnitSize",       expectedValues[3], ted.codeUnitSize);
+        assertEquals("languageTable",      expectedValues[4], ted.languageTable);
+        assertEquals("languageShiftTable", expectedValues[5], ted.languageShiftTable);
     }
 
     private void callCdmaLengthMethods(CharSequence msgBody, boolean use7bitOnly,
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 13665e1..0129114 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -79,15 +79,22 @@
         new Test("STATUS_BAR_HIDDEN") {
             public void run() {
                 View v = findViewById(android.R.id.list);
-                v.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
                 v.setOnSystemUiVisibilityChangeListener(mOnSystemUiVisibilityChangeListener);
+                v.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
+            }
+        },
+        new Test("STATUS_BAR_VISIBLE") {
+            public void run() {
+                View v = findViewById(android.R.id.list);
+                v.setOnSystemUiVisibilityChangeListener(mOnSystemUiVisibilityChangeListener);
+                v.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
             }
         },
         new Test("no setSystemUiVisibility") {
             public void run() {
                 View v = findViewById(android.R.id.list);
-                v.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
                 v.setOnSystemUiVisibilityChangeListener(null);
+                v.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
             }
         },
         new Test("Double Remove") {