Merge "rsLib cleanup"
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/13.txt b/api/13.txt
index a17b80c..ebb4308 100644
--- a/api/13.txt
+++ b/api/13.txt
@@ -15558,7 +15558,6 @@
 
   public static abstract interface MediaStore.Audio.AudioColumns implements android.provider.MediaStore.MediaColumns {
     field public static final java.lang.String ALBUM = "album";
-    field public static final java.lang.String ALBUM_ART = "album_art";
     field public static final java.lang.String ALBUM_ID = "album_id";
     field public static final java.lang.String ALBUM_KEY = "album_key";
     field public static final java.lang.String ARTIST = "artist";
diff --git a/api/current.txt b/api/current.txt
index 950c130..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
@@ -15630,7 +15631,6 @@
 
   public static abstract interface MediaStore.Audio.AudioColumns implements android.provider.MediaStore.MediaColumns {
     field public static final java.lang.String ALBUM = "album";
-    field public static final java.lang.String ALBUM_ART = "album_art";
     field public static final java.lang.String ALBUM_ID = "album_id";
     field public static final java.lang.String ALBUM_KEY = "album_key";
     field public static final java.lang.String ARTIST = "artist";
@@ -22795,6 +22795,7 @@
     method public java.lang.String getLocale();
     method public java.lang.String getMode();
     method public int getNameResId();
+    method public boolean isAuxiliary();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
   }
@@ -22991,7 +22992,7 @@
 
   public class WebHistoryItem implements java.lang.Cloneable {
     method public android.graphics.Bitmap getFavicon();
-    method public int getId();
+    method public deprecated int getId();
     method public java.lang.String getOriginalUrl();
     method public java.lang.String getTitle();
     method public java.lang.String getUrl();
@@ -23042,13 +23043,13 @@
     method public synchronized java.lang.String getFixedFontFamily();
     method public synchronized boolean getJavaScriptCanOpenWindowsAutomatically();
     method public synchronized boolean getJavaScriptEnabled();
-    method public synchronized android.webkit.WebSettings.LayoutAlgorithm getLayoutAlgorithm();
+    method public deprecated synchronized android.webkit.WebSettings.LayoutAlgorithm getLayoutAlgorithm();
     method public boolean getLightTouchEnabled();
     method public boolean getLoadWithOverviewMode();
     method public synchronized boolean getLoadsImagesAutomatically();
     method public synchronized int getMinimumFontSize();
     method public synchronized int getMinimumLogicalFontSize();
-    method public boolean getNavDump();
+    method public deprecated boolean getNavDump();
     method public synchronized android.webkit.WebSettings.PluginState getPluginState();
     method public deprecated synchronized boolean getPluginsEnabled();
     method public deprecated synchronized java.lang.String getPluginsPath();
@@ -23059,7 +23060,7 @@
     method public synchronized java.lang.String getStandardFontFamily();
     method public synchronized android.webkit.WebSettings.TextSize getTextSize();
     method public deprecated synchronized boolean getUseDoubleTree();
-    method public boolean getUseWebViewBackgroundForOverscrollBackground();
+    method public deprecated boolean getUseWebViewBackgroundForOverscrollBackground();
     method public synchronized boolean getUseWideViewPort();
     method public deprecated synchronized int getUserAgent();
     method public synchronized java.lang.String getUserAgentString();
@@ -23088,13 +23089,13 @@
     method public synchronized void setGeolocationEnabled(boolean);
     method public synchronized void setJavaScriptCanOpenWindowsAutomatically(boolean);
     method public synchronized void setJavaScriptEnabled(boolean);
-    method public synchronized void setLayoutAlgorithm(android.webkit.WebSettings.LayoutAlgorithm);
+    method public deprecated synchronized void setLayoutAlgorithm(android.webkit.WebSettings.LayoutAlgorithm);
     method public void setLightTouchEnabled(boolean);
     method public void setLoadWithOverviewMode(boolean);
     method public synchronized void setLoadsImagesAutomatically(boolean);
     method public synchronized void setMinimumFontSize(int);
     method public synchronized void setMinimumLogicalFontSize(int);
-    method public void setNavDump(boolean);
+    method public deprecated void setNavDump(boolean);
     method public void setNeedInitialFocus(boolean);
     method public synchronized void setPluginState(android.webkit.WebSettings.PluginState);
     method public deprecated synchronized void setPluginsEnabled(boolean);
@@ -23109,7 +23110,7 @@
     method public void setSupportZoom(boolean);
     method public synchronized void setTextSize(android.webkit.WebSettings.TextSize);
     method public deprecated synchronized void setUseDoubleTree(boolean);
-    method public void setUseWebViewBackgroundForOverscrollBackground(boolean);
+    method public deprecated void setUseWebViewBackgroundForOverscrollBackground(boolean);
     method public synchronized void setUseWideViewPort(boolean);
     method public deprecated synchronized void setUserAgent(int);
     method public synchronized void setUserAgentString(java.lang.String);
@@ -23122,7 +23123,7 @@
     field public static final int LOAD_NO_CACHE = 2; // 0x2
   }
 
-  public static final class WebSettings.LayoutAlgorithm extends java.lang.Enum {
+  public static final deprecated class WebSettings.LayoutAlgorithm extends java.lang.Enum {
     method public static android.webkit.WebSettings.LayoutAlgorithm valueOf(java.lang.String);
     method public static final android.webkit.WebSettings.LayoutAlgorithm[] values();
   }
@@ -23200,12 +23201,12 @@
     method public void clearSslPreferences();
     method public void clearView();
     method public android.webkit.WebBackForwardList copyBackForwardList();
-    method public void debugDump();
+    method public deprecated void debugDump();
     method public void destroy();
-    method public static void disablePlatformNotifications();
+    method public static deprecated void disablePlatformNotifications();
     method public void documentHasImages(android.os.Message);
-    method public void emulateShiftHeld();
-    method public static void enablePlatformNotifications();
+    method public deprecated void emulateShiftHeld();
+    method public static deprecated void enablePlatformNotifications();
     method public static java.lang.String findAddress(java.lang.String);
     method public int findAll(java.lang.String);
     method public void findNext(boolean);
@@ -23248,11 +23249,11 @@
     method public void removeJavascriptInterface(java.lang.String);
     method public void requestFocusNodeHref(android.os.Message);
     method public void requestImageRef(android.os.Message);
-    method public boolean restorePicture(android.os.Bundle, java.io.File);
+    method public deprecated boolean restorePicture(android.os.Bundle, java.io.File);
     method public android.webkit.WebBackForwardList restoreState(android.os.Bundle);
     method public void resumeTimers();
     method public void savePassword(java.lang.String, java.lang.String, java.lang.String);
-    method public boolean savePicture(android.os.Bundle, java.io.File);
+    method public deprecated boolean savePicture(android.os.Bundle, java.io.File);
     method public android.webkit.WebBackForwardList saveState(android.os.Bundle);
     method public void saveWebArchive(java.lang.String);
     method public void saveWebArchive(java.lang.String, boolean, android.webkit.ValueCallback<java.lang.String>);
@@ -23263,7 +23264,7 @@
     method public void setInitialScale(int);
     method public void setMapTrackballToArrowKeys(boolean);
     method public void setNetworkAvailable(boolean);
-    method public void setPictureListener(android.webkit.WebView.PictureListener);
+    method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
     method public void setVerticalScrollbarOverlay(boolean);
     method public void setWebChromeClient(android.webkit.WebChromeClient);
     method public void setWebViewClient(android.webkit.WebViewClient);
@@ -23291,8 +23292,8 @@
     field public static final int UNKNOWN_TYPE = 0; // 0x0
   }
 
-  public static abstract interface WebView.PictureListener {
-    method public abstract void onNewPicture(android.webkit.WebView, android.graphics.Picture);
+  public static abstract deprecated interface WebView.PictureListener {
+    method public abstract deprecated void onNewPicture(android.webkit.WebView, android.graphics.Picture);
   }
 
   public class WebView.WebViewTransport {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index b073004..5c8abe4 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -106,6 +106,8 @@
             runDumpHeap();
         } else if (op.equals("monitor")) {
             runMonitor();
+        } else if (op.equals("screen-compat")) {
+            runScreenCompat();
         } else {
             throw new IllegalArgumentException("Unknown command: " + op);
         }
@@ -776,6 +778,29 @@
         controller.run();
     }
 
+    private void runScreenCompat() throws Exception {
+        String mode = nextArgRequired();
+        boolean enabled;
+        if ("on".equals(mode)) {
+            enabled = true;
+        } else if ("off".equals(mode)) {
+            enabled = false;
+        } else {
+            System.err.println("Error: enabled mode must be 'on' or 'off' at " + mode);
+            showUsage();
+            return;
+        }
+
+        String packageName = nextArgRequired();
+        do {
+            try {
+                mAm.setPackageScreenCompatMode(packageName, enabled);
+            } catch (RemoteException e) {
+            }
+            packageName = nextArg();
+        } while (packageName != null);
+    }
+
     private class IntentReceiver extends IIntentReceiver.Stub {
         private boolean mFinished = false;
 
@@ -956,6 +981,8 @@
                 "    start monitoring: am monitor [--gdb <port>]\n" +
                 "        --gdb: start gdbserv on the given port at crash/ANR\n" +
                 "\n" +
+                "    control screen compatibility: am screen-compat [on|off] [package]\n" +
+                "\n" +
                 "    <INTENT> specifications include these flags:\n" +
                 "        [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
                 "        [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
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/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index f987d61..69c4597 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -302,6 +302,7 @@
     glShadeModel(GL_FLAT);
     glDisable(GL_DITHER);
     glDisable(GL_SCISSOR_TEST);
+    glClearColor(0,0,0,1);
     glClear(GL_COLOR_BUFFER_BIT);
     eglSwapBuffers(mDisplay, mSurface);
 
@@ -439,6 +440,7 @@
     glDisable(GL_DITHER);
     glDisable(GL_SCISSOR_TEST);
     glDisable(GL_BLEND);
+    glClearColor(0,0,0,1);
     glClear(GL_COLOR_BUFFER_BIT);
 
     eglSwapBuffers(mDisplay, mSurface);
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/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 22dd3c7..adfda8e 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -48,6 +48,18 @@
  * with setting up the basic properties of the animations used in these four situations,
  * or with setting up custom animations for any or all of the four.</p>
  *
+ * <p>By default, the DISAPPEARING animation begins immediately, as does the CHANGE_APPEARING
+ * animation. The other animations begin after a delay that is set to the default duration
+ * of the animations. This behavior facilitates a sequence of animations in transitions as
+ * follows: when an item is being added to a layout, the other children of that container will
+ * move first (thus creating space for the new item), then the appearing animation will run to
+ * animate the item being added. Conversely, when an item is removed from a container, the
+ * animation to remove it will run first, then the animations of the other children in the
+ * layout will run (closing the gap created in the layout when the item was removed). If this
+ * default choreography behavior is not desired, the {@link #setDuration(int, long)} and
+ * {@link #setStartDelay(int, long)} of any or all of the animations can be changed as
+ * appropriate.</p>
+ *
  * <p>The animations specified for the transition, both the defaults and any custom animations
  * set on the transition object, are templates only. That is, these animations exist to hold the
  * basic animation properties, such as the duration, start delay, and properties being animated.
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 4b09b34c..5ffaee7 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1397,6 +1397,16 @@
             return true;
         }
 
+        case SET_PACKAGE_SCREEN_COMPAT_MODE_TRANSACTION:
+        {
+            data.enforceInterface(IActivityManager.descriptor);
+            String pkg = data.readString();
+            boolean enabled = data.readInt() != 0;
+            setPackageScreenCompatMode(pkg, enabled);
+            reply.writeNoException();
+            return true;
+        }
+        
         case SWITCH_USER_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int userid = data.readInt();
@@ -3172,6 +3182,19 @@
         return result;
     }
 
+    public void setPackageScreenCompatMode(String packageName, boolean compatEnabled)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(packageName);
+        data.writeInt(compatEnabled ? 1 : 0);
+        mRemote.transact(SET_PACKAGE_SCREEN_COMPAT_MODE_TRANSACTION, data, reply, 0);
+        reply.readException();
+        reply.recycle();
+        data.recycle();
+    }
+
     public boolean switchUser(int userid) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 79552bf..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(";");
@@ -202,7 +202,7 @@
 
     Bundle mCoreSettings = null;
 
-    private static final class ActivityClientRecord {
+    static final class ActivityClientRecord {
         IBinder token;
         int ident;
         Intent intent;
@@ -220,6 +220,7 @@
         ActivityClientRecord nextIdle;
 
         ActivityInfo activityInfo;
+        CompatibilityInfo compatInfo;
         LoadedApk packageInfo;
 
         List<ResultInfo> pendingResults;
@@ -260,7 +261,7 @@
         }
     }
 
-    private final class ProviderClientRecord implements IBinder.DeathRecipient {
+    final class ProviderClientRecord implements IBinder.DeathRecipient {
         final String mName;
         final IContentProvider mProvider;
         final ContentProvider mLocalProvider;
@@ -277,7 +278,7 @@
         }
     }
 
-    private static final class NewIntentData {
+    static final class NewIntentData {
         List<Intent> intents;
         IBinder token;
         public String toString() {
@@ -285,7 +286,7 @@
         }
     }
 
-    private static final class ReceiverData extends BroadcastReceiver.PendingResult {
+    static final class ReceiverData extends BroadcastReceiver.PendingResult {
         public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras,
                 boolean ordered, boolean sticky, IBinder token) {
             super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, token);
@@ -294,6 +295,7 @@
 
         Intent intent;
         ActivityInfo info;
+        CompatibilityInfo compatInfo;
         public String toString() {
             return "ReceiverData{intent=" + intent + " packageName=" +
                     info.packageName + " resultCode=" + getResultCode()
@@ -302,8 +304,9 @@
         }
     }
 
-    private static final class CreateBackupAgentData {
+    static final class CreateBackupAgentData {
         ApplicationInfo appInfo;
+        CompatibilityInfo compatInfo;
         int backupMode;
         public String toString() {
             return "CreateBackupAgentData{appInfo=" + appInfo
@@ -312,9 +315,10 @@
         }
     }
 
-    private static final class CreateServiceData {
+    static final class CreateServiceData {
         IBinder token;
         ServiceInfo info;
+        CompatibilityInfo compatInfo;
         Intent intent;
         public String toString() {
             return "CreateServiceData{token=" + token + " className="
@@ -323,7 +327,7 @@
         }
     }
 
-    private static final class BindServiceData {
+    static final class BindServiceData {
         IBinder token;
         Intent intent;
         boolean rebind;
@@ -332,7 +336,7 @@
         }
     }
 
-    private static final class ServiceArgsData {
+    static final class ServiceArgsData {
         IBinder token;
         boolean taskRemoved;
         int startId;
@@ -344,7 +348,7 @@
         }
     }
 
-    private static final class AppBindData {
+    static final class AppBindData {
         LoadedApk info;
         String processName;
         ApplicationInfo appInfo;
@@ -356,20 +360,21 @@
         int debugMode;
         boolean restrictedBackupMode;
         Configuration config;
+        CompatibilityInfo compatInfo;
         boolean handlingProfiling;
         public String toString() {
             return "AppBindData{appInfo=" + appInfo + "}";
         }
     }
 
-    private static final class DumpComponentInfo {
+    static final class DumpComponentInfo {
         ParcelFileDescriptor fd;
         IBinder token;
         String prefix;
         String[] args;
     }
 
-    private static final class ResultData {
+    static final class ResultData {
         IBinder token;
         List<ResultInfo> results;
         public String toString() {
@@ -377,22 +382,27 @@
         }
     }
 
-    private static final class ContextCleanupInfo {
+    static final class ContextCleanupInfo {
         ContextImpl context;
         String what;
         String who;
     }
 
-    private static final class ProfilerControlData {
+    static final class ProfilerControlData {
         String path;
         ParcelFileDescriptor fd;
     }
 
-    private static final class DumpHeapData {
+    static final class DumpHeapData {
         String path;
         ParcelFileDescriptor fd;
     }
 
+    static final class UpdateCompatibilityData {
+        String pkg;
+        CompatibilityInfo info;
+    }
+    
     native private void dumpGraphicsInfo(FileDescriptor fd);
 
     private final class ApplicationThread extends ApplicationThreadNative {
@@ -445,7 +455,8 @@
         // we use token to identify this activity without having to send the
         // activity itself back to the activity manager. (matters more with ipc)
         public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
-                ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
+                ActivityInfo info, CompatibilityInfo compatInfo, Bundle state,
+                List<ResultInfo> pendingResults,
                 List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) {
             ActivityClientRecord r = new ActivityClientRecord();
 
@@ -453,6 +464,7 @@
             r.ident = ident;
             r.intent = intent;
             r.activityInfo = info;
+            r.compatInfo = compatInfo;
             r.state = state;
 
             r.pendingResults = pendingResults;
@@ -486,33 +498,40 @@
         }
 
         public final void scheduleReceiver(Intent intent, ActivityInfo info,
-                int resultCode, String data, Bundle extras, boolean sync) {
+                CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
+                boolean sync) {
             ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                     sync, false, mAppThread.asBinder());
             r.info = info;
+            r.compatInfo = compatInfo;
             queueOrSendMessage(H.RECEIVER, r);
         }
 
-        public final void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) {
+        public final void scheduleCreateBackupAgent(ApplicationInfo app,
+                CompatibilityInfo compatInfo, int backupMode) {
             CreateBackupAgentData d = new CreateBackupAgentData();
             d.appInfo = app;
+            d.compatInfo = compatInfo;
             d.backupMode = backupMode;
 
             queueOrSendMessage(H.CREATE_BACKUP_AGENT, d);
         }
 
-        public final void scheduleDestroyBackupAgent(ApplicationInfo app) {
+        public final void scheduleDestroyBackupAgent(ApplicationInfo app,
+                CompatibilityInfo compatInfo) {
             CreateBackupAgentData d = new CreateBackupAgentData();
             d.appInfo = app;
+            d.compatInfo = compatInfo;
 
             queueOrSendMessage(H.DESTROY_BACKUP_AGENT, d);
         }
 
         public final void scheduleCreateService(IBinder token,
-                ServiceInfo info) {
+                ServiceInfo info, CompatibilityInfo compatInfo) {
             CreateServiceData s = new CreateServiceData();
             s.token = token;
             s.info = info;
+            s.compatInfo = compatInfo;
 
             queueOrSendMessage(H.CREATE_SERVICE, s);
         }
@@ -556,7 +575,8 @@
                 ComponentName instrumentationName, String profileFile,
                 Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
                 int debugMode, boolean isRestrictedBackupMode, Configuration config,
-                Map<String, IBinder> services, Bundle coreSettings) {
+                CompatibilityInfo compatInfo, Map<String, IBinder> services,
+                Bundle coreSettings) {
 
             if (services != null) {
                 // Setup the service cache in the ServiceManager
@@ -576,6 +596,7 @@
             data.debugMode = debugMode;
             data.restrictedBackupMode = isRestrictedBackupMode;
             data.config = config;
+            data.compatInfo = compatInfo;
             queueOrSendMessage(H.BIND_APPLICATION, data);
         }
 
@@ -897,6 +918,13 @@
         public void setCoreSettings(Bundle coreSettings) {
             queueOrSendMessage(H.SET_CORE_SETTINGS, coreSettings);
         }
+
+        public void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) {
+            UpdateCompatibilityData ucd = new UpdateCompatibilityData();
+            ucd.pkg = pkg;
+            ucd.info = info;
+            queueOrSendMessage(H.UPDATE_PACKAGE_COMPATIBILITY_INFO, ucd);
+        }
     }
 
     private final class H extends Handler {
@@ -939,6 +967,7 @@
         public static final int DUMP_ACTIVITY           = 136;
         public static final int SLEEPING                = 137;
         public static final int SET_CORE_SETTINGS       = 138;
+        public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139;
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
                 switch (code) {
@@ -981,6 +1010,7 @@
                     case DUMP_ACTIVITY: return "DUMP_ACTIVITY";
                     case SLEEPING: return "SLEEPING";
                     case SET_CORE_SETTINGS: return "SET_CORE_SETTINGS";
+                    case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO";
                 }
             }
             return "(unknown)";
@@ -992,7 +1022,7 @@
                     ActivityClientRecord r = (ActivityClientRecord)msg.obj;
 
                     r.packageInfo = getPackageInfoNoCheck(
-                            r.activityInfo.applicationInfo);
+                            r.activityInfo.applicationInfo, r.compatInfo);
                     handleLaunchActivity(r, null);
                 } break;
                 case RELAUNCH_ACTIVITY: {
@@ -1066,7 +1096,7 @@
                     handleRequestThumbnail((IBinder)msg.obj);
                     break;
                 case CONFIGURATION_CHANGED:
-                    handleConfigurationChanged((Configuration)msg.obj);
+                    handleConfigurationChanged((Configuration)msg.obj, null);
                     break;
                 case CLEAN_UP_CONTEXT:
                     ContextCleanupInfo cci = (ContextCleanupInfo)msg.obj;
@@ -1119,6 +1149,8 @@
                 case SET_CORE_SETTINGS:
                     handleSetCoreSettings((Bundle) msg.obj);
                     break;
+                case UPDATE_PACKAGE_COMPATIBILITY_INFO:
+                    handleUpdatePackageCompatibilityInfo((UpdateCompatibilityData)msg.obj);
             }
             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
         }
@@ -1329,7 +1361,8 @@
         return mH;
     }
 
-    public final LoadedApk getPackageInfo(String packageName, int flags) {
+    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
+            int flags) {
         synchronized (mPackages) {
             WeakReference<LoadedApk> ref;
             if ((flags&Context.CONTEXT_INCLUDE_CODE) != 0) {
@@ -1363,13 +1396,14 @@
         }
 
         if (ai != null) {
-            return getPackageInfo(ai, flags);
+            return getPackageInfo(ai, compatInfo, flags);
         }
 
         return null;
     }
 
-    public final LoadedApk getPackageInfo(ApplicationInfo ai, int flags) {
+    public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
+            int flags) {
         boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
         boolean securityViolation = includeCode && ai.uid != 0
                 && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
@@ -1388,14 +1422,27 @@
                 throw new SecurityException(msg);
             }
         }
-        return getPackageInfo(ai, null, securityViolation, includeCode);
+        return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode);
     }
 
-    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai) {
-        return getPackageInfo(ai, null, false, true);
+    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
+            CompatibilityInfo compatInfo) {
+        return getPackageInfo(ai, compatInfo, null, false, true);
     }
 
-    private final LoadedApk getPackageInfo(ApplicationInfo aInfo,
+    public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
+        synchronized (mPackages) {
+            WeakReference<LoadedApk> ref;
+            if (includeCode) {
+                ref = mPackages.get(packageName);
+            } else {
+                ref = mResourcePackages.get(packageName);
+            }
+            return ref != null ? ref.get() : null;
+        }
+    }
+
+    private final LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
             ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
         synchronized (mPackages) {
             WeakReference<LoadedApk> ref;
@@ -1413,7 +1460,7 @@
                                 ? mBoundApplication.processName : null)
                         + ")");
                 packageInfo =
-                    new LoadedApk(this, aInfo, this, baseLoader,
+                    new LoadedApk(this, aInfo, compatInfo, this, baseLoader,
                             securityViolation, includeCode &&
                             (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
                 if (includeCode) {
@@ -1470,7 +1517,8 @@
             if (mSystemContext == null) {
                 ContextImpl context =
                     ContextImpl.createSystemContext(this);
-                LoadedApk info = new LoadedApk(this, "android", context, null);
+                LoadedApk info = new LoadedApk(this, "android", context, null,
+                        CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
                 context.init(info, null, this);
                 context.getResources().updateConfiguration(
                         getConfiguration(), getDisplayMetricsLocked(false));
@@ -1485,7 +1533,8 @@
     public void installSystemApplicationInfo(ApplicationInfo info) {
         synchronized (this) {
             ContextImpl context = getSystemContext();
-            context.init(new LoadedApk(this, "android", context, info), null, this);
+            context.init(new LoadedApk(this, "android", context, info,
+                    new CompatibilityInfo(info, 0, false)), null, this);
         }
     }
 
@@ -1635,7 +1684,7 @@
 
         ActivityInfo aInfo = r.activityInfo;
         if (r.packageInfo == null) {
-            r.packageInfo = getPackageInfo(aInfo.applicationInfo,
+            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                     Context.CONTEXT_INCLUDE_CODE);
         }
 
@@ -1859,7 +1908,7 @@
         String component = data.intent.getComponent().getClassName();
 
         LoadedApk packageInfo = getPackageInfoNoCheck(
-                data.info.applicationInfo);
+                data.info.applicationInfo, data.compatInfo);
 
         IActivityManager mgr = ActivityManagerNative.getDefault();
 
@@ -1920,7 +1969,7 @@
         unscheduleGcIdler();
 
         // instantiate the BackupAgent class named in the manifest
-        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo);
+        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
         String packageName = packageInfo.mPackageName;
         if (mBackupAgents.get(packageName) != null) {
             Slog.d(TAG, "BackupAgent " + "  for " + packageName
@@ -1930,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);
@@ -1974,7 +2025,7 @@
             }
         } catch (Exception e) {
             throw new RuntimeException("Unable to create BackupAgent "
-                    + data.appInfo.backupAgentName + ": " + e.toString(), e);
+                    + classname + ": " + e.toString(), e);
         }
     }
 
@@ -1982,7 +2033,7 @@
     private final void handleDestroyBackupAgent(CreateBackupAgentData data) {
         if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data);
 
-        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo);
+        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
         String packageName = packageInfo.mPackageName;
         BackupAgent agent = mBackupAgents.get(packageName);
         if (agent != null) {
@@ -2004,7 +2055,7 @@
         unscheduleGcIdler();
 
         LoadedApk packageInfo = getPackageInfoNoCheck(
-                data.info.applicationInfo);
+                data.info.applicationInfo, data.compatInfo);
         Service service = null;
         try {
             java.lang.ClassLoader cl = packageInfo.getClassLoader();
@@ -2721,6 +2772,18 @@
         }
     }
 
+    private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) {
+        LoadedApk apk = peekPackageInfo(data.pkg, false);
+        if (apk != null) {
+            apk.mCompatibilityInfo = data.info;
+        }
+        apk = peekPackageInfo(data.pkg, true);
+        if (apk != null) {
+            apk.mCompatibilityInfo = data.info;
+        }
+        handleConfigurationChanged(mConfiguration, data.info);
+    }
+
     private final void deliverResults(ActivityClientRecord r, List<ResultInfo> results) {
         final int N = results.size();
         for (int i=0; i<N; i++) {
@@ -3058,7 +3121,7 @@
         
         // If there was a pending configuration change, execute it first.
         if (changedConfig != null) {
-            handleConfigurationChanged(changedConfig);
+            handleConfigurationChanged(changedConfig, null);
         }
 
         ActivityClientRecord r = mActivities.get(tmp.token);
@@ -3228,11 +3291,12 @@
         }
     }
 
-    final boolean applyConfigurationToResourcesLocked(Configuration config) {
+    final boolean applyConfigurationToResourcesLocked(Configuration config,
+            CompatibilityInfo compat) {
         if (mResConfiguration == null) {
             mResConfiguration = new Configuration();
         }
-        if (!mResConfiguration.isOtherSeqNewer(config)) {
+        if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
                     + mResConfiguration.seq + ", newSeq=" + config.seq);
             return false;
@@ -3245,7 +3309,7 @@
             Locale.setDefault(config.locale);
         }
 
-        Resources.updateSystemConfiguration(config, dm);
+        Resources.updateSystemConfiguration(config, dm, compat);
 
         ApplicationPackageManager.configurationChanged();
         //Slog.i(TAG, "Configuration changed in " + currentPackageName());
@@ -3260,7 +3324,7 @@
             if (r != null) {
                 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
                         + r + " config to: " + config);
-                r.updateConfiguration(config, dm);
+                r.updateConfiguration(config, dm, compat);
                 //Slog.i(TAG, "Updated app resources " + v.getKey()
                 //        + " " + r + ": " + r.getConfiguration());
             } else {
@@ -3272,7 +3336,7 @@
         return changes != 0;
     }
     
-    final void handleConfigurationChanged(Configuration config) {
+    final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
 
         ArrayList<ComponentCallbacks> callbacks = null;
 
@@ -3291,15 +3355,21 @@
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "
                     + config);
         
-            applyConfigurationToResourcesLocked(config);
+            applyConfigurationToResourcesLocked(config, compat);
             
             if (mConfiguration == null) {
                 mConfiguration = new Configuration();
             }
-            if (!mConfiguration.isOtherSeqNewer(config)) {
+            if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
                 return;
             }
             mConfiguration.updateFrom(config);
+            if (compat != null) {
+                // Can't do this here, because it causes us to report the
+                // comatible config back to the am as the current config
+                // of the activity, and much unhappiness results.
+                //compat.applyToConfiguration(mConfiguration);
+            }
 
             callbacks = collectComponentCallbacksLocked(false, config);
         }
@@ -3446,9 +3516,10 @@
          * reflect configuration changes. The configuration object passed
          * in AppBindData can be safely assumed to be up to date
          */
-        Resources.getSystem().updateConfiguration(mConfiguration, null);
+        Resources.getSystem().updateConfiguration(mConfiguration,
+                Resources.getSystem().getDisplayMetrics(), data.compatInfo);
 
-        data.info = getPackageInfoNoCheck(data.appInfo);
+        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
 
         /**
          * For system applications on userdebug/eng builds, log stack
@@ -3540,7 +3611,7 @@
             instrApp.publicSourceDir = ii.publicSourceDir;
             instrApp.dataDir = ii.dataDir;
             instrApp.nativeLibraryDir = ii.nativeLibraryDir;
-            LoadedApk pi = getPackageInfo(instrApp,
+            LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                     appContext.getClassLoader(), false, true);
             ContextImpl instrContext = new ContextImpl();
             instrContext.init(pi, null, this);
@@ -3954,7 +4025,7 @@
                     // We need to apply this change to the resources
                     // immediately, because upon returning the view
                     // hierarchy will be informed about it.
-                    if (applyConfigurationToResourcesLocked(newConfig)) {
+                    if (applyConfigurationToResourcesLocked(newConfig, null)) {
                         // This actually changed the resources!  Tell
                         // everyone about it.
                         if (mPendingConfiguration == null ||
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 0e511f2..dc0f529 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -23,6 +23,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ServiceInfo;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.Bundle;
@@ -131,12 +132,13 @@
             IBinder b = data.readStrongBinder();
             int ident = data.readInt();
             ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
+            CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
             Bundle state = data.readBundle();
             List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
             List<Intent> pi = data.createTypedArrayList(Intent.CREATOR);
             boolean notResumed = data.readInt() != 0;
             boolean isForward = data.readInt() != 0;
-            scheduleLaunchActivity(intent, b, ident, info, state, ri, pi,
+            scheduleLaunchActivity(intent, b, ident, info, compatInfo, state, ri, pi,
                     notResumed, isForward);
             return true;
         }
@@ -181,11 +183,12 @@
             data.enforceInterface(IApplicationThread.descriptor);
             Intent intent = Intent.CREATOR.createFromParcel(data);
             ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
+            CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
             int resultCode = data.readInt();
             String resultData = data.readString();
             Bundle resultExtras = data.readBundle();
             boolean sync = data.readInt() != 0;
-            scheduleReceiver(intent, info, resultCode, resultData,
+            scheduleReceiver(intent, info, compatInfo, resultCode, resultData,
                     resultExtras, sync);
             return true;
         }
@@ -194,7 +197,8 @@
             data.enforceInterface(IApplicationThread.descriptor);
             IBinder token = data.readStrongBinder();
             ServiceInfo info = ServiceInfo.CREATOR.createFromParcel(data);
-            scheduleCreateService(token, info);
+            CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
+            scheduleCreateService(token, info, compatInfo);
             return true;
         }
 
@@ -257,12 +261,13 @@
             int testMode = data.readInt();
             boolean restrictedBackupMode = (data.readInt() != 0);
             Configuration config = Configuration.CREATOR.createFromParcel(data);
+            CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
             HashMap<String, IBinder> services = data.readHashMap(null);
             Bundle coreSettings = data.readBundle();
             bindApplication(packageName, info,
                             providers, testName, profileName,
                             testArgs, testWatcher, testMode, restrictedBackupMode,
-                            config, services, coreSettings);
+                            config, compatInfo, services, coreSettings);
             return true;
         }
         
@@ -390,8 +395,9 @@
         {
             data.enforceInterface(IApplicationThread.descriptor);
             ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data);
+            CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
             int backupMode = data.readInt();
-            scheduleCreateBackupAgent(appInfo, backupMode);
+            scheduleCreateBackupAgent(appInfo, compatInfo, backupMode);
             return true;
         }
 
@@ -399,7 +405,8 @@
         {
             data.enforceInterface(IApplicationThread.descriptor);
             ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data);
-            scheduleDestroyBackupAgent(appInfo);
+            CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
+            scheduleDestroyBackupAgent(appInfo, compatInfo);
             return true;
         }
 
@@ -457,12 +464,20 @@
             return true;
         }
 
-        case SET_CORE_SETTINGS: {
+        case SET_CORE_SETTINGS_TRANSACTION: {
             data.enforceInterface(IApplicationThread.descriptor);
             Bundle settings = data.readBundle();
             setCoreSettings(settings);
             return true;
         }
+
+        case UPDATE_PACKAGE_COMPATIBILITY_INFO_TRANSACTION: {
+            data.enforceInterface(IApplicationThread.descriptor);
+            String pkg = data.readString();
+            CompatibilityInfo compat = CompatibilityInfo.CREATOR.createFromParcel(data);
+            updatePackageCompatibilityInfo(pkg, compat);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -555,7 +570,8 @@
     }
 
     public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
-            ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
+            ActivityInfo info, CompatibilityInfo compatInfo, Bundle state,
+            List<ResultInfo> pendingResults,
     		List<Intent> pendingNewIntents, boolean notResumed, boolean isForward)
     		throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -564,6 +580,7 @@
         data.writeStrongBinder(token);
         data.writeInt(ident);
         info.writeToParcel(data, 0);
+        compatInfo.writeToParcel(data, 0);
         data.writeBundle(state);
         data.writeTypedList(pendingResults);
         data.writeTypedList(pendingNewIntents);
@@ -620,12 +637,13 @@
     }
     
     public final void scheduleReceiver(Intent intent, ActivityInfo info,
-            int resultCode, String resultData,
+            CompatibilityInfo compatInfo, int resultCode, String resultData,
             Bundle map, boolean sync) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         intent.writeToParcel(data, 0);
         info.writeToParcel(data, 0);
+        compatInfo.writeToParcel(data, 0);
         data.writeInt(resultCode);
         data.writeString(resultData);
         data.writeBundle(map);
@@ -635,32 +653,36 @@
         data.recycle();
     }
 
-    public final void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode)
-            throws RemoteException {
+    public final void scheduleCreateBackupAgent(ApplicationInfo app,
+            CompatibilityInfo compatInfo, int backupMode) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         app.writeToParcel(data, 0);
+        compatInfo.writeToParcel(data, 0);
         data.writeInt(backupMode);
         mRemote.transact(SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
     }
 
-    public final void scheduleDestroyBackupAgent(ApplicationInfo app) throws RemoteException {
+    public final void scheduleDestroyBackupAgent(ApplicationInfo app,
+            CompatibilityInfo compatInfo) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         app.writeToParcel(data, 0);
+        compatInfo.writeToParcel(data, 0);
         mRemote.transact(SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
     }
     
-    public final void scheduleCreateService(IBinder token, ServiceInfo info)
-            throws RemoteException {
+    public final void scheduleCreateService(IBinder token, ServiceInfo info,
+            CompatibilityInfo compatInfo) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
         info.writeToParcel(data, 0);
+        compatInfo.writeToParcel(data, 0);
         mRemote.transact(SCHEDULE_CREATE_SERVICE_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
@@ -721,7 +743,7 @@
     public final void bindApplication(String packageName, ApplicationInfo info,
             List<ProviderInfo> providers, ComponentName testName,
             String profileName, Bundle testArgs, IInstrumentationWatcher testWatcher, int debugMode,
-            boolean restrictedBackupMode, Configuration config,
+            boolean restrictedBackupMode, Configuration config, CompatibilityInfo compatInfo,
             Map<String, IBinder> services, Bundle coreSettings) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -740,6 +762,7 @@
         data.writeInt(debugMode);
         data.writeInt(restrictedBackupMode ? 1 : 0);
         config.writeToParcel(data, 0);
+        compatInfo.writeToParcel(data, 0);
         data.writeMap(services);
         data.writeBundle(coreSettings);
         mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
@@ -954,6 +977,16 @@
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeBundle(coreSettings);
-        mRemote.transact(SET_CORE_SETTINGS, data, null, IBinder.FLAG_ONEWAY);
+        mRemote.transact(SET_CORE_SETTINGS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+    }
+
+    public void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeString(pkg);
+        info.writeToParcel(data, 0);
+        mRemote.transact(UPDATE_PACKAGE_COMPATIBILITY_INFO_TRANSACTION, data, null,
+                IBinder.FLAG_ONEWAY);
     }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index cc1f81c..36b9d72 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1372,7 +1372,7 @@
         }
 
         LoadedApk pi =
-            mMainThread.getPackageInfo(packageName, flags);
+            mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags);
         if (pi != null) {
             ContextImpl c = new ContextImpl();
             c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
@@ -1454,7 +1454,7 @@
                         " compatiblity info:" + container.getDisplayMetrics());
             }
             mResources = mainThread.getTopLevelResources(
-                    mPackageInfo.getResDir(), container.getCompatibilityInfo().copy());
+                    mPackageInfo.getResDir(), container.getCompatibilityInfo());
         }
         mMainThread = mainThread;
         mContentResolver = new ApplicationContentResolver(this, mainThread);
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/IActivityManager.java b/core/java/android/app/IActivityManager.java
index bec697a..994a924 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -342,6 +342,9 @@
     public int startActivitiesInPackage(int uid,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException;
 
+    public void setPackageScreenCompatMode(String packageName, boolean compatEnabled)
+            throws RemoteException;
+    
     // Multi-user APIs
     public boolean switchUser(int userid) throws RemoteException;
     
@@ -564,7 +567,8 @@
     int START_ACTIVITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+120;
     int START_ACTIVITIES_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+121;
     int ACTIVITY_SLEPT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+122;
-    int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+123;
-    int REMOVE_SUB_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+124;
-    int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+125;
+    int SET_PACKAGE_SCREEN_COMPAT_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+123;
+    int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+124;
+    int REMOVE_SUB_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+125;
+    int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+126;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index b29b088..8c31559 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -23,6 +23,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ServiceInfo;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.Debug;
@@ -52,7 +53,8 @@
     void scheduleResumeActivity(IBinder token, boolean isForward) throws RemoteException;
     void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException;
     void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
-            ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
+            ActivityInfo info, CompatibilityInfo compatInfo, Bundle state,
+            List<ResultInfo> pendingResults,
     		List<Intent> pendingNewIntents, boolean notResumed, boolean isForward)
     		throws RemoteException;
     void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults,
@@ -61,14 +63,17 @@
     void scheduleNewIntent(List<Intent> intent, IBinder token) throws RemoteException;
     void scheduleDestroyActivity(IBinder token, boolean finished,
             int configChanges) throws RemoteException;
-    void scheduleReceiver(Intent intent, ActivityInfo info, int resultCode,
-            String data, Bundle extras, boolean sync) throws RemoteException;
+    void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo,
+            int resultCode, String data, Bundle extras, boolean sync) throws RemoteException;
     static final int BACKUP_MODE_INCREMENTAL = 0;
     static final int BACKUP_MODE_FULL = 1;
     static final int BACKUP_MODE_RESTORE = 2;
-    void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) throws RemoteException;
-    void scheduleDestroyBackupAgent(ApplicationInfo app) throws RemoteException;
-    void scheduleCreateService(IBinder token, ServiceInfo info) throws RemoteException;
+    void scheduleCreateBackupAgent(ApplicationInfo app, CompatibilityInfo compatInfo,
+            int backupMode) throws RemoteException;
+    void scheduleDestroyBackupAgent(ApplicationInfo app, CompatibilityInfo compatInfo)
+            throws RemoteException;
+    void scheduleCreateService(IBinder token, ServiceInfo info,
+            CompatibilityInfo compatInfo) throws RemoteException;
     void scheduleBindService(IBinder token,
             Intent intent, boolean rebind) throws RemoteException;
     void scheduleUnbindService(IBinder token,
@@ -82,7 +87,7 @@
     void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers,
             ComponentName testName, String profileName, Bundle testArguments, 
             IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode,
-            Configuration config, Map<String, IBinder> services,
+            Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
             Bundle coreSettings) throws RemoteException;
     void scheduleExit() throws RemoteException;
     void scheduleSuicide() throws RemoteException;
@@ -112,6 +117,7 @@
     void dumpActivity(FileDescriptor fd, IBinder servicetoken, String prefix, String[] args)
             throws RemoteException;
     void setCoreSettings(Bundle coreSettings) throws RemoteException;
+    void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) throws RemoteException;
 
     String descriptor = "android.app.IApplicationThread";
 
@@ -153,5 +159,6 @@
     int DUMP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36;
     int CLEAR_DNS_CACHE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+37;
     int SET_HTTP_PROXY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+38;
-    int SET_CORE_SETTINGS = IBinder.FIRST_CALL_TRANSACTION+39;
+    int SET_CORE_SETTINGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+39;
+    int UPDATE_PACKAGE_COMPATIBILITY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+40;
 }
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/LoadedApk.java b/core/java/android/app/LoadedApk.java
index c406524..5307696 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -99,6 +99,7 @@
     }
 
     public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
+            CompatibilityInfo compatInfo,
             ActivityThread mainThread, ClassLoader baseLoader,
             boolean securityViolation, boolean includeCode) {
         mActivityThread = activityThread;
@@ -114,7 +115,7 @@
         mBaseClassLoader = baseLoader;
         mSecurityViolation = securityViolation;
         mIncludeCode = includeCode;
-        mCompatibilityInfo = new CompatibilityInfo(aInfo);
+        mCompatibilityInfo = compatInfo;
 
         if (mAppDir == null) {
             if (ActivityThread.mSystemContext == null) {
@@ -122,7 +123,8 @@
                     ContextImpl.createSystemContext(mainThread);
                 ActivityThread.mSystemContext.getResources().updateConfiguration(
                          mainThread.getConfiguration(),
-                         mainThread.getDisplayMetricsLocked(false));
+                         mainThread.getDisplayMetricsLocked(false),
+                         compatInfo);
                 //Slog.i(TAG, "Created system resources "
                 //        + mSystemContext.getResources() + ": "
                 //        + mSystemContext.getResources().getConfiguration());
@@ -133,7 +135,7 @@
     }
 
     public LoadedApk(ActivityThread activityThread, String name,
-            Context systemContext, ApplicationInfo info) {
+            Context systemContext, ApplicationInfo info, CompatibilityInfo compatInfo) {
         mActivityThread = activityThread;
         mApplicationInfo = info != null ? info : new ApplicationInfo();
         mApplicationInfo.packageName = name;
@@ -149,7 +151,7 @@
         mIncludeCode = true;
         mClassLoader = systemContext.getClassLoader();
         mResources = systemContext.getResources();
-        mCompatibilityInfo = new CompatibilityInfo(mApplicationInfo);
+        mCompatibilityInfo = compatInfo;
     }
 
     public String getPackageName() {
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/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index e403ac2..ab9bfce 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -21,9 +21,9 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
@@ -34,32 +34,27 @@
  * 
  *  {@hide} 
  */
-public class CompatibilityInfo {
-    private static final boolean DBG = false;
-    private static final String TAG = "CompatibilityInfo";
-    
+public class CompatibilityInfo implements Parcelable {
     /** default compatibility info object for compatible applications */
     public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo() {
-        @Override
-        public void setExpandable(boolean expandable) {
-            throw new UnsupportedOperationException("trying to change default compatibility info");
-        }
     };
 
     /**
-     * The default width of the screen in portrait mode. 
+     * This is the number of pixels we would like to have along the
+     * short axis of an app that needs to run on a normal size screen.
      */
-    public static final int DEFAULT_PORTRAIT_WIDTH = 320;
+    public static final int DEFAULT_NORMAL_SHORT_DIMENSION = 320;
 
     /**
-     * The default height of the screen in portrait mode. 
-     */    
-    public static final int DEFAULT_PORTRAIT_HEIGHT = 480;
+     * This is the maximum aspect ratio we will allow while keeping
+     * applications in a compatible screen size.
+     */
+    public static final float MAXIMUM_ASPECT_RATIO = (854f/480f);
 
     /**
      *  A compatibility flags
      */
-    private int mCompatibilityFlags;
+    private final int mCompatibilityFlags;
     
     /**
      * A flag mask to tell if the application needs scaling (when mApplicationScale != 1.0f)
@@ -68,54 +63,27 @@
     private static final int SCALING_REQUIRED = 1; 
 
     /**
-     * A flag mask to indicates that the application can expand over the original size.
-     * The flag is set to true if
-     * 1) Application declares its expandable in manifest file using <supports-screens> or
-     * 2) Configuration.SCREENLAYOUT_COMPAT_NEEDED is not set
-     * {@see compatibilityFlag}
+     * Has the application said that its UI is expandable?  Based on the
+     * <supports-screen> android:expandible in the manifest.
      */
     private static final int EXPANDABLE = 2;
     
     /**
-     * A flag mask to tell if the application is configured to be expandable. This differs
-     * from EXPANDABLE in that the application that is not expandable will be 
-     * marked as expandable if Configuration.SCREENLAYOUT_COMPAT_NEEDED is not set.
-     */
-    private static final int CONFIGURED_EXPANDABLE = 4; 
-
-    /**
-     * A flag mask to indicates that the application supports large screens.
-     * The flag is set to true if
-     * 1) Application declares it supports large screens in manifest file using <supports-screens> or
-     * 2) The screen size is not large
-     * {@see compatibilityFlag}
+     * Has the application said that its UI supports large screens?  Based on the
+     * <supports-screen> android:largeScreens in the manifest.
      */
     private static final int LARGE_SCREENS = 8;
     
     /**
-     * A flag mask to tell if the application supports large screens. This differs
-     * from LARGE_SCREENS in that the application that does not support large
-     * screens will be marked as supporting them if the current screen is not
-     * large.
-     */
-    private static final int CONFIGURED_LARGE_SCREENS = 16; 
-
-    /**
-     * A flag mask to indicates that the application supports xlarge screens.
-     * The flag is set to true if
-     * 1) Application declares it supports xlarge screens in manifest file using <supports-screens> or
-     * 2) The screen size is not xlarge
-     * {@see compatibilityFlag}
+     * Has the application said that its UI supports xlarge screens?  Based on the
+     * <supports-screen> android:xlargeScreens in the manifest.
      */
     private static final int XLARGE_SCREENS = 32;
     
     /**
-     * A flag mask to tell if the application supports xlarge screens. This differs
-     * from XLARGE_SCREENS in that the application that does not support xlarge
-     * screens will be marked as supporting them if the current screen is not
-     * xlarge.
+     * Set if the application needs to run in screen size compatibility mode.
      */
-    private static final int CONFIGURED_XLARGE_SCREENS = 64;
+    private static final int NEEDS_SCREEN_COMPAT = 128;
 
     /**
      * The effective screen density we have selected for this application.
@@ -132,28 +100,55 @@
      */
     public final float applicationInvertedScale;
 
-    /**
-     * The flags from ApplicationInfo.
-     */
-    public final int appFlags;
-    
-    public CompatibilityInfo(ApplicationInfo appInfo) {
-        appFlags = appInfo.flags;
-        
+    public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, boolean forceCompat) {
+        int compatFlags = 0;
+
         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
-            // Saying you support large screens also implies you support xlarge
-            // screens; there is no compatibility mode for a large app on an
-            // xlarge screen.
-            mCompatibilityFlags |= LARGE_SCREENS | CONFIGURED_LARGE_SCREENS
-                    | XLARGE_SCREENS | CONFIGURED_XLARGE_SCREENS
-                    | EXPANDABLE | CONFIGURED_EXPANDABLE;
+            compatFlags |= LARGE_SCREENS;
+            if (!forceCompat) {
+                // If we aren't forcing the app into compatibility mode, then
+                // assume if it supports large screens that we should allow it
+                // to use the full space of an xlarge screen as well.
+                compatFlags |= XLARGE_SCREENS | EXPANDABLE;
+            }
         }
         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
-            mCompatibilityFlags |= XLARGE_SCREENS | CONFIGURED_XLARGE_SCREENS
-                    | EXPANDABLE | CONFIGURED_EXPANDABLE;
+            compatFlags |= XLARGE_SCREENS | EXPANDABLE;
         }
-        if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
-            mCompatibilityFlags |= EXPANDABLE | CONFIGURED_EXPANDABLE;
+        if (!forceCompat) {
+            // If we are forcing compatibility mode, then ignore an app that
+            // just says it is resizable for screens.  We'll only have it fill
+            // the screen if it explicitly says it supports the screen size we
+            // are running in.
+            if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
+                compatFlags |= EXPANDABLE;
+            }
+        }
+
+        boolean supportsScreen = false;
+        switch (screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK) {
+            case Configuration.SCREENLAYOUT_SIZE_XLARGE:
+                if ((compatFlags&XLARGE_SCREENS) != 0) {
+                    supportsScreen = true;
+                }
+                break;
+            case Configuration.SCREENLAYOUT_SIZE_LARGE:
+                if ((compatFlags&LARGE_SCREENS) != 0) {
+                    supportsScreen = true;
+                }
+                break;
+        }
+
+        if ((screenLayout&Configuration.SCREENLAYOUT_COMPAT_NEEDED) == 0) {
+            if ((compatFlags&EXPANDABLE) != 0) {
+                supportsScreen = true;
+            }
+        }
+
+        if (supportsScreen) {
+            compatFlags &= ~NEEDS_SCREEN_COMPAT;
+        } else {
+            compatFlags |= NEEDS_SCREEN_COMPAT;
         }
         
         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
@@ -165,13 +160,14 @@
             applicationScale = DisplayMetrics.DENSITY_DEVICE
                     / (float) DisplayMetrics.DENSITY_DEFAULT;
             applicationInvertedScale = 1.0f / applicationScale;
-            mCompatibilityFlags |= SCALING_REQUIRED;
+            compatFlags |= SCALING_REQUIRED;
         }
+
+        mCompatibilityFlags = compatFlags;
     }
 
-    private CompatibilityInfo(int appFlags, int compFlags,
+    private CompatibilityInfo(int compFlags,
             int dens, float scale, float invertedScale) {
-        this.appFlags = appFlags;
         mCompatibilityFlags = compFlags;
         applicationDensity = dens;
         applicationScale = scale;
@@ -179,81 +175,13 @@
     }
 
     private CompatibilityInfo() {
-        this(ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
-                | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS
-                | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS
-                | ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS
-                | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS,
-                EXPANDABLE | CONFIGURED_EXPANDABLE,
+        this(XLARGE_SCREENS | LARGE_SCREENS | EXPANDABLE,
                 DisplayMetrics.DENSITY_DEVICE,
                 1.0f,
                 1.0f);
     }
 
     /**
-     * Returns the copy of this instance.
-     */
-    public CompatibilityInfo copy() {
-        CompatibilityInfo info = new CompatibilityInfo(appFlags, mCompatibilityFlags,
-                applicationDensity, applicationScale, applicationInvertedScale);
-        return info;
-    }
- 
-    /**
-     * Sets expandable bit in the compatibility flag.
-     */
-    public void setExpandable(boolean expandable) {
-        if (expandable) {
-            mCompatibilityFlags |= CompatibilityInfo.EXPANDABLE;
-        } else {
-            mCompatibilityFlags &= ~CompatibilityInfo.EXPANDABLE;
-        }
-    }
-
-    /**
-     * Sets large screen bit in the compatibility flag.
-     */
-    public void setLargeScreens(boolean expandable) {
-        if (expandable) {
-            mCompatibilityFlags |= CompatibilityInfo.LARGE_SCREENS;
-        } else {
-            mCompatibilityFlags &= ~CompatibilityInfo.LARGE_SCREENS;
-        }
-    }
-
-    /**
-     * Sets large screen bit in the compatibility flag.
-     */
-    public void setXLargeScreens(boolean expandable) {
-        if (expandable) {
-            mCompatibilityFlags |= CompatibilityInfo.XLARGE_SCREENS;
-        } else {
-            mCompatibilityFlags &= ~CompatibilityInfo.XLARGE_SCREENS;
-        }
-    }
-
-    /**
-     * @return true if the application is configured to be expandable.
-     */
-    public boolean isConfiguredExpandable() {
-        return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0;
-    }
-
-    /**
-     * @return true if the application is configured to be expandable.
-     */
-    public boolean isConfiguredLargeScreens() {
-        return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_LARGE_SCREENS) != 0;
-    }
-
-    /**
-     * @return true if the application is configured to be expandable.
-     */
-    public boolean isConfiguredXLargeScreens() {
-        return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_XLARGE_SCREENS) != 0;
-    }
-
-    /**
      * @return true if the scaling is required
      */
     public boolean isScalingRequired() {
@@ -261,14 +189,12 @@
     }
     
     public boolean supportsScreen() {
-        return (mCompatibilityFlags & (EXPANDABLE|LARGE_SCREENS))
-                == (EXPANDABLE|LARGE_SCREENS);
+        return (mCompatibilityFlags&NEEDS_SCREEN_COMPAT) == 0;
     }
     
     @Override
     public String toString() {
-        return "CompatibilityInfo{scale=" + applicationScale +
-                ", supports screen=" + supportsScreen() + "}";
+        return "CompatibilityInfo{scale=" + applicationScale + "}";
     }
 
     /**
@@ -423,24 +349,144 @@
         }
     }
 
+    public void applyToDisplayMetrics(DisplayMetrics inoutDm) {
+        if (!supportsScreen()) {
+            // This is a larger screen device and the app is not
+            // compatible with large screens, so diddle it.
+            CompatibilityInfo.updateCompatibleScreenFrame(inoutDm, null, inoutDm);
+        }
+
+        if (isScalingRequired()) {
+            float invertedRatio = applicationInvertedScale;
+            inoutDm.density *= invertedRatio;
+            inoutDm.densityDpi = (int)((inoutDm.density*DisplayMetrics.DENSITY_DEFAULT)+.5f);
+            inoutDm.scaledDensity *= invertedRatio;
+            inoutDm.xdpi *= invertedRatio;
+            inoutDm.ydpi *= invertedRatio;
+            inoutDm.widthPixels = (int) (inoutDm.widthPixels * invertedRatio + 0.5f);
+            inoutDm.heightPixels = (int) (inoutDm.heightPixels * invertedRatio + 0.5f);
+        }
+    }
+
+    public void applyToConfiguration(Configuration inoutConfig) {
+        if (!supportsScreen()) {
+            // This is a larger screen device and the app is not
+            // compatible with large screens, so we are forcing it to
+            // run as if the screen is normal size.
+            inoutConfig.screenLayout =
+                    (inoutConfig.screenLayout&~Configuration.SCREENLAYOUT_SIZE_MASK)
+                    | Configuration.SCREENLAYOUT_SIZE_NORMAL;
+        }
+    }
+
     /**
-     * Returns the frame Rect for applications runs under compatibility mode.
+     * Compute the frame Rect for applications runs under compatibility mode.
      *
      * @param dm the display metrics used to compute the frame size.
      * @param orientation the orientation of the screen.
      * @param outRect the output parameter which will contain the result.
+     * @return Returns the scaling factor for the window.
      */
-    public static void updateCompatibleScreenFrame(DisplayMetrics dm, int orientation,
-            Rect outRect) {
-        int width = dm.widthPixels;
-        int portraitHeight = (int) (DEFAULT_PORTRAIT_HEIGHT * dm.density + 0.5f);
-        int portraitWidth = (int) (DEFAULT_PORTRAIT_WIDTH * dm.density + 0.5f);
-        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            int xOffset = (width - portraitHeight) / 2 ;
-            outRect.set(xOffset, 0, xOffset + portraitHeight, portraitWidth);
+    public static float updateCompatibleScreenFrame(DisplayMetrics dm,
+            Rect outRect, DisplayMetrics outDm) {
+        final int width = dm.realWidthPixels;
+        final int height = dm.realHeightPixels;
+        int shortSize, longSize;
+        if (width < height) {
+            shortSize = width;
+            longSize = height;
         } else {
-            int xOffset = (width - portraitWidth) / 2 ;
-            outRect.set(xOffset, 0, xOffset + portraitWidth, portraitHeight);
+            shortSize = height;
+            longSize = width;
         }
+        int newShortSize = (int)(DEFAULT_NORMAL_SHORT_DIMENSION * dm.density + 0.5f);
+        float aspect = ((float)longSize) / shortSize;
+        if (aspect > MAXIMUM_ASPECT_RATIO) {
+            aspect = MAXIMUM_ASPECT_RATIO;
+        }
+        int newLongSize = (int)(newShortSize * aspect + 0.5f);
+        int newWidth, newHeight;
+        if (width < height) {
+            newWidth = newShortSize;
+            newHeight = newLongSize;
+        } else {
+            newWidth = newLongSize;
+            newHeight = newShortSize;
+        }
+
+        float sw = width/(float)newWidth;
+        float sh = height/(float)newHeight;
+        float scale = sw < sh ? sw : sh;
+        if (scale < 1) {
+            scale = 1;
+        }
+
+        if (outRect != null) {
+            final int left = (int)((width-(newWidth*scale))/2);
+            final int top = (int)((height-(newHeight*scale))/2);
+            outRect.set(left, top, left+newWidth, top+newHeight);
+        }
+
+        if (outDm != null) {
+            outDm.widthPixels = newWidth;
+            outDm.heightPixels = newHeight;
+        }
+
+        return scale;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        try {
+            CompatibilityInfo oc = (CompatibilityInfo)o;
+            if (mCompatibilityFlags != oc.mCompatibilityFlags) return false;
+            if (applicationDensity != oc.applicationDensity) return false;
+            if (applicationScale != oc.applicationScale) return false;
+            if (applicationInvertedScale != oc.applicationInvertedScale) return false;
+            return true;
+        } catch (ClassCastException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + mCompatibilityFlags;
+        result = 31 * result + applicationDensity;
+        result = 31 * result + Float.floatToIntBits(applicationScale);
+        result = 31 * result + Float.floatToIntBits(applicationInvertedScale);
+        return result;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCompatibilityFlags);
+        dest.writeInt(applicationDensity);
+        dest.writeFloat(applicationScale);
+        dest.writeFloat(applicationInvertedScale);
+    }
+
+    public static final Parcelable.Creator<CompatibilityInfo> CREATOR
+            = new Parcelable.Creator<CompatibilityInfo>() {
+        public CompatibilityInfo createFromParcel(Parcel source) {
+            return new CompatibilityInfo(source);
+        }
+
+        public CompatibilityInfo[] newArray(int size) {
+            return new CompatibilityInfo[size];
+        }
+    };
+
+    private CompatibilityInfo(Parcel source) {
+        mCompatibilityFlags = source.readInt();
+        applicationDensity = source.readInt();
+        applicationScale = source.readFloat();
+        applicationInvertedScale = source.readFloat();
     }
 }
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 93b3429..0de08f2 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -814,12 +814,22 @@
     }
     
     public int hashCode() {
-        return ((int)this.fontScale) + this.mcc + this.mnc
-                + (this.locale != null ? this.locale.hashCode() : 0)
-                + this.touchscreen
-                + this.keyboard + this.keyboardHidden + this.hardKeyboardHidden
-                + this.navigation + this.navigationHidden
-                + this.orientation + this.screenLayout + this.uiMode
-                + this.screenWidthDp + this.screenHeightDp;
+        int result = 17;
+        result = 31 * result + Float.floatToIntBits(fontScale);
+        result = 31 * result + mcc;
+        result = 31 * result + mnc;
+        result = 31 * result + (locale != null ? locale.hashCode() : 0);
+        result = 31 * result + touchscreen;
+        result = 31 * result + keyboard;
+        result = 31 * result + keyboardHidden;
+        result = 31 * result + hardKeyboardHidden;
+        result = 31 * result + navigation;
+        result = 31 * result + navigationHidden;
+        result = 31 * result + orientation;
+        result = 31 * result + screenLayout;
+        result = 31 * result + uiMode;
+        result = 31 * result + screenWidthDp;
+        result = 31 * result + screenHeightDp;
+        return result;
     }
 }
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 2e6ae70..540f704 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -92,6 +92,7 @@
     private static boolean mPreloaded;
 
     /*package*/ final TypedValue mTmpValue = new TypedValue();
+    /*package*/ final Configuration mTmpConfig = new Configuration();
 
     // These are protected by the mTmpValue lock.
     private final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache
@@ -1401,10 +1402,23 @@
      */
     public void updateConfiguration(Configuration config,
             DisplayMetrics metrics) {
+        updateConfiguration(config, metrics, null);
+    }
+
+    /**
+     * @hide
+     */
+    public void updateConfiguration(Configuration config,
+            DisplayMetrics metrics, CompatibilityInfo compat) {
         synchronized (mTmpValue) {
+            if (compat != null) {
+                mCompatibilityInfo = compat;
+            }
             int configChanges = 0xfffffff;
             if (config != null) {
-                configChanges = mConfiguration.updateFrom(config);
+                mTmpConfig.setTo(config);
+                mCompatibilityInfo.applyToConfiguration(mTmpConfig);
+                configChanges = mConfiguration.updateFrom(mTmpConfig);
                 configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
             }
             if (mConfiguration.locale == null) {
@@ -1412,8 +1426,7 @@
             }
             if (metrics != null) {
                 mMetrics.setTo(metrics);
-                mMetrics.updateMetrics(mCompatibilityInfo,
-                        mConfiguration.orientation, mConfiguration.screenLayout);
+                mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
             }
             mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
 
@@ -1503,15 +1516,23 @@
      *
      * @hide
      */
-    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) {
+    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics,
+            CompatibilityInfo compat) {
         if (mSystem != null) {
-            mSystem.updateConfiguration(config, metrics);
+            mSystem.updateConfiguration(config, metrics, compat);
             //Log.i(TAG, "Updated system resources " + mSystem
             //        + ": " + mSystem.getConfiguration());
         }
     }
 
     /**
+     * @hide
+     */
+    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) {
+        updateSystemConfiguration(config, metrics, null);
+    }
+    
+    /**
      * Return the current display metrics that are in effect for this resource 
      * object.  The returned object should be treated as read-only.
      * 
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/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 02fd6e4..3bc1348 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1096,12 +1096,6 @@
             public static final String ALBUM_KEY = "album_key";
 
             /**
-             * A URI to the album art, if any
-             * <P>Type: TEXT</P>
-             */
-            public static final String ALBUM_ART = "album_art";
-
-            /**
              * The track number of this song on the album, if any.
              * This number encodes both the track number and the
              * disc number. For multi-disc sets, this number will
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 63baf14..8018ff9 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -16,9 +16,7 @@
 
 package android.util;
 
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.os.*;
+import android.os.SystemProperties;
 
 
 /**
@@ -107,6 +105,11 @@
      */
     public float ydpi;
 
+    /** @hide */
+    public int realWidthPixels;
+    /** @hide */
+    public int realHeightPixels;
+
     public DisplayMetrics() {
     }
     
@@ -118,6 +121,8 @@
         scaledDensity = o.scaledDensity;
         xdpi = o.xdpi;
         ydpi = o.ydpi;
+        realWidthPixels = o.realWidthPixels;
+        realHeightPixels = o.realHeightPixels;
     }
     
     public void setToDefaults() {
@@ -128,101 +133,8 @@
         scaledDensity = density;
         xdpi = DENSITY_DEVICE;
         ydpi = DENSITY_DEVICE;
-    }
-
-    /**
-     * Update the display metrics based on the compatibility info and orientation
-     * NOTE: DO NOT EXPOSE THIS API!  It is introducing a circular dependency
-     * with the higher-level android.res package.
-     * {@hide}
-     */
-    public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation,
-            int screenLayout) {
-        boolean expandable = compatibilityInfo.isConfiguredExpandable();
-        boolean largeScreens = compatibilityInfo.isConfiguredLargeScreens();
-        boolean xlargeScreens = compatibilityInfo.isConfiguredXLargeScreens();
-        
-        // Note: this assume that configuration is updated before calling
-        // updateMetrics method.
-        if (!expandable) {
-            if ((screenLayout&Configuration.SCREENLAYOUT_COMPAT_NEEDED) == 0) {
-                expandable = true;
-                // the current screen size is compatible with non-resizing apps.
-                compatibilityInfo.setExpandable(true);
-            } else {
-                compatibilityInfo.setExpandable(false);
-            }
-        }
-        if (!largeScreens) {
-            if ((screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK)
-                    != Configuration.SCREENLAYOUT_SIZE_LARGE) {
-                largeScreens = true;
-                // the current screen size is not large.
-                compatibilityInfo.setLargeScreens(true);
-            } else {
-                compatibilityInfo.setLargeScreens(false);
-            }
-        }
-        if (!xlargeScreens) {
-            if ((screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK)
-                    != Configuration.SCREENLAYOUT_SIZE_XLARGE) {
-                xlargeScreens = true;
-                // the current screen size is not large.
-                compatibilityInfo.setXLargeScreens(true);
-            } else {
-                compatibilityInfo.setXLargeScreens(false);
-            }
-        }
-        
-        if (!expandable || (!largeScreens && !xlargeScreens)) {
-            // This is a larger screen device and the app is not 
-            // compatible with large screens, so diddle it.
-            
-            // Figure out the compatibility width and height of the screen.
-            int defaultWidth;
-            int defaultHeight;
-            switch (orientation) {
-                case Configuration.ORIENTATION_LANDSCAPE: {
-                    defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density +
-                            0.5f);
-                    defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density +
-                            0.5f);
-                    break;
-                }
-                case Configuration.ORIENTATION_PORTRAIT:
-                case Configuration.ORIENTATION_SQUARE:
-                default: {
-                    defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density +
-                            0.5f);
-                    defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density +
-                            0.5f);
-                    break;
-                }
-                case Configuration.ORIENTATION_UNDEFINED: {
-                    // don't change
-                    return;
-                }
-            }
-            
-            if (defaultWidth < widthPixels) {
-                // content/window's x offset in original pixels
-                widthPixels = defaultWidth;
-            }
-            if (defaultHeight < heightPixels) {
-                heightPixels = defaultHeight;
-            }
-        }
-        
-        if (compatibilityInfo.isScalingRequired()) {
-            float invertedRatio = compatibilityInfo.applicationInvertedScale;
-            density *= invertedRatio;
-            densityDpi = (int)((density*DisplayMetrics.DENSITY_DEFAULT)+.5f);
-            scaledDensity *= invertedRatio;
-            xdpi *= invertedRatio;
-            ydpi *= invertedRatio;
-            widthPixels = (int) (widthPixels * invertedRatio + 0.5f);
-            heightPixels = (int) (heightPixels * invertedRatio + 0.5f);
-        }
+        realWidthPixels = 0;
+        realHeightPixels = 0;
     }
 
     @Override
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 8e839c0..1d60066 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -226,6 +226,9 @@
         outMetrics.scaledDensity= outMetrics.density;
         outMetrics.xdpi         = mDpiX;
         outMetrics.ydpi         = mDpiY;
+
+        outMetrics.realWidthPixels  = outMetrics.widthPixels;
+        outMetrics.realHeightPixels = outMetrics.heightPixels;
     }
 
     static IWindowManager getWindowManager() {
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 3d19380..5a418f3 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -466,6 +466,18 @@
     public int getMaxWallpaperLayer();
     
     /**
+     * Return the display width available after excluding the window
+     * decor.
+     */
+    public int getNonDecorDisplayWidth(int fullWidth);
+
+    /**
+     * Return the display height available after excluding the screen
+     * decor.
+     */
+    public int getNonDecorDisplayHeight(int fullHeight);
+
+    /**
      * Return whether the given window should forcibly hide everything
      * behind it.  Typically returns true for the keyguard.
      */
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index 807f6ce..de38fbe 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -38,12 +38,13 @@
     private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
     private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
 
-    private final int mSubtypeNameResId;
+    private final boolean mIsAuxiliary;
+    private final int mSubtypeHashCode;
     private final int mSubtypeIconResId;
+    private final int mSubtypeNameResId;
     private final String mSubtypeLocale;
     private final String mSubtypeMode;
     private final String mSubtypeExtraValue;
-    private final int mSubtypeHashCode;
     private HashMap<String, String> mExtraValueHashMapCache;
 
     /**
@@ -55,12 +56,27 @@
      * @param extraValue The extra value of the subtype
      */
     InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue) {
+        this(nameId, iconId, locale, mode, extraValue, false);
+    }
+
+    /**
+     * Constructor
+     * @param nameId The name of the subtype
+     * @param iconId The icon of the subtype
+     * @param locale The locale supported by the subtype
+     * @param modeId The mode supported by the subtype
+     * @param extraValue The extra value of the subtype
+     * @param isAuxiliary true when this subtype is one shot subtype.
+     */
+    InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
+            boolean isAuxiliary) {
         mSubtypeNameResId = nameId;
         mSubtypeIconResId = iconId;
         mSubtypeLocale = locale != null ? locale : "";
         mSubtypeMode = mode != null ? mode : "";
         mSubtypeExtraValue = extraValue != null ? extraValue : "";
         mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue);
+        mIsAuxiliary = isAuxiliary;
     }
 
     InputMethodSubtype(Parcel source) {
@@ -74,6 +90,7 @@
         s = source.readString();
         mSubtypeExtraValue = s != null ? s : "";
         mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue);
+        mIsAuxiliary = (source.readInt() == 1);
     }
 
     /**
@@ -111,6 +128,15 @@
         return mSubtypeExtraValue;
     }
 
+    /**
+     * @return true if this subtype is one shot subtype. One shot subtype will not be shown in the
+     * ime switch list when this subtype is implicitly enabled. The framework will never
+     * switch the current ime to this subtype by switchToLastInputMethod in InputMethodManager.
+     */
+    public boolean isAuxiliary() {
+        return mIsAuxiliary;
+    }
+
     private HashMap<String, String> getExtraValueHashMap() {
         if (mExtraValueHashMapCache == null) {
             mExtraValueHashMapCache = new HashMap<String, String>();
@@ -170,24 +196,29 @@
         return false;
     }
 
+    @Override
     public int describeContents() {
         return 0;
     }
 
+    @Override
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         dest.writeInt(mSubtypeNameResId);
         dest.writeInt(mSubtypeIconResId);
         dest.writeString(mSubtypeLocale);
         dest.writeString(mSubtypeMode);
         dest.writeString(mSubtypeExtraValue);
+        dest.writeInt(mIsAuxiliary ? 1 : 0);
     }
 
     public static final Parcelable.Creator<InputMethodSubtype> CREATOR
             = new Parcelable.Creator<InputMethodSubtype>() {
+        @Override
         public InputMethodSubtype createFromParcel(Parcel source) {
             return new InputMethodSubtype(source);
         }
 
+        @Override
         public InputMethodSubtype[] newArray(int size) {
             return new InputMethodSubtype[size];
         }
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index 0918683..1004b5f 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -208,7 +208,7 @@
                 // view. This happens in the WebChromeClient before this method
                 // is invoked.
                 pauseAndDispatch(mProxy);
-
+                mProxy.dispatchOnStopFullScreen();
                 mLayout.removeView(getSurfaceView());
 
                 if (mProgressView != null) {
@@ -253,7 +253,8 @@
         client.onShowCustomView(mLayout, mCallback);
         // Plugins like Flash will draw over the video so hide
         // them while we're playing.
-        mProxy.getWebView().getViewManager().hideAll();
+        if (webView.getViewManager() != null)
+            webView.getViewManager().hideAll();
 
         mProgressView = client.getVideoLoadingProgressView();
         if (mProgressView != null) {
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index d1b8cfc..7d8669bf 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -65,6 +65,7 @@
     private static final int ENDED             = 201;
     private static final int POSTER_FETCHED    = 202;
     private static final int PAUSED            = 203;
+    private static final int STOPFULLSCREEN    = 204;
 
     // Timer thread -> UI thread
     private static final int TIMEUPDATE = 300;
@@ -287,8 +288,13 @@
     }
 
     public void dispatchOnPaused() {
-      Message msg = Message.obtain(mWebCoreHandler, PAUSED);
-      mWebCoreHandler.sendMessage(msg);
+        Message msg = Message.obtain(mWebCoreHandler, PAUSED);
+        mWebCoreHandler.sendMessage(msg);
+    }
+
+    public void dispatchOnStopFullScreen() {
+        Message msg = Message.obtain(mWebCoreHandler, STOPFULLSCREEN);
+        mWebCoreHandler.sendMessage(msg);
     }
 
     public void onTimeupdate() {
@@ -560,6 +566,9 @@
                     case TIMEUPDATE:
                         nativeOnTimeupdate(msg.arg1, mNativePointer);
                         break;
+                    case STOPFULLSCREEN:
+                        nativeOnStopFullscreen(mNativePointer);
+                        break;
                 }
             }
         };
@@ -686,6 +695,7 @@
     private native void nativeOnPaused(int nativePointer);
     private native void nativeOnPosterFetched(Bitmap poster, int nativePointer);
     private native void nativeOnTimeupdate(int position, int nativePointer);
+    private native void nativeOnStopFullscreen(int nativePointer);
     private native static boolean nativeSendSurfaceTexture(SurfaceTexture texture,
             int baseLayer, int videoLayerId, int textureName,
             int playerState);
diff --git a/core/java/android/webkit/JniUtil.java b/core/java/android/webkit/JniUtil.java
index 62b415c..b5d4933 100644
--- a/core/java/android/webkit/JniUtil.java
+++ b/core/java/android/webkit/JniUtil.java
@@ -48,6 +48,12 @@
         initialized = true;
     }
 
+    protected static synchronized Context getContext() {
+        if (!initialized)
+            return null;
+        return sContext;
+    }
+
     /**
      * Called by JNI. Gets the application's database directory, excluding the trailing slash.
      * @return String The application's database directory
diff --git a/core/java/android/webkit/WebHistoryItem.java b/core/java/android/webkit/WebHistoryItem.java
index 7c0e478..ccf3d6b 100644
--- a/core/java/android/webkit/WebHistoryItem.java
+++ b/core/java/android/webkit/WebHistoryItem.java
@@ -90,7 +90,9 @@
      * another item, the identifiers will be the same even if they are not the
      * same object.
      * @return The id for this item.
+     * @deprecated This method is now obsolete.
      */
+    @Deprecated
     public int getId() {
         return mId;
     }
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 4755362..8ffbda2 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -42,8 +42,10 @@
      * SINGLE_COLUMN moves all content into one column that is the width of the
      * view.
      * NARROW_COLUMNS makes all columns no wider than the screen if possible.
+     * @deprecated This enum is now obsolete.
      */
     // XXX: These must match LayoutAlgorithm in Settings.h in WebCore.
+    @Deprecated
     public enum LayoutAlgorithm {
         NORMAL,
         SINGLE_COLUMN,
@@ -520,14 +522,18 @@
 
     /**
      * Enables dumping the pages navigation cache to a text file.
+     * @deprecated This method is now obsolete.
      */
+    @Deprecated
     public void setNavDump(boolean enabled) {
         mNavDump = enabled;
     }
 
     /**
      * Returns true if dumping the navigation cache is enabled.
+     * @deprecated This method is now obsolete.
      */
+    @Deprecated
     public boolean getNavDump() {
         return mNavDump;
     }
@@ -665,7 +671,9 @@
      * Set whether the WebView uses its background for over scroll background.
      * If true, it will use the WebView's background. If false, it will use an
      * internal pattern. Default is true.
+     * @deprecated This method is now obsolete.
      */
+    @Deprecated
     public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
         mUseWebViewBackgroundForOverscroll = view;
     }
@@ -673,7 +681,9 @@
     /**
      * Returns true if this WebView uses WebView's background instead of
      * internal pattern for over scroll background.
+     * @deprecated This method is now obsolete.
      */
+    @Deprecated
     public boolean getUseWebViewBackgroundForOverscrollBackground() {
         return mUseWebViewBackgroundForOverscroll;
     }
@@ -876,7 +886,9 @@
      * WebView.
      * @param l A LayoutAlgorithm enum specifying the algorithm to use.
      * @see WebSettings.LayoutAlgorithm
+     * @deprecated This method is now obsolete.
      */
+    @Deprecated
     public synchronized void setLayoutAlgorithm(LayoutAlgorithm l) {
         // XXX: This will only be affective if libwebcore was built with
         // ANDROID_LAYOUT defined.
@@ -891,7 +903,9 @@
      * @return LayoutAlgorithm enum value describing the layout algorithm
      *         being used.
      * @see WebSettings.LayoutAlgorithm
+     * @deprecated This method is now obsolete.
      */
+    @Deprecated
     public synchronized LayoutAlgorithm getLayoutAlgorithm() {
         return mLayoutAlgorithm;
     }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 0504532..0acd748 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -822,6 +822,8 @@
 
     private WebViewCore.AutoFillData mAutoFillData;
 
+    private static boolean sNotificationsEnabled = true;
+
     /**
      * URI scheme for telephone number
      */
@@ -854,13 +856,17 @@
     private PictureListener mPictureListener;
     /**
      * Interface to listen for new pictures as they change.
+     * @deprecated This interface is now obsolete.
      */
+    @Deprecated
     public interface PictureListener {
         /**
          * Notify the listener that the picture has changed.
          * @param view The WebView that owns the picture.
          * @param picture The new picture.
+         * @deprecated This method is now obsolete.
          */
+        @Deprecated
         public void onNewPicture(WebView view, Picture picture);
     }
 
@@ -1024,24 +1030,38 @@
     }
 
     /*
-     * A variable to track if there is a receiver added for PROXY_CHANGE_ACTION
+     * Receiver for PROXY_CHANGE_ACTION, will be null when it is not added handling broadcasts.
      */
-    private static boolean sProxyReceiverAdded;
+    private static ProxyReceiver sProxyReceiver;
 
+    /*
+     * @param context This method expects this to be a valid context
+     */
     private static synchronized void setupProxyListener(Context context) {
-        if (sProxyReceiverAdded) {
+        if (sProxyReceiver != null || sNotificationsEnabled == false) {
             return;
         }
         IntentFilter filter = new IntentFilter();
         filter.addAction(Proxy.PROXY_CHANGE_ACTION);
+        sProxyReceiver = new ProxyReceiver();
         Intent currentProxy = context.getApplicationContext().registerReceiver(
-                new ProxyReceiver(), filter);
-        sProxyReceiverAdded = true;
+                sProxyReceiver, filter);
         if (currentProxy != null) {
             handleProxyBroadcast(currentProxy);
         }
     }
 
+    /*
+     * @param context This method expects this to be a valid context
+     */
+    private static synchronized void disableProxyListener(Context context) {
+        if (sProxyReceiver == null)
+            return;
+
+        context.getApplicationContext().unregisterReceiver(sProxyReceiver);
+        sProxyReceiver = null;
+    }
+
     private static void handleProxyBroadcast(Intent intent) {
         ProxyProperties proxyProperties = (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO);
         if (proxyProperties == null || proxyProperties.getHost() == null) {
@@ -1530,19 +1550,38 @@
 
     /**
      * Enables platform notifications of data state and proxy changes.
+     * Notifications are enabled by default.
+     *
+     * @deprecated This method is now obsolete.
      */
+    @Deprecated
     public static void enablePlatformNotifications() {
         checkThread();
-        Network.enablePlatformNotifications();
+        synchronized (WebView.class) {
+            Network.enablePlatformNotifications();
+            sNotificationsEnabled = true;
+            Context context = JniUtil.getContext();
+            if (context != null)
+                setupProxyListener(context);
+        }
     }
 
     /**
-     * If platform notifications are enabled, this should be called
-     * from the Activity's onPause() or onStop().
+     * Disables platform notifications of data state and proxy changes.
+     * Notifications are enabled by default.
+     *
+     * @deprecated This method is now obsolete.
      */
+    @Deprecated
     public static void disablePlatformNotifications() {
         checkThread();
-        Network.disablePlatformNotifications();
+        synchronized (WebView.class) {
+            Network.disablePlatformNotifications();
+            sNotificationsEnabled = false;
+            Context context = JniUtil.getContext();
+            if (context != null)
+                disableProxyListener(context);
+        }
     }
 
     /**
@@ -1647,7 +1686,9 @@
      * @param dest The file to store the serialized picture data. Will be
      *             overwritten with this WebView's picture data.
      * @return True if the picture was successfully saved.
+     * @deprecated This method is now obsolete.
      */
+    @Deprecated
     public boolean savePicture(Bundle b, final File dest) {
         checkThread();
         if (dest == null || b == null) {
@@ -1710,7 +1751,9 @@
      * @param b A Bundle containing the saved display data.
      * @param src The file where the picture data was stored.
      * @return True if the picture was successfully restored.
+     * @deprecated This method is now obsolete.
      */
+    @Deprecated
     public boolean restorePicture(Bundle b, File src) {
         checkThread();
         if (src == null || b == null) {
@@ -3729,7 +3772,9 @@
      * Set the Picture listener. This is an interface used to receive
      * notifications of a new Picture.
      * @param listener An implementation of WebView.PictureListener.
+     * @deprecated This method is now obsolete.
      */
+    @Deprecated
     public void setPictureListener(PictureListener listener) {
         checkThread();
         mPictureListener = listener;
@@ -5095,7 +5140,9 @@
     /**
      * Use this method to put the WebView into text selection mode.
      * Do not rely on this functionality; it will be deprecated in the future.
+     * @deprecated This method is now obsolete.
      */
+    @Deprecated
     public void emulateShiftHeld() {
         checkThread();
         setUpSelect(false, 0, 0);
@@ -8798,6 +8845,10 @@
         mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
     }
 
+    /**
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
     public void debugDump() {
         checkThread();
         nativeDebugDump();
diff --git a/core/java/android/widget/MultiAutoCompleteTextView.java b/core/java/android/widget/MultiAutoCompleteTextView.java
index 02c1ec7..134e4c4 100644
--- a/core/java/android/widget/MultiAutoCompleteTextView.java
+++ b/core/java/android/widget/MultiAutoCompleteTextView.java
@@ -30,7 +30,7 @@
  * can show completion suggestions for the substring of the text where
  * the user is typing instead of necessarily for the entire thing.
  * <p>
- * You must must provide a {@link Tokenizer} to distinguish the
+ * You must provide a {@link Tokenizer} to distinguish the
  * various substrings.
  *
  * <p>The following code snippet shows how to create a text view which suggests
@@ -41,7 +41,7 @@
  *     protected void onCreate(Bundle savedInstanceState) {
  *         super.onCreate(savedInstanceState);
  *         setContentView(R.layout.autocomplete_7);
- * 
+ *
  *         ArrayAdapter&lt;String&gt; adapter = new ArrayAdapter&lt;String&gt;(this,
  *                 android.R.layout.simple_dropdown_item_1line, COUNTRIES);
  *         MultiAutoCompleteTextView textView = (MultiAutoCompleteTextView) findViewById(R.id.edit);
@@ -132,7 +132,7 @@
      * Instead of validating the entire text, this subclass method validates
      * each token of the text individually.  Empty tokens are removed.
      */
-    @Override 
+    @Override
     public void performValidation() {
         Validator v = getValidator();
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 359cfcc..86b6b1d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7260,11 +7260,23 @@
                 mInputContentType.enterDown = false;
             }
             hideControllers();
+            removeAllSuggestionSpans();
         }
 
         startStopMarquee(hasWindowFocus);
     }
 
+    private void removeAllSuggestionSpans() {
+        if (mText instanceof Editable) {
+            Editable editable = ((Editable) mText);
+            SuggestionSpan[] spans = editable.getSpans(0, mText.length(), SuggestionSpan.class);
+            final int length = spans.length;
+            for (int i = 0; i < length; i++) {
+                editable.removeSpan(spans[i]);
+            }
+        }
+    }
+
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
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/jni/android_server_Watchdog.cpp b/core/jni/android_server_Watchdog.cpp
index 2a90db7..9e0ed47 100644
--- a/core/jni/android_server_Watchdog.cpp
+++ b/core/jni/android_server_Watchdog.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "Watchdog_N"
 #include <utils/Log.h>
 
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <fcntl.h>
 #include <dirent.h>
@@ -63,7 +64,8 @@
 
     const char *path = env->GetStringUTFChars(pathStr, NULL);
 
-    int outFd = open(path, O_WRONLY | O_APPEND | O_CREAT);
+    int outFd = open(path, O_WRONLY | O_APPEND | O_CREAT,
+        S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
     if (outFd < 0) {
         LOGE("Unable to open stack dump file: %d (%s)", errno, strerror(errno));
         goto done;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1c4bffdc..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 fa662ed..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
@@ -1839,7 +1844,7 @@
 
     <!-- Do not translate.  WebView User Agent string -->
     <string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>)
-        AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.13</string>
+        AppleWebKit/534.16 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.16</string>
     <!-- Do not translate.  WebView User Agent targeted content -->
     <string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
 
diff --git a/docs/html/guide/appendix/api-levels.jd b/docs/html/guide/appendix/api-levels.jd
index 013cc94..5c18a83 100644
--- a/docs/html/guide/appendix/api-levels.jd
+++ b/docs/html/guide/appendix/api-levels.jd
@@ -83,8 +83,10 @@
 
 <table>
   <tr><th>Platform Version</th><th>API Level</th></tr>
+  <tr><td>Android 3.1</td><td>12</td></tr>
   <tr><td>Android 3.0</td><td>11</td></tr>
-  <tr><td>Android 2.3.3</td><td>10</td></tr>
+  <tr><td>Android 2.3.4</td><td rowspan="2">10</td></tr>
+  <tr><td>Android 2.3.3</td></tr>
   <tr><td>Android 2.3</td><td>9</td></tr>
   <tr><td>Android 2.2</td><td>8</td></tr>
   <tr><td>Android 2.1</td><td>7</td></tr>
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 35acdd7..916da09 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -304,6 +304,7 @@
             <ul>
               <li><a href="<?cs var:toroot ?>guide/topics/usb/accessory.html">Accessory</a></li>
               <li><a href="<?cs var:toroot ?>guide/topics/usb/host.html">Host</a></li>
+              <li><a href="<?cs var:toroot ?>guide/topics/usb/adk.html">Open Accessory Dev Kit</a></li>
             </ul>
           </li>
 
@@ -652,9 +653,19 @@
       <li><a href="<?cs var:toroot ?>guide/practices/compatibility.html">
             <span class="en">Compatibility</span>
           </a></li>
-      <li><a href="<?cs var:toroot ?>guide/practices/screens_support.html">
-            <span class="en">Supporting Multiple Screens</span>
+      <li class="toggle-list">
+        <div><a href="<?cs var:toroot ?>guide/practices/screens_support.html">
+          <span class="en">Supporting Multiple Screens</span>
+        </a></div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>guide/practices/screens-distribution.html">
+            <span class="en">Distributing to Specific Screens</span>
           </a></li>
+          <li><a href="<?cs var:toroot ?>guide/practices/screens-support-1.5.html">
+            <span class="en">Strategies for Android 1.5</span>
+          </a></li>
+        </ul>
+      </li>
       <li><a href="<?cs var:toroot ?>guide/practices/optimizing-for-3.0.html">
             <span class="en">Optimizing Apps for Android 3.0</span>
           </a></li>
diff --git a/docs/html/guide/practices/screens-distribution.jd b/docs/html/guide/practices/screens-distribution.jd
new file mode 100644
index 0000000..0c5193b
--- /dev/null
+++ b/docs/html/guide/practices/screens-distribution.jd
@@ -0,0 +1,164 @@
+page.title=Distributing to Specific Screens
+parent.title=Supporting Multiple Screens
+parent.link=screens_support.html
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+  <h2>Quickview</h2>
+  <ul>
+    <li>If necessary, you can control distribution of your application based on the device
+screen configuration</li>
+  </ul>
+
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#FilteringHansetApps">Filtering a Handset Application from Tablets</a></li>
+    <li><a href="#FilteringTabletApps">Filtering a Tablet Application from Handsets</a></li>
+  </ol>
+
+  <h2>See also</h2>
+  <ol>
+    <li><a
+href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></li>
+    <li><a
+href="{@docRoot}guide/practices/optimizing-for-3.0.html">Optimizing Apps for Android 3.0</a></li>
+  </ol>
+
+</div>
+</div>
+
+
+
+<p>Although we recommend that you design your application to function properly on multiple
+configurations of screen size and density, you can instead choose to limit the distribution of your
+application to certain types of screens, such as only tablets and other large devices or only
+handsets and similar-sized devices. To do so, you can enable filtering by external services such as
+Android Market by adding elements to your manifest file that specify the screen configurations your
+application supports.</p>
+
+<p>However, before you decide to restrict your application to certain screen configurations, you
+should understand the techniques for <a
+href="{@docRoot}guide/practices/screens_support.html">supporting multiple screens</a> and implement
+them to the best of your ability. By supporting multiple screens, your application can be made
+available to the greatest number of users with different devices, using a single {@code .apk}.</p>
+
+
+
+<h2 id="FilteringHandsetApps">Filtering a Handset Application from Tablets</h2>
+
+<p>Because the system generally scales applications to fit larger screens well, you shouldn't
+need to filter your application from larger screens. As long as you follow the <a
+href="{@docRoot}guide/practices/screens_support.html#screen-independence">Best Practices for Screen
+Independence</a>, your application should work well on larger screens such as tablets. However, you
+might discover that your application can't scale up well or perhaps you've decided to publish two
+versions of your application for different screen configurations. In such a case, you can use the <a
+href="{@docRoot}guide/topics/manifest/compatible-screens-element.html">{@code
+&lt;compatible-screens>}</a> element to manage the distribution of your application based on
+combinations of screen size and density. External services such as Android Market use this
+information to apply filtering to your application, so that only devices that have a screen
+configuration with which you declare compatibility can download your application.</p>
+
+<p>The <a href="{@docRoot}guide/topics/manifest/compatible-screens-element.html">{@code
+&lt;compatible-screens>}</a> element must contain one or more {@code &lt;screen&gt;} elements. Each
+{@code &lt;screen&gt;} element specifies a screen configuration with which your application is
+compatible, using both the {@code android:screenSize} and {@code android:screenDensity} attributes.
+Each {@code &lt;screen&gt;} element <strong>must include both attributes</strong> to specify an
+individual screen configuration&mdash;if either attribute is missing, then the element is invalid
+(external services such as Android Market will ignore it).</p>
+
+<p>For example, if your application is compatible with only small and normal size screens,
+regardless of screen density, you must specify eight different {@code &lt;screen&gt;} elements,
+because each screen size has four density configurations. You must declare each one of
+these; any combination of size and density that you do <em>not</em> specify is considered a screen
+configuration with which your application is <em>not</em> compatible. Here's what the manifest
+entry looks like if your application is compatible with only small and normal screen sizes:</p>
+
+<pre>
+&lt;manifest ... >
+    ...
+    &lt;compatible-screens>
+        &lt;!-- all small size screens -->
+        &lt;screen android:screenSize="small" android:screenDensity="ldpi" />
+        &lt;screen android:screenSize="small" android:screenDensity="mdpi" />
+        &lt;screen android:screenSize="small" android:screenDensity="hdpi" />
+        &lt;screen android:screenSize="small" android:screenDensity="xhdpi" />
+        &lt;!-- all normal size screens -->
+        &lt;screen android:screenSize="normal" android:screenDensity="ldpi" />
+        &lt;screen android:screenSize="normal" android:screenDensity="mdpi" />
+        &lt;screen android:screenSize="normal" android:screenDensity="hdpi" />
+        &lt;screen android:screenSize="normal" android:screenDensity="xhdpi" />
+    &lt;/compatible-screens>
+    &lt;application ... >
+        ...
+    &lt;application>
+&lt;/manifest>
+</pre>
+
+<p class="note"><strong>Note:</strong> Although you can also use the <a
+href="{@docRoot}guide/topics/manifest/compatible-screens-element.html">{@code
+&lt;compatible-screens>}</a> element for the reverse scenario (when your application is not
+compatible with smaller screens), it's easier if you instead use the <a
+href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
+&lt;supports-screens>}</a> as discussed in the next section, because it doesn't require you
+to specify each screen density your application supports.</p>
+
+
+
+<h2 id="FilteringTabletApps">Filtering a Tablet Application from Handsets</h2>
+
+<p>If your application's UI is adversely affected when the system scales your application down to
+smaller screens, you should add <a
+href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">alternative
+layouts</a> for smaller screens to adjust the layout for those screens. However, sometimes your
+layout still might not fit a smaller screen or you've explicitly designed your application only for
+tablets and other large devices. In this case, you can manage the availability of your application
+to smaller screens by using the <a
+href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
+&lt;supports-screens>}</a> manifest element.</p>
+
+<p>For example, if you want your application to be available only to large and extra large
+screens, you can declare the element in your manifest like this:</p>
+
+<pre>
+&lt;manifest ... >
+    ...
+    &lt;supports-screens android:smallScreens="false"
+                      android:normalScreens="false"
+                      android:largeScreens="true"
+                      android:xlargeScreens="true" /&gt;
+    &lt;application ... >
+        ...
+    &lt;application>
+&lt;/manifest>
+</pre>
+
+<p>External services such as Android Market read this manifest element and use it to ensure that
+your application is available only to devices with either a large or an extra large screen.</p>
+
+<p class="caution"><strong>Caution:</strong> If you use the <a
+href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
+&lt;supports-screens>}</a> element for the reverse scenario (when your application is not compatible
+with <em>larger</em> screens) and set the larger screen size attributes to {@code "false"}, then
+external services such as Android Market <strong>do not</strong> apply filtering. Your application
+will still be available to larger screens, but when it runs, it will not resize to fit the screen.
+Instead, the system will draw it in a "postage stamp" window that's the same relative size as the
+screen size that your application does support (see <a
+href="screens-support-1.5.html#CompatMode">compatibility mode</a> for more information). If you want
+to prevent your application from being downloaded on larger screens, use <a
+href="{@docRoot}guide/topics/manifest/compatible-screens-element.html">{@code
+&lt;compatible-screens>}</a>, as discussed in the previous section about <a
+href="#FilteringHandsetApps">Filtering a Handset Application from Tablets</a>.</p>
+
+<p>Remember, you should strive to make your application available to as many devices as possible by
+applying all necessary techniques for <a
+href="{@docRoot}guide/practices/screens_support.html">supporting multiple screens</a>. You should
+use <a href="{@docRoot}guide/topics/manifest/compatible-screens-element.html">{@code
+&lt;compatible-screens>}</a> or <a
+href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
+&lt;supports-screens>}</a> only when you cannot provide compatibility on all screen configurations
+or you have decided to provide different versions of your application for different sets of screen
+configurations.</p>
+
diff --git a/docs/html/guide/practices/screens-support-1.5.jd b/docs/html/guide/practices/screens-support-1.5.jd
new file mode 100644
index 0000000..6fd36bb
--- /dev/null
+++ b/docs/html/guide/practices/screens-support-1.5.jd
@@ -0,0 +1,186 @@
+page.title=Strategies for Android 1.5
+parent.title=Supporting Multiple Screens
+parent.link=screens_support.html
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+  <h2>Quickview</h2>
+  <ul>
+    <li>Apps developed for Android 1.5 and below support only the baseline screen
+configuration, by default</li>
+    <li>There are some simple steps you should take to enable support for multiple screens in
+an application designed for Android 1.5</li>
+  </ul>
+
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#strategies">Adding Multiple Screens Support</a></li>
+    <li><a href="#CompatMode">Compatibility Mode</a></li>
+  </ol>
+
+  <h2>See also</h2>
+  <ol>
+    <li><a
+href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></li>
+  </ol>
+
+</div>
+</div>
+
+
+<p>All applications written for Android 1.5 or earlier, by default, support only the
+baseline HVGA screen used on the T-Mobile G1 and similar devices, which is <em>normal</em> screen
+size and medium density (<em>mdpi</em>). Android 1.6 introduced support for different screen
+configurations and added APIs that allow applications to control how they operate on different
+screens, using alternative resources for different screen configurations.</p>
+
+<p>If your manifest file includes the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code &lt;uses-sdk&gt;}</a> element,
+with the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code android:minSdkVersion}</a>
+attribute set to {@code "3"} or lower, and does <em>not</em> include the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+android:targetSdkVersion}</a> set to {@code "4"} or higher, then this document is for you. By
+default, an application written for Android 1.5 or below that does not set the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+android:targetSdkVersion}</a> set to {@code "4"} or higher runs in <a
+href="#CompatMode">compatibility mode</a> when on a device with a screen larger than the
+<em>normal</em> screen size (basically, the system displays the application in a small window
+that is roughly the size of the normal screen size).</p>
+
+<p>This document describes how to get your application out of <a href="#CompatMode">compatibility
+mode</a> and instead support multiple screens, but also maintain compatibility with Android 1.5 and
+below.</p>
+
+<p class="note"><strong>Note:</strong> Before you begin, you should first decide whether it's even
+necessary to support Android 1.5. To see the relative number of devices that are still running
+Android 1.5, see the <a
+href="http://developer.android.com/resources/dashboard/platform-versions.html">Platform Versions
+Dashboard</a>.</p>
+
+
+
+
+<h2 id="strategies">Adding Multiple Screens Support</h2>
+
+<p>If you have already developed and published an Android application based on
+Android 1.5 or earlier platform version, and want to maintain compatibility with Android 1.5, you
+need to make some changes to your application in order for it to properly run on newer devices with
+different screen configurations. You should be able to make these changes to your application such
+that you can distribute a single {@code .apk} to all devices.</p>
+
+<p>The recommended strategy is to develop your application against Android 1.6 (because it's the
+lowest version that includes support for multiple screens) and test your application on each
+platform version your application supports (especially the minimum platform, such as Android 1.5).
+Here's how to do that:</p>
+
+<ol>
+  <li>Maintain compatibility with existing devices by leaving your application's <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code android:minSdkVersion}</a>
+attribute as it is. You <em>do not</em> need to increment the value of the attribute to support new
+devices and multiple screens. </li>
+  <li>Extend compatibility for Android 1.6 (and higher) devices by adding <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+android:targetSdkVersion}</a> to the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code &lt;uses-sdk&gt;}</a> element.
+Set the value of <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+android:targetSdkVersion}</a> to <code>"4"</code>. This allows your application to "inherit" the
+platform's multiple screens support, even though it is technically using an earlier version of the
+API.
+  <p>Adding this attribute will cause an error in the compiler, because the attribute is unknown to
+Android 1.5. You'll fix this next.</p></li>
+  <li>Change your application's build properties, such that it compiles against the Android 1.6 (API
+Level 4) library, rather than against the Android 1.5 (or earlier) library. You must do this in
+order for your application to successfully compile when using the new manifest attributes. Older
+versions of the platform simply ignore the attributes they don't know, so your application still
+runs fine on them, as long as you don't use APIs in your application code from Android 1.6. </li>
+</ol>
+
+<p>Your application is now prepared to run on multiple screens, while maintaining support for
+Android 1.5 or lower.</p>
+
+<p>Here's how you should begin testing your application:</p>
+
+<ol>
+  <li>Set up AVDs for testing your application on Android 1.6 and some new versions. Create AVDs
+that use the screen sizes and densities that you want to support. When you create the AVDs, make
+sure to select the Android 1.6 or higher platform as the system image to use. For more information,
+see <a href="{@docRoot}guide/practices/screens_support.html#testing">How to Test Your Application on
+Multiple Screens</a>.</li>
+  <li>Set up AVDs for testing your application on older versions of the platform, as low as the
+version declared by your <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code android:minSdkVersion}</a>.
+You need AVDs running the older platforms you are targeting, so that you can ensure there are
+no functional regressions.</li>
+  <li>Compile your application against the Android 1.6 library and run it on the AVDs you created.
+Observe the way your application looks and runs, and test all of the user interactions.</li>
+  <li>Debug any display or functional issues. For issues that you resolve in
+your application code, <span style="color:red">make certain not to use any APIs
+introduced later than the version declared by your <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+android:minSdkVersion}</a></span>. If you
+are in doubt, refer to SDK reference documentation and look for the API Level specifier for the API
+you want to use. Using newer APIs not supported by your minimum version will mean that your
+application will no longer be compatible with devices running on that version.</li>
+</ol>
+
+<p>In particular, remember to test your application on an AVD that emulates a small-screen device.
+Users of devices with QVGA resolution at low density may want to download your application, so you
+should understand how your application will look and function on a small-screen device. In some
+cases, the reduced screen area and density mean that you need to make tradeoffs in design on those
+devices.</p>
+
+<p>Also give extra attention to testing your application on an AVD that emulates an <em>xlarge</em>
+screen. Devices with extra large screens are tablet-sized or larger, so you should pay close
+attention to how usable your application is on such screens. You might want to design new layouts
+specifically for extra large screens, to address usability aspects such as the location and size of
+buttons in your UI. To test your application on an extra large screen, create an AVD targeted to
+Android 3.0 and use the WXGA emulator skin.</p>
+
+<p>Once you've completed the procedures above, you should follow the recommendations in <a
+href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a> to add
+complete support for different screen configurations.</p>
+
+
+<h2 id="CompatMode">Compatibility Mode</h2>
+
+<div class="figure" style="width:450px;margin:0">
+<img src="{@docRoot}images/screens_support/compat-mode-on.png" alt="" />
+<p class="img-caption"><strong>Figure 1.</strong> An application running in compatibility mode
+on an extra large screen.</p>
+</div>
+
+<p>To allow applications to run on larger screens without stretching the UI, Android provides a
+compatibility mode that draws an application's UI in a "postage stamp" window when on larger
+screens. That is, the system displays the application at the baseline size (<em>normal</em>) and
+density (<em>mdpi</em>), with a black border that fills the rest of the screen.</p>
+
+<p>Compatibility mode exists primarily to support application's developed for Android 1.5 (or lower)
+when running on larger screens, because multiple screen support was not added until Android 1.6,
+older applications were not designed to support different screen configurations.</p>
+
+<p>As such, if you've set your <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+android:minSdkVersion}</a> to {@code "3"} or lower and have <em>not</em> set the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+android:targetSdkVersion}</a> to {@code "4"} or higher, then compatibility mode is enabled and
+the system will not scale your application, because your application implicitly declares that it
+only supports the baseline screen configuration (normal screen size and medium density).</p>
+
+<p>To disable compatibility mode, set either <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+android:minSdkVersion}</a> or <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+android:targetSdkVersion}</a> to {@code "4"} or higher. For more information, see the previous
+section about <a href="#strategies">Adding Multiple Screens Support</a>.</p>
+
+<p>You can also affect whether compatibility mode is enabled by using the <a
+href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
+&lt;supports-screens>}</a> element (you can enable it by setting {@code android:resizeable} or
+specific screen sizes to {@code "false"}). However, you should not explicitly enable compatibility
+mode for your application, but should instead apply the necessary techniques to <a
+href="{@docRoot}guide/practices/screens_support.html">supporting multiple screens</a> and allow your
+application to properly fit the screen on all screen sizes.</p>
diff --git a/docs/html/guide/practices/screens_support.jd b/docs/html/guide/practices/screens_support.jd
index 9875a6ec..14de152 100644
--- a/docs/html/guide/practices/screens_support.jd
+++ b/docs/html/guide/practices/screens_support.jd
@@ -1,5 +1,4 @@
 page.title=Supporting Multiple Screens
-
 @jd:body
 
 <div id="qv-wrapper">
@@ -7,772 +6,359 @@
 
   <h2>Quickview</h2>
   <ul>
-    <li>Android runs on devices that have different screen sizes and resolutions.</li>
+    <li>Android runs on devices that have different screen sizes and densities.</li>
     <li>The screen on which your application is displayed can affect its user interface.</li>
-    <li>The platform handles most of the work of adapting your app to the current screen.</li>
-    <li>You can create screen-specific resources for precise control of your UI, if needed. </li>
-    <li>Older applications run in a compatibility mode that provides best-effort rendering on the current screen.</li>
-    <li>It's important to follow the best practices described in this document and test your application in all supported screens. </li>
+    <li>The system handles most of the work of adapting your app to the current screen.</li>
+    <li>You should create screen-specific resources for precise control of your UI. </li>
   </ul>
 
   <h2>In this document</h2>
   <ol>
-    <li><a href="#overview">Overview of Screen Support</a></li>
-    <ol>
-       <li><a href="#range">Range of screens supported</a></li>
-       <li><a href="#support">How Android supports multiple screens</a></li>
-       <li><a href="#density-independence">Density independence</a></li>
-       <li><a href="#attrs">Manifest attributes</a></li>
-       <li><a href="#qualifiers">Resource qualifiers</a></li>
-    </ol>
-    <li style="padding-top:4px;"><a href="#screen-independence">Best Practices for Screen Independence</a></li>
-    <li><a href="#strategies">Strategies for Legacy Apps</a></li>
-    <li><a href="#testing">How to Test Your App</a></li>
-
+    <li><a href="#overview">Overview of Screen Support</a>
+      <ol>
+        <li><a href="#terms">Terms and concepts</a></li>
+        <li><a href="#range">Range of screens supported</a></li>
+        <li><a href="#density-independence">Density independence</a></li>
+      </ol></li>
+    <li><a href="#support">How to Support Multiple Screens</a>
+      <ol>
+        <li><a href="#qualifiers">Using configuration qualifiers</a></li>
+        <li><a href="#DesigningResources">Designing alternative layouts and drawables</a></li>
+      </ol></li>
+    <li><a href="#screen-independence">Best Practices</a></li>
+    <li><a href="#DensityConsiderations">Additional Density Considerations</a>
+      <ol>
+        <li><a href="#scaling">Scaling Bitmap objects created at runtime</a></li>
+        <li><a href="#dips-pels">Converting dp units to pixel units</a></li>
+      </ol></li>
+    <li><a href="#testing">How to Test Your Application on Multiple Screens</a></li>
   </ol>
 
-  <h2>See Also</h2>
+  <h2>Related samples</h2>
   <ol>
-    <li><code><a href="{@docRoot}guide/topics/manifest/supports-screens-element.html">&lt;supports-screens&gt;</a></code></li>
-    <li><code><a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">&lt;uses-sdk&gt;</a></code></li>
+    <li><a href="{@docRoot}resources/samples/MultiResolution/index.html">Multiple
+Resolutions</a></li>
+  </ol>
+
+  <h2>See also</h2>
+  <ol>
     <li><a
 href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">
 Providing Alternative Resources</a></li>
-    <li><a href="{@docRoot}guide/developing/devices/index.html">Creating and Managing Virtual Devices</a></li>
+    <li><a href="{@docRoot}guide/practices/ui_guidelines/icon_design.html">Icon Design
+Guidelines</a></li>
+    <li><a href="{@docRoot}guide/developing/devices/index.html">Managing Virtual Devices</a></li>
   </ol>
 
 </div>
 </div>
 
-<p>Android is designed to run on a variety of devices that offer a range of
-screen sizes and resolutions. For applications, the platform provides a
-consistent environment across devices and handles much of the complexity of
-adapting an application's UI to the screen on which it is being displayed. At
-the same time, the platform exposes APIs that give application developers
-precise control over their application's UI when displayed on specific screen
-sizes and resolutions. </p>
+<p>Android runs on a variety of devices that offer different screen sizes and densities. For
+applications, the Android system provides a consistent development environment across devices and
+handles most of the work to adjust each application's user interface to the screen on which it is
+displayed. At the same time, the system provides APIs that allow you to control your
+application's UI on specific screen sizes and densities, in order to modify and optimize your UI
+design for different screen configurations. For example, you might want a UI for tablets
+that's different from the design for handsets.</p>
 
-<p>This document explains the screens-support features provided by the platform
-and how you use them in your application. By following the practices described
-here, you can easily create an application that displays properly on all
-supported device screens and that you can deploy to any device as a single {@code .apk}.
-</p>
+<p>Although the system performs sufficient scaling and resizing to make your application work on
+different screens, you should make the effort to optimize your application for different screen
+sizes and densities. In doing so, you maximize the user experience for all devices and your users
+believe that your application was actually designed for <em>their</em> devices&mdash;rather than
+simply stretched to fit their devices.</p>
 
-<p>If you have already developed and published an application for Android 1.5 or
-earlier, you should read this document and consider how you may need to adapt
-your application for proper display on new devices that offer different screens
-and that are running Android 1.6 or later. In most cases, only minor adjustments
-are needed, however you should make sure to <a href="#testing">test your
-application</a> on all supported screens. </p>
+<p>By following the practices described in this document, you can create an application that
+displays properly and provides an optimized user experience on all supported screen configurations,
+using a single {@code .apk} file.</p>
 
-<p>Starting in Android 2.2, the platform includes support for extra high density screens
-(<em>xhdpi</em>), and starting in Android 2.3, the platform includes support for extra large screens
-(<em>xlarge</em>). If you've already followed the guidance in this document to support all other
-screen types, you should consider providing additional support for <em>xhdpi</em> and
-<em>xlarge</em> screens.</p>
+<p class="note"><strong>Note:</strong> The information in this document assumes that your
+application is designed for Android 1.6 (API Level 4) or higher. If your application supports
+Android 1.5 or lower, please first read <a
+href="{@docRoot}guide/practices/screens-support-1.5.html">Strategies for Android 1.5</a>.</p>
 
-<p>In particular, if you have an existing application that you would like to
-make available on small screens (such as QVGA) or for which you would like to provide better support
-for extra large screens, please see <a href="#strategies">Strategies for Legacy Applications</a> for
-more information about how to do that. </p>
 
 
 <h2 id="overview">Overview of Screens Support</h2>
 
-<p>The sections below provide an overview of the Android platform's support for
-multiple screens, including an introduction to terms and concepts used in this
-document and in the API, a summary of the screen configurations that the
-platform supports, and an overview of the API and underlying
+<p>This section provides an overview of Android's support for multiple screens, including: an
+introduction to the terms and concepts used in this document and in the API, a summary of the screen
+configurations that the system supports, and an overview of the API and underlying
 screen-compatibility features.</p>
 
-
-<h3>Terms and Concepts</h3>
+<h3 id="terms">Terms and concepts</h3>
 
 <dl>
 <dt><em>Screen size</em></dt>
   <dd>Actual physical size, measured as the screen's diagonal.
 
-  <p>For simplicity, Android collapses all actual screen sizes into four
-generalized sizes: small, normal, large, and extra large. Applications can provide custom
-layouts for each of these four sizes &mdash; the platform transparently handles
-the rendering of the layouts at the actual screen size.</p></dd>
+  <p>For simplicity, Android groups all actual screen sizes into four generalized sizes: small,
+normal, large, and extra large.</p></dd>
 
-<dt><em>Aspect ratio</em></dt>
-  <dd>The porportional relationship of the screen's physical width to its
-height. Applications can provide layout resources for specific aspect ratios by
-using the resource qualifiers <code>long</code> and <code>notlong</code>. </dd>
+<dt><em>Screen density</em></dt>
+  <dd>The quantity of pixels within a physical area of the screen; usually referred to as dpi (dots
+per inch). For example, a "low" density screen has fewer pixels within a given physical area,
+compared to a "normal" or "high" density screen.</p>
+
+  <p>For simplicity, Android groups all actual screen densities into four generalized densities:
+low, medium, high, and extra high.</p></dd>
+
+<dt><em>Orientation</em></dt>
+  <dd>The orientation of the screen from the user's point of view. This is either landscape or
+portrait, meaning that the screen's aspect ratio is either wide or tall, respectively. Be aware
+that not only do different devices operate in different orientations by default, but the
+orientation can change at runtime when the user rotates the device.
+</dd>
 
 <dt><em>Resolution</em></dt>
-  <dd>The total number of physical pixels on a screen. Note that, although
-resolution is often expressed as <em>width</em> x <em>height</em>, resolution
-does not imply a specific aspect ratio. In Android, applications do not work
-directly with resolution.</dd>
+  <dd>The total number of physical pixels on a screen. When adding support for multiple screens,
+applications do not work directly with resolution; applications should be concerned only with screen
+size and density, as specified by the generalized size and density groups.</dd>
 
-<dt><em>Density</em></dt>
-  <dd>Based on the screen resolution, the spread of pixels across the physical
-width and height of the screen.
-
-  <p>A screen with lower density has fewer available pixels spread across the
-screen width and height, where a screen with higher density has more &mdash;
-sometimes significantly more &mdash; pixels spread across the same area. The
-density of a screen is important because, other things being equal, a UI element
-(such as a button) whose height and width are defined in terms of screen pixels
-will appear larger on the lower density screen and smaller on the higher density
-screen.</p>
-
-  <p>For simplicity, Android collapses all actual screen densities into four
-generalized densities: low, medium, large, and extra large. Applications can provide custom
-resources for each of these densities &mdash; the platform handles any necessary
-scaling of the resources up or down to meet the specific screen density. </p></dd>
 <dt><em>Density-independent pixel (dp)</em></dt>
-  <dd>A virtual pixel unit that applications can use in defining their UI, to
-express layout dimensions or position in a density-independent way.
-  <p>The density-independent pixel is equivalent to one physical pixel on a 160
-dpi screen, the baseline density assumed by the platform (as described later in
-this document). At run time, the platform transparently handles any scaling of
-the dp units needed, based on the actual density of the screen in use. The
-conversion of dp units to screen pixels is simple: <nobr><code>pixels = dps *
-(density / 160)</code></nobr>. For example, on 240 dpi screen, 1 dp would equal 1.5
-physical pixels. Using dp units to define your application's UI is highly
-recommended, as a way of ensuring proper display of your UI on different
-screens. </p></dd>
+  <dd>A virtual pixel unit that you should use when defining UI layout, to express layout dimensions
+or position in a density-independent way.
+  <p>The density-independent pixel is equivalent to one physical pixel on a 160 dpi screen, which is
+the baseline density assumed by the system for a "medium" density screen. At runtime, the system
+transparently handles any scaling of the dp units, as necessary, based on the actual density of the
+screen in use. The conversion of dp units to screen pixels is simple: 
+<nobr><code>px = dp * (dpi / 160)</code></nobr>.
+For example, on a 240 dpi screen, 1 dp equals 1.5 physical pixels. You should always use dp units
+when defining your application's UI, to ensure proper display of your UI on screens with different
+densities. </p></dd>
 </dl>
 
 
 <h3 id="range">Range of screens supported</h3>
 
-<p>Starting from Android 1.6, the platform provides support for multiple screen
-sizes and resolutions, reflecting the many new types and sizes of devices on
-which the platform runs. If you are developing an application that will run
-on Android 1.6 or later, you can use the compatibility features of the Android
-platform to ensure that your application UI renders properly across the range of
-supported screen sizes and resolutions.</p>
+<p>Starting with Android 1.6 (API Level 4), Android provides support for multiple screen sizes and
+densities, reflecting the many different screen configurations that a device may have. You can use
+features of the Android system to optimize your application's user interface for each screen
+configuration and ensure that your application not only renders properly, but provides the best
+user experience possible on each screen.</p>
 
-<p>To simplify the way that developers design their user interfaces for
-multiple devices and to allow more devices to participate without affecting
-applications, the platform divides the range of actual supported screen sizes
-and resolutions into:</p>
+<p>To simplify the way that you design your user interfaces for multiple screens, Android divides
+the range of actual screen sizes and densities into:</p>
 
 <ul>
-<li>A set of four generalized sizes: <em>small</em>, <em>normal</em>, <em>large</em>,
+<li>A set of four generalized <strong>sizes</strong>: <em>small</em>, <em>normal</em>,
+<em>large</em>,
 and <em>xlarge</em></em>
-<li>A set of four generalized densities: <em>ldpi</em> (low), <em>mdpi</em> (medium),
+<li>A set of four generalized <strong>densities</strong>: <em>ldpi</em> (low), <em>mdpi</em>
+(medium),
 <em>hdpi</em> (high), and <em>xhdpi</em> (extra high)
 </ul>
 
-<p class="note"><strong>Note:</strong> The <code>xhdpi</code> density category was added in
-Android 2.2 (API Level 8). The <em>xlarge</em> size category was added in Android 2.3 (API Level
-9).</p>
+<p>The generalized sizes and densities are arranged around a
+baseline configuration that is a <em>normal</em> size and <em>mdpi</em> (medium) density. This
+baseline is based upon the screen configuration for the first Android-powered device, the T-Mobile
+G1, which has an HVGA screen (until Android 1.6, this was the only screen configuration that Android
+supported).</p>
 
-<p>Applications can provide custom resources (primarily layouts) for any of the
-four generalized sizes and can provide resources (primarily drawables such as
-images) for any of the four generalized densities. Applications do not need to
-work with the actual physical size or density of the device screen. At run time,
-the platform handles the loading of the correct size or density resources, based
-on the generalized size or density of the current device screen, and adapts them
-to the actual pixel map of the screen.</p>
+<p>Each generalized size or density spans a range of actual screen sizes or density. For example,
+two devices that both report a screen size of <em>normal</em> might have actual screen sizes and
+aspect ratios that are slightly different when measured by hand. Similarly, two devices that report
+a screen density of <em>hdpi</em> might have real pixel densities that are slightly different.
+Android makes these differences abstract to applications, so you can provide UI designed for the
+generalized sizes and densities and let the system handle any final adjustments as necessary. Figure
+1 illustrates how different sizes and densities are roughly categorized into the different size
+and density groups.</p>
 
-<p>The generalized size/density configurations are arranged around a
-baseline configuration that is assigned a size of <em>normal</em> and a density of
-<em>mdpi</em> (medium). All applications written for Android 1.5 or earlier are (by
-definition) designed for the baseline HVGA screen used on the T-Mobile G1 and
-similar devices, which is size <em>normal</em> and density
-<em>mdpi</em>.</p>
-
-<p>Each generalized screen configuration spans a range of actual screen
-densities and physical sizes. For example, that means that multiple devices that
-report a screen size of <em>normal</em> might offer screens that differ slightly
-in actual size or aspect ratio. Similarly, devices that report a screen density
-of <em>hdpi</em> might offer screens with slightly different pixel densities.
-The platform makes these differences abstract, however &mdash; applications can
-offer UI designed for the generalized sizes and densities and let the system
-handle the actual rendering of the UI on the current device screen according to
-its characteristics. </p>
-
-<img src="{@docRoot}images/screens_support/screens-ranges.png" />
+<img src="{@docRoot}images/screens_support/screens-ranges.png" style="padding:1em 0 0" alt="" />
 <p class="img-caption"><strong>Figure 1.</strong>
-Illustration of how the Android platform maps actual screen densities and sizes
-to generalized density and size configurations. </p>
+Illustration of how Android roughly maps actual sizes and densities
+to generalized sizes and densities (figures are not exact).</p>
 
-<p>Layout designs often need to be done against a minimum amount of
-available space, so each screen size bucket has an associated minimum size.
-These sizes are in "dp" units -- the same units you should use in defining
-your layouts, which allow us to avoid worrying about changes in screen density.</p>
+<p>To optimize your application's UI for the different screen sizes and densities, you can provide
+<a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">alternative
+resources</a> for any of the generalized sizes and densities. Typically, you should
+provide alternative layouts for some of the different screen sizes and alternative bitmap images for
+different screen densities. At runtime, the system uses the appropriate size or density resources
+for your application, based on the generalized size or density of the current device screen.</p>
 
-<ul>
-<li> <em>xlarge</em> screens are at least 960dp x 720dp.
-<li> <em>large</em> screens are at least 640dp x 480dp.
-<li> <em>normal</em> screens are at least 470dp x 320dp.
-<li> <em>small</em> screens are at least 426dp x 320dp.
-</ul>
+<p>You do not need to provide alternative resources for every combination of screen size and
+density. The system provides robust compatibility features that can handle most of the work of
+rendering your application on any device screen, provided that you've implemented your UI using
+techniques that allow it to gracefully resize (as described in the <a
+href="#screen-independence">Best Practices</a>, below).</p>
 
-<p>Note that these minimum screen sizes were not
-as well defined prior to Android 3.0, so you may encounter some devices
-that are mis-classified between normal and large.  These are also based
-on the physical resolution of the screen, so may vary across devices --
-for example a 1024x720 tablet with a system bar would actually have a bit
-less space available to the application due to it being used by the system
-bar.  Android does not currently support screens smaller than the "small"
-426dp x 320dp size.</p>
+<p class="note"><strong>Note:</strong> The characteristics that define a device's generalized screen
+size and density are independent from each other. For example, a WVGA high-density screen is
+considered a normal size screen because its physical size is about the same as the T-Mobile G1
+(Android's first device and baseline screen configuration). On the other hand, a WVGA medium-density
+screen is considered a large size screen. Although it offers the same resolution (the same number of
+pixels), the WVGA medium-density screen has a lower screen density, meaning that each pixel is
+physically larger and, thus, the entire screen is larger than the baseline (normal size) screen.</p>
 
-<p>Although the platform lets your application provide customized resources for
-the various size and density configurations, you do not need to do write
-custom code or provide custom resources for every combination of screen size and density.
-The platform provides robust compatibility features, described
-in the sections below, that can handle most of the work of rendering your
-application on any device screen, provided that you've implemented your
-application UI properly. For more information about how to implement a UI that
-renders properly across device screens and platform versions, see
-<a href="#screen-independence">Best Practices for Screen Independence</a>.</p>
-
-<p>To help you test your applications, the Android SDK includes emulator skins
-that replicate the sizes and densities of actual device screens on which your
-application is likely to run. You can also modify the default size and density
-of the emulator skins to replicate the characteristics of any specific
-screen.</p>
-
-<p class="table-caption" id="screens-table"><strong>Table 1.</strong> Screen
-sizes and densities of emulator skins included in the Android SDK and other
-representative resolutions.</p>
-
-  <table>
-    <tbody>
-    <tr>
-      <td style="border:none"></td>
-      <td style="background-color:#f3f3f3">
-        <nobr>Low density (120), <em>ldpi</em></nobr>
-      </td>
-      <td style="background-color:#f3f3f3">
-        <nobr>Medium density (160), <em>mdpi</em></nobr>
-      </td>
-      <td  style="background-color:#f3f3f3">
-        <nobr>High density (240), <em>hdpi</em><nobr>
-      </td>
-      <td  style="background-color:#f3f3f3">
-        <nobr>Extra high density (320), <em>xhdpi</em><nobr>
-      </td>
-    </tr>
-    <tr>
-      <td  style="background-color:#f3f3f3">
-        <em>Small</em> screen
-      </td>
-      <td style="font-size:.9em;"><strong>QVGA (240x320)</strong></td>
-      </td>
-      <td></td>
-      <td style="font-size:.9em;">480x640</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td style="background-color:#f3f3f3">
-        <em>Normal</em> screen
-      </td>
-      <td style="font-size:.9em;"><strong>WQVGA400 (240x400)</strong>
-        <br><strong>WQVGA432 (240x432)</strong></td>
-      <td style="font-size:.9em;"><strong>HVGA (320x480)</strong></td>
-      <td style="font-size:.9em;"><strong>WVGA800 (480x800)</strong>
-        <br><strong>WVGA854 (480x854)</strong>
-        <br>600x1024</td>
-      <td style="font-size:.9em;">640x960</td>
-    </tr>
-    <tr>
-      <td style="background-color:#f3f3f3">
-        <em>Large</em> screen
-      </td>
-      <td style="font-size:.9em;"><strong>WVGA800** (480x800)</strong>
-        <br><strong>WVGA854** (480x854)</strong></td>
-      <td style="font-size:.9em;"><strong>WVGA800* (480x800)</strong>
-        <br><strong>WVGA854* (480x854)</strong>
-        <br>600x1024</td>
-      <td></td>
-      <td></td>
-    </tr>
-    <tr>
-      <td style="background-color:#f3f3f3">
-        <em>Extra Large</em> screen
-      </td>
-      <td style="font-size:.9em;">600x1024</td>
-      <td style="font-size:.9em;">768x1024<br><strong>WXGA (768x1280)</strong>
-        <br>800x1280</td>
-      <td style="font-size:.9em;">1152x1536<br>1152x1920
-        <br>1200x1920</td>
-      <td style="font-size:.9em;">1536x2048<br>1536x2560
-        <br>1600x2560</td>
-    </tr>
-    <tr>
-      <td colspan="4" style="border:none;font-size:90%;">* To emulate this
-        configuration, specify a custom density of 160 when
-        creating an AVD that uses a WVGA800 or WVGA854 skin.
-      </td>
-    </tr>
-    <tr>
-      <td colspan="4" style="border:none;font-size:90%;">** To emulate this
-        configuration, specify a custom density of 120 when
-        creating an AVD that uses a WVGA800 or WVGA854 skin.
-      </td>
-    </tr>
-</table>
-
-<p>For an overview of the relative numbers of high (hdpi), medium (mdpi), and
-low (ldpi) density screens in Android-powered devices available now, see the <a
-href="{@docRoot}resources/dashboard/screens.html">Screen Sizes and Densities</a> dashboard.</p>
-
-
-<h3 id="support">How Android supports multiple screens</h3>
-
-<div class="sidebox-wrapper">
-<div class="sidebox">
-<h2>Using the alternative resources framework</h2>
-
-<p>The platform's support for loading screen size- and density-specific
-resources at run time is based on the alternative resources framework.
-
-<p> If you want to use size- or density-specific layouts or drawables in your
-application and you are not familiar with resource qualifiers or how the
-platform uses them, please read
-<a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">
-Providing Alternative Resources</a>.
-</div>
-</div>
-
-<p>The foundation of Android's support for multiple screens is a set of built-in
-compatibility features that together manage the rendering of application
-resources in an appropriate way for the current device screen. The platform
-handles most of the work of rendering your application, but also gives you two
-key ways to control how your application is displayed, if you need or want
-to use them:</p>
-
-<ul>
-  <li>The platform supports a set of resource qualifiers that let you provide
-size- and density-specific resources, if needed. The qualifiers for
-size-specific resources are <code>small</code>, <code>normal</code>, <code>large</code>, and
-<code>xlarge</code>. Those for density-specific resources are <code>ldpi</code>
-(low), <code>mdpi</code> (medium), <code>hdpi</code> (high), and <code>xhdpi</code> (extra high).
-The qualifiers correspond to the generalized densities described in
-<a href="#range">Range of screens supported</a>, above.</li>
-  <li>The platform also provides a
-<a href="{@docRoot}guide/topics/manifest/supports-screens-element.html">
-<code>&lt;supports-screens&gt;</code></a>
-manifest element, whose attributes
-<code>android:smallScreens</code>, <code>android:normalScreens</code>,
-<code>android:largeScreens</code>, and <code>android:xlargeScreens</code> let you specify what
-generalized screen sizes
-your application supports. Another attribute, <code>android:anyDensity</code>,
-lets you indicate whether or not your application includes built-in support for
-multiple densities.</li>
-</ul>
-
-<p>At run time, the platform provides three types of support to your
-application, to ensure the best possible display on the current device
-screen:</p>
-
-<ol>
-<li><em>Pre-scaling of resources (such as image assets)</em>
-
-  <p>Based on the density of the current screen, the platform automatically
-loads any size- or density-specific resources from your application and displays
-them without scaling. If no matching resources are available, the platform loads
-the default resources and scales them up or down as needed to match the current
-screen's generalized density. The platform assumes that default resources are
-designed for proper display at the baseline screen density of "medium" (160),
-unless they are loaded from a density-specific resource directory.</p>
-
-  <p>For example, if the current screen's density is "high", the platform loads
-resources that are tagged with the qualifier <code>hdpi</code> and uses them
-without scaling. If no such resources are available, the platform uses the
-default resources instead, scaling them from the baseline density ("medium") to
-"high".  </p>
-
-  <p>For more information about how to create size- and density-specific
-resources, see <a href="#qualifiers">Resource qualifiers</a>.</p></li>
-
-<li><em>Auto-scaling of pixel dimensions and coordinates</em>
-
-  <p>If the application states that it does not support different screen
-densities, the platform auto-scales any absolute pixel coordinates, pixel
-dimension values, and pixel math used in the application (such as might be used
-for specifying the width or padding for a view). It does this to ensure that
-pixel-defined screen elements are displayed at approximately the same physical
-size as they would be at the baseline density of "medium" (160). The platform
-handles this scaling transparently to the application and also reports scaled
-overall pixel dimensions to the application, rather than physical pixel
-dimensions. </p>
-
-  <p>For instance, suppose a given device is using a WVGA high-denisty screen,
-which is 480x800 and about the same size as a traditional HVGA screen, but it's
-running an app that states that it does not support multiple densities. In this
-case, the system will "lie" to the application when it queries for screen
-dimensions, and report 320x533. Then, when the app does drawing operations, such
-as invalidating the rectangle from (10,10) to (100, 100), the system will
-likewise automatically transform the coordinates by scaling them the appropriate
-amount, and actually invalidate the region (15,15) to (150, 150).  The same
-thing happens in the other direction, if the application is running on a
-lower-density screen, coordinates are scaled down.<p>
-
-  <p>For more information, see the <code>android:anyDensity</code> attribute in
-<a href="#attrs">Manifest attributes for screens support</a>.</p></li>
-
-<div class="sidebox-wrapper" xstyle="margin-bottom:2em;margin-top:.5em;width:90%;">
-  <img id="rule" src="{@docRoot}assets/images/grad-rule-qv.png">
-  <div id="qv-sub-rule">
-    <img src="{@docRoot}assets/images/icon_market.jpg" style="float:left;margin:0;padding:0;">
-    <p style="color:#669999;">Publishing to Small Screen Devices</p>
-    <p>To ensure the best experience for users on small-screen devices, Android
-Market only shows applications that explicitly declare support for small
-screens. If you developed an application on Android 1.5 or earlier and published
-it on Android Market, you need to <a href="#testing">test your application</a>
-on small screens and then upload an updated version that explicitly
-<a href="#attrs">indicates support for small screens</a>. </p>
-  </div>
-</div>
-
-<li><em>Compatibility-mode display on larger screen-sizes</em>
-
-  <p>If the current screen's size is larger than your application supports, as
-specified in the <code>supports-screens</code> element, the platform displays
-the application at the baseline size ("normal") and density ("medium). For
-screens larger than baseline, the platform displays the application in a
-baseline-sized portion of the overall screen, against a black background. </p>
-
-  <p>For instance, suppose a given device is using a WVGA medium density screen,
-classified as a "large" screen, but the application states that it does not
-support large screens; in this case, the system will again "lie" to the
-application when it queries for screen dimensions, and report 320x480.  Instead
-of scaling the application, however, the application's 320x480 interface will be
-placed as a "postage stamp" in the larger 480x800 screen.</p>
-
-  <p>For more information, see the <code>android:anyDensity</code> attribute in
-<a href="#attrs">Manifest elements for screens support</a> and the
-<a href="#compatibility-examples">Screen-Compatibility Examples</a>
-section.</p></li>
-</ol>
-
-<p>In general, these compatibility features ensure that all applications,
-including those written against Android 1.5 and earlier platform versions, can
-display properly on most devices, especially when the device's screen is at the
-baseline "normal" size or larger. </p>
-
-<p>However, note that applications written for the baseline screen may need
-minor adjustments before they display properly on smaller screens such as QVGA.
-With the reduced screen area of small screens, there may be tradeoffs in design,
-content, and function that you, as the application developer, need to consider.
-For more information about how to prepare an existing application for display on
-small screens, see <a href="#strategies">Strategies for Legacy
-Applications</a>.</p>
-
-<p>The sections below provide more information how to take advantage of the
-platform's multiple-screens support. </p>
 
 
 <h3 id="density-independence">Density independence</h3>
 
-<p>The goal of density independence is to preserve the physical size, from the
-user's point of view, of user interface elements declared in an application,
-when the application is displayed on screens with different densities. Density
-independence applies to both layouts and drawables such as icons. Maintaining
-density-independence is important because, other things being equal, a UI
-element (such as a button) whose height and width are defined in terms of screen
-pixels will appear physically larger on the lower density screen and smaller on
-the higher density screen. Such density-related size changes can cause problems
-in application layout, usability, and consistency with other applications
-installed on the device.</p>
+<p>Your application achieves "density independence" when it preserves the physical size (from
+the user's point of view) of user interface elements when displayed on screens with different
+densities.</p>
 
-<p>The platform provides density independence to applications by default. It
-does this in three ways: </p>
+<p>Maintaining density independence is important because, without it, a UI element (such as a
+button) appears physically larger on a low density screen and smaller on a high density screen. Such
+density-related size changes can cause problems in your application layout and usability. Figures 2
+and 3 show the difference between an application when it does not provide density independence and
+when it does, respectively.</p>
+
+<img src="{@docRoot}images/screens_support/density-test-bad.png" alt=""  />
+<p class="img-caption"><strong>Figure 2.</strong> Example application without support for
+different densities, as shown on low, medium, and high density screens.</p>
+
+<img src="{@docRoot}images/screens_support/density-test-good.png" alt="" />
+<p class="img-caption"><strong>Figure 3.</strong> Example application with good support for
+different densities (it's density independent), as shown on low, medium, and high
+density screens.</p>
+
+<p>The Android system helps your application achieve density independence in two ways: </p>
 
 <ul>
-<li>Through pre-scaling of drawable resources (scaled at resource loading
-time)</li>
-<li>Through auto-scaling of density-independent pixel (dp) values used in
-layouts</li>
-<li>Through auto-scaling of absolute pixel values used in the application (only
-needed if the application has set <code>android:anyDensity="false"</code> in its
-manifest)</li>
+<li>The system scales dp units as appropriate for the current screen density</li>
+<li>The system scales drawable resources to the appropriate size, based on the current screen
+density, if necessary</li>
 </ul>
 
-<p>The example screens below illustrate the density independence provided by the
-platform. Note that both the layouts and launcher icons are displayed at the
-same physical sizes, although screen sizes, aspect ratios, and densities are
-different.</p>
-
-
-<div id=vi09 style=TEXT-ALIGN:left>
-<img src="{@docRoot}images/screens_support/dip.png" style="padding-bottom:0;margin-bottom:0;" />
-<p class="caption" style="margin:0 0 1.5em 1em;padding:0 0 0
-1em;"><strong>Figure 2.</strong> Examples of density independence on WVGA high
-density (left), HVGA medium density (center), and QVGA low density (right). </p>
-</div>
-
-<p>In most cases, you can take advantage of density independence in your
-application simply by making sure that your layouts specify all dimension values
-in density-independent pixels (<code>dp</code> or <code>dp</code>) or
-scale-independent pixels (<code>sip</code> or <code>sp</code>, for text only).
-If you are using absolute pixel values in the application and manifest includes
-<a href="#attrs"><code>android:anyDensity="true"</code></a>, you will also need
-to scale the pixel values. See <a href="#dips-pels">Converting dp units to
-pixel units</a> for more information. </p>
-
-
-<h3 id="attrs">Manifest attributes for screens support</h3>
-
-<p> Android 1.6 introduced a new manifest element,
-<a href="{@docRoot}guide/topics/manifest/supports-screens-element.html"><code>&lt;supports-screens&gt;</code></a>,
-whose attributes you can use to control the
-display of your application on different classes of device screens, as listed
-in table 2. The <code>smallScreens</code>, <code>normalScreens</code>, <code>largeScreens</code> and
-<code>xlargeScreens</code> attributes correspond to the generalized screen sizes
-described in <a href="#range">Range of screens supported</a>, earlier in this
-document. Notice that the default values for each attribute vary, depending
-on your minimum and targeted platform, as indicated in the <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code
-android:minSdkVersion}</a> and <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code
-android:targetSdkVersion}</a> attributes of your <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code &lt;uses-sdk&gt;}</a>
-manifest element.</p>
-
-<p class="table-caption" id="table2"><strong>Table 2.</strong> Summary of attributes for the <a
-href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
-&lt;supports-screens&gt;}</a> manifest element, including default values based on platform
-version.</p>
-    <table id="vrr8">
-      <tr>
-        <th>
-          Attribute
-        </th>
-        <th >
-          Description
-        </th>
-        <th>
-          Default value, when<br><nobr><code>minSdkVersion</code> or</nobr>
-<code>targetSdkVersion</code> is 4 or lower
-        </th>
-        <th>
-          Default value, when<br><nobr><code>minSdkVersion</code> or</nobr>
-<code>targetSdkVersion</code> is 5 or higher
-        </th>
-      </tr>
-      <tr>
-        <td>
-          <code>android:smallScreens</code>
-        </td>
-        <td>
-          Whether or not the application UI is designed for use on
-<em>small</em> screens &mdash; "<code>true</code>" if it is, and
-"<code>false</code>" if not. </p>
-        </td>
-<td>"<code>false</code>"</td>
-<td>"<code>true</code>"</td>
-      </tr>
-      <tr>
-        <td>
-          <code>android:normalScreens</code>
-        </td>
-        <td>
-           Whether or not the application UI is designed for use on
-<em>normal</em> screens &mdash; "<code>true</code>" if it is, and
-"<code>false</code>" if not. The default value is always "<code>true</code>".
-        </td>
-<td>"<code>true</code>"</td>
-<td>"<code>true</code>"</td>
-      </tr>
-      <tr>
-        <td>
-          <code>android:largeScreens</code>
-        </td>
-        <td>
-           Whether or not the application UI is designed for use on
-<em>large</em> screens &mdash; "<code>true</code>" if it is, and
-"<code>false</code>" if not.
-        </td>
-<td>"<code>false</code>"</td>
-<td>"<code>true</code>"</td>
-      </tr>
-      <tr>
-        <td>
-          <code>android:anyDensity</code>
-        </td>
-        <td>
-         <p>Whether or not the application is designed to manage its UI properly
-in different density environments &mdash; "<code>true</code>" if so, and
-"<code>false</code>" if not. </p>
-        <ul>
-          <li>If set to "<code>true</code>", the platform disables its
-density-compatibility features for all screen densities &mdash; specifically,
-the auto-scaling of absolute pixel units (<code>px</code>) and math &mdash; and
-relies on the application to use density-independent pixel units
-(<code>dp</code>) and/or math to manage the adaptation of pixel values according
-to density of the current screen. That is, as long as your application uses
-density-independent units (dp) for screen layout sizes, then it will perform
-properly on different densities when this attribute is set to
-"<code>true</code>".</li>
-
-          <li>If set to "<code>false</code>", the platform enables its
-density-compatibility features for all screen densities. In this case, the
-platform provides a scaled, virtual screen pixel map to the application, against
-which it can layout and draw its UI as though against a medium-density screen
-(160). The platform then transparently auto-scales the application's pixel units
-and math as needed to match the actual device screen density. </li>
-        </ul>
-<p>Note that the setting of this attribute affects density-compatibility only.
-It does not affect size-compatibility features such as display on a virtual
-baseline screen.</p>
-        </td>
-<td>"<code>false</code>"</td>
-<td>"<code>true</code>"</td>
-      </tr>
-      <tr>
-        <td colspan="4"><strong>Note:</strong> Android 2.3 (API Level 9) introduced a new
-attribute for the <code>&lt;supports-screens&gt;</code> element: <code>xlargeScreens</code>, shown
-below. It works the same as the other screen attributes above, but, if neither your
-<code>minSdkVersion</code> or <code>targetSdkVersion</code> are set to "9", the default value is
-"false" when your application is installed on a device running Android 2.3.</td>
-      </tr>
-      <tr>
-        <th>
-          Attribute
-        </th>
-        <th >
-          Description
-        </th>
-        <th>
-          Default value, when<br><nobr><code>minSdkVersion</code> or</nobr>
-<code>targetSdkVersion</code> is 8 or lower
-        </th>
-        <th>
-          Default value, when<br><nobr><code>minSdkVersion</code> or</nobr>
-<code>targetSdkVersion</code> is 9 or higher
-        </th>
-      </tr>
-      <tr>
-        <td>
-          <code>android:xlargeScreens</code>
-        </td>
-        <td>
-           Whether or not the application UI is designed for use on
-<em>xlarge</em> screens &mdash; "<code>true</code>" if it is, and
-"<code>false</code>" if not.
-        </td>
-<td>"<code>false</code>"</td>
-<td>"<code>true</code>"</td>
-      </tr>
-    </table>
-
-<p>In general, when you declare a screen-size attribute
-(<code>smallScreens</code>, <code>normalScreens</code>, <code>largeScreens</code>, or
-<code>xlargeScreens</code>) as "<code>true</code>", you are signaling to the
-platform that your application is designed to render properly on that screen
-size. As a result, the platform does not apply any size-compatibility features
-(such as a virtual HVGA display area). If you declare a screen-size attribute as
-"<code>false</code>", you are signaling that your application is <em>not</em>
-designed for that screen size. In this case, the platform <em>does</em> apply
-size-compatibility features, rendering the application in an HVGA baseline
-display area. If the current screen is larger than <em>normal</em> size, the
-platform renders the application in a virtual HVGA screen on the larger screen.
-See <a href="#compatibility-examples">Screen-Compatibility Examples</a> for an
-illustration of what an application looks like when displayed in a virtual HVGA
-screen.</p>
-
-<p>In other words, setting a <code>&lt;supports-screens&gt;</code> attribute to
-"<code>false</code>" tells the platform to enable it's compatibility features
-when displaying the application on a screen of that size <em>or any larger
-size</em>, if also disallowed. Otherwise, the platform gives the application a
-normal display area that can use the full device screen area, if
+<p>In figure 2, the text view and bitmap drawable have dimensions specified in pixels ({@code px}
+units), so the elements are physically larger on a low density screen and smaller on a high density
+screen. This is because although the actual screen sizes may be the same, the high density screen
+has more pixels per inch (the same amount of pixels fit in a smaller area). In figure 3, the layout
+dimensions are specified in density-independent pixels ({@code dp} units). Because the baseline for
+density-independent pixels is a medium-density screen, the device with a medium-density screen looks
+the same as it does in figure 2. For the low-density and high-density screens, however, the system
+scales the density-independent pixel values down and up, respectively, to fit the screen as
 appropriate.</p>
 
-<p>Android Market also makes use of the <code>&lt;supports-screens&gt;</code>
-attributes. It uses them to filter the application from devices whose screens
-are not supported by the application. Specifically, Android Market considers an
-application compatible with a device if the application supports a screen that
-is the same or smaller than the current device screen. Android Market filters
-the application if it disallows the device's screen size and does not support a
-smaller size. In general, Android does not provide downward size-compatibility
-features for applications.</p>
+<p>In most cases, you can ensure density independence in your application simply by specifying all
+layout dimension values in density-independent pixels (<code>dp</code> units) or with {@code
+"wrap_content"}, as appropriate. The system then scales bitmap drawables as appropriate in order to
+display at the appropriate size, based on the appropriate scaling factor for the current screen's
+density.</p>
 
-<p>Here are some examples:</p>
+<p>However, bitmap scaling can result in blurry or pixelated bitmaps, which you might notice in the
+above screenshots. To avoid these artifacts, you should provide alternative bitmap resources for
+different densities. For example, you should provide higher-resolution bitmaps for high-density
+screens and the system will use those instead of resizing the bitmap designed for medium-density
+screens. The following section describes more about how to supply alternative resources for
+different screen configurations.</p>
+
+
+
+<h2 id="support">How to Support Multiple Screens</h2>
+
+<p>The foundation of Android's support for multiple screens is its ability to manage the rendering
+of an application's layout and bitmap drawables in an appropriate way for the current screen
+configuration. The system handles most of the work to render your application properly on each
+screen configuration by scaling layouts to fit the screen size/density and scaling bitmap drawables
+for the screen density, as appropriate. To more gracefully handle different screen configurations,
+however, you should also:</p>
 
 <ul>
-    <li>Assume that you declare <code>smallScreens="false" normalScreens="true"
-largeScreens="false" xlargeScreens="false"</code> in your application's manifest. <p>Although the
-application is not designed for display on large or extra large screens, the platform can still
-run it successfully in <a href="#compatibility-examples">screen-compatibility
-mode</a>. Android Market shows the application to devices with
-<em>normal</em>, <em>large</em>, and <em>xlarge</em> size screens, but does filter it from
-<em>small</em> size screens, because the application provides no screen support at
-<em>small</em> size. Android's <a href="#compatibility-examples">screen-compatibility
-mode</a> mode does not provide support for screens that are smaller than those the
-application supports&mdash;it only provides support for screens that are larger. Thus,
-although the application declares "false" for <em>large</em> and <em>xlarge</em> screens,
-the application still functions, but runs in compatibility mode.</p></li>
-
-    <li>Assume that you declare <code>smallScreens="false" normalScreens="false"
-largeScreens="true" xlargeScreens="true"</code> in your application's manifest. <p>Android Market
-filters the application from users of devices with <em>small</em> and
-<em>normal</em> size screens. In effect, this prevents such users from
-installing the application.</p></li>
+  <li><strong>Provide different layouts for different screen sizes</strong>
+    <p>By default, Android resizes your application layout to fit the current device screen. In most
+cases, this works fine. In other cases, your UI might not look as good and might need adjustments
+for different screen sizes. For example, on a larger screen, you might want to adjust the position
+and size of some elements to take advantage of the additional screen space, or on a smaller screen,
+you might need to adjust sizes so that everything can fit on the screen.</p>
+    <p>The configuration qualifiers you can use to provide size-specific resources are
+<code>small</code>, <code>normal</code>, <code>large</code>, and <code>xlarge</code>. For
+example, layouts for an extra large screen should go in {@code layout-xlarge/}.</p>
+  </li>
+  
+  <li><strong>Provide different bitmap drawables for different screen densities</strong>
+    <p>By default, Android scales your bitmap drawables ({@code .png}, {@code .jpg}, and {@code
+.gif} files) and Nine-Patch drawables ({@code .9.png} files) so that they render at the appropriate
+physical size on each device. For example, if your application provides bitmap drawables only for
+the baseline, medium screen density (mdpi), then the system scales them up when on a high-density
+screen, and scales them down when on a low-density screen. This scaling can cause artifacts in the
+bitmaps. To ensure your bitmaps look their best, you should include alternative versions at
+different resolutions for different screen densities.</p>
+    <p>The configuration qualifiers you can use for density-specific resources are
+<code>ldpi</code> (low), <code>mdpi</code> (medium), <code>hdpi</code> (high), and
+<code>xhdpi</code> (extra high). For example, bitmaps for high-density screens should go in
+{@code drawable-hdpi/}.</p>
+  </li>
 </ul>
 
-<p>If you declare the <code>android:anyDensity</code> attribute as
-"<code>true</code>", you are signaling to the platform that your application is
-designed to display properly on any screen density. In this case, the
-application must ensure that it declares its UI dimensions using
-density-independent pixels (<code>dp</code>) and scales any absolute pixel
-values (<code>px</code>) or math by the scaling factor available from {@link
-android.util.DisplayMetrics#density android.util.DisplayMetrics.density}. See <a
-href="#dips-pels">Converting dp units to pixel units</a> for an example.</p>
+<p>The size and density configuration qualifiers correspond to the generalized sizes and densities
+described in <a href="#range">Range of screens supported</a>, above.</p>
 
-<p>Note that the setting of the <code>android:anyDensity</code> attribute does
-not affect the platform's pre-scaling of drawable resources, such as bitmaps and
-nine-patch images, which always takes place by default. </p>
+<p class="note"><strong>Note:</strong> If you're not familiar with configuration qualifiers and how
+the system uses them to apply alternative resources, read <a
+href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">Providing
+Alternative Resources</a> for more information.</p>
 
-<p>The following example shows a manifest that declares support for small, normal, large, and
- xlarge screens in any density.</p>
+<p>At runtime, the system ensures the best possible display on the current screen with
+the following procedure for any given resource:</p>
 
-<pre>
-&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"&gt;
-    &lt;supports-screens
-        android:smallScreens="true"
-        android:normalScreens="true"
-        android:largeScreens="true"
-        android:xlargeScreens="true"
-        android:anyDensity="true" /&gt;
-    ...
-&lt;/manifest&gt;
-</pre>
-<!--  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; android:resizeable="true" -->
-<h4 id="defaults">
-  Default values for attributes
-</h4>
+<ol>
+<li>The system uses the appropriate alternative resource
+  <p>Based on the size and density of the current screen, the system uses any size- and
+density-specific resource provided in your application. For example, if the device has a
+high-density screen and the application requests a drawable resource, the system looks for a
+drawable resource directory that best matches the device configuration. Depending on the other
+alternative resources available, a resource directory with the {@code hdpi} qualifier (such as
+{@code drawable-hdpi/}) might be the best match, so the system uses the drawable resource from this
+directory.</p>
+</li>
 
-<p>The default values for the <code>&lt;supports-screens&gt;</code> attributes
-differ, depending on the the value of the
-<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code>android:minSdkVersion</code></a>
- attribute in the application's manifest, as well as on
-the value of <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code
-android:targetSdkVersion}</a>, if declared.</p>
+<li>If no matching resource is available, the system uses the default resource and scales it up
+or down as needed to match the current screen size and density
+  <p>The "default" resources are those that are not tagged with a configuration qualifier. For
+example, the resources in {@code drawable/} are the default drawable resources. The system
+assumes that default resources are designed for the baseline screen size and density, which is a
+normal screen size and a medium density. As such, the system scales default density
+resources up for high-density screens and down for low-density screens, as appropriate.</p>
+  <p>However, when the system is looking for a density-specific resource and does not find it in
+the density-specific directory, it won't always use the default resources. The system may
+instead use one of the other density-specific resources in order to provide better results
+when scaling. For example, when looking for a low-density resource and it is not available, the
+system prefers to scale-down the high-density version of the resource, because the
+system can easily scale a high-density resource down to low-density by a factor of 0.5, with 
+fewer artifacts, compared to scaling a medium-density resource by a factor of 0.75.</p>
+</li>
+</ol>
 
-<p>Above, <a href="#table2">table 2</a> indicates the default values for each attribute, based on
-the values you provide for the <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code
-android:minSdkVersion}</a> and <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code
-android:targetSdkVersion}</a>, in the <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code &lt;uses-sdk&gt;}</a>
-element.</p>
-
-<p class="note"><strong>Note:</strong> If your application uses APIs introduced in Android 1.6 or
-higher, but does not support specific screen densities and/or screen sizes, you need to explicitly
-set the appropriate attributes to "<code>false</code>" (because most are "true", by default).</p>
+  <p>For more information about how Android selects alternative resources by matching configuration
+qualifiers to the device configuration, read
+<a href="{@docRoot}guide/topics/resources/providing-resources.html#BestMatch">How Android
+Finds the Best-matching Resource</a>.</p>
 
 
-<h3 id="qualifiers">Resource directory qualifiers for screen size and density</h3>
 
-<p>Android supports resource directory qualifiers for controlling the selection
-of resources based on the characteristics of the screen on which your application
-is running. You can use these qualifiers to provide size- and density-specific
-resources in your application. For more information about the generalized sizes
-and densities that correspond to the qualifiers, see <a href="#range">Range
-of Screens Supported</a>, earlier in this document.</p>
+
+<h3 id="qualifiers">Using configuration qualifiers</h3>
+
+<p>Android supports several configuration qualifiers that allow you to control how the system
+selects your alternative resources based on the characteristics of the current device screen. A
+configuration qualifier is a string that you can append to a resource directory in your Android
+project and specifies the configuration for which the resources inside are designed.</p>
+
+<p>To use a configuration qualifier:</p>
+<ol>
+  <li>Create a new directory in your project's {@code res/} directory and name it using the
+format: <nobr>{@code &lt;resources_name&gt;-&lt;qualifier&gt;}</nobr>
+    <ul>
+      <li>{@code &lt;resources_name&gt;} is the standard resource name (such as {@code drawable} or
+{@code layout}).</li>
+      <li>{@code &lt;qualifier&gt;} is a configuration qualifier from table 1, below, specifying the
+screen configuration for which these resources are to be used (such as {@code hdpi} or {@code
+xlarge}).</li>
+    </ul>
+    <p>You can use more than one {@code &lt;qualifier&gt;} at a time&mdash;simply separate each
+qualifier with a dash.</p>
+  </li>
+  <li>Save the appropriate configuration-specific resources in this new directory. The resource
+files must be named exactly the same as the default resource files.</li>
+</ol>
+
+<p>For example, {@code xlarge} is a configuration qualifier for extra large screens. When you append
+this string to a resource directory name (such as {@code layout-xlarge}), it indicates to the
+system that these resources are to be used on devices that have an extra large screen.</p>
+
+<p class="table-caption"><strong>Table 1.</strong> Configuration qualifiers that allow you to
+provide special resources for different screen configurations.</p>
 
 <table>
 <tr>
@@ -784,691 +370,627 @@
 <tr>
   <td rowspan="4">Size</td>
   <td><code>small</code></td>
-  <td>Resources designed for <em>small</em> size screens.</td>
+  <td>Resources for <em>small</em> size screens.</td>
 </tr>
 <tr>
   <td><code>normal</code></td>
-  <td>Resources designed for <em>normal</em> size screens.</td>
+  <td>Resources for <em>normal</em> size screens. (This is the baseline size.)</td>
 </tr>
 <tr>
 <td><code>large</code></td>
-<td>Resources designed for <em>large</em> size screens.</td>
+<td>Resources for <em>large</em> size screens.</td>
 </tr>
 <tr>
 <td><code>xlarge</code></td>
-<td>Resources designed for <em>extra large</em> size screens.</td>
+<td>Resources for <em>extra large</em> size screens.</td>
 </tr>
 
 <tr>
 <td rowspan="5">Density</td>
 <td><code>ldpi</code></td>
-<td>Resources designed for low-density (<em>ldpi</em>) screens.</td>
+<td>Resources for low-density (<em>ldpi</em>) screens (~120dpi).</td>
 </tr>
 <tr>
 <td><code>mdpi</code></td>
-<td>Resources designed for medium-density (<em>mdpi</em>) screens.</td>
+<td>Resources for medium-density (<em>mdpi</em>) screens (~160dpi). (This is the baseline
+density.)</td>
 </tr>
 <tr>
 <td><code>hdpi</code></td>
-<td>Resources designed for high-density (<em>hdpi</em>) screens.</td>
+<td>Resources for high-density (<em>hdpi</em>) screens (~240dpi).</td>
 </tr>
 <tr>
 <td><code>xhdpi</code></td>
-<td>Resources designed for extra high-density (<em>xhdpi</em>) screens.</td>
+<td>Resources for extra high-density (<em>xhdpi</em>) screens (~320dpi).</td>
 </tr>
 <tr>
 <td><code>nodpi</code></td>
-<td>Density-independent resources. The platform does not auto-scale resources
-tagged with this qualifier, regardless of the current screen's density.</td>
+<td>Resources for all densities. These are density-independent resources. The system does not
+scale resources tagged with this qualifier, regardless of the current screen's density.</td>
+</tr>
+
+<tr>
+<td rowspan="2">Orientation</td>
+<td><code>land</code></td>
+<td>Resources for screens in the landscape orientation (wide aspect ratio).</td>
+</tr>
+<tr>
+<td><code>port</code></td>
+<td>Resources for screens in the portrait orientation (tall aspect ratio).</td>
 </tr>
 
 <tr>
 <td rowspan="2">Aspect ratio</td>
 <td><code>long</code></td>
-<td>Resources for screens of any size or density that have a significantly
-taller (in portrait mode) and wider (in landscape mode) aspect ratio than the
-baseline screen configuration.</td>
+<td>Resources for screens that have a significantly taller or wider aspect ratio (when in portrait
+or landscape orientation, respectively) than the baseline screen configuration.</td>
 </tr>
 <tr>
 <td><code>notlong</code></td>
-<td>Resources for use only on screens that have an aspect ratio that is similar
-to the baseline screen configuration.</td>
-</tr>
-<tr>
-<td>Platform version</td>
-<td><nobr><code>v&lt;api-level&gt;</code></nobr></td>
-<td>Resources that are for use only on a specific API Level or higher. For
-example, if your application is designed to run on both Android 1.5 (API Level
-3) and Android 1.6 (API Level 4 and higher), you can use the <code>-v4</code>
-qualifier to tag any resources that should be excluded when your application is
-running on Android 1.5 (API Level 3).  </td>
+<td>Resources for use screens that have an aspect ratio that is similar to the baseline screen
+configuration.</td>
 </tr>
 </table>
 
-<p>
-Note that the density and the screen size are independent parameters and are
-interpreted by the system individually. For example, WVGA high density is
-considered a normal screen because its physical size is about the same as one of
-T-Mobile G1. On the other hand, a WVGA medium density screen is considered a
-<i>large</i> screen &mdash; it offers the same resolution but at lower pixel
-density, meaning that it is both physically larger than the baseline screen and
-can display significantly more information than a normal screen size.
-</p>
+<p>For more information about how these qualifiers roughly correspond to real screen
+sizes and densities, see <a href="#range">Range of Screens Supported</a>, earlier in this
+document.</p>
 
-<p>Here is an example of the resource directory structure of an application that
-employs different layout schemes for different screen sizes and supports low and high density
-screens.</p>
+<p>For example, the following is a list of resource directories in an application that
+provides different layout designs for different screen sizes and different bitmap drawables
+for medium, high, and extra high density screens.</p>
 
-<pre>
-res/layout/my_layout.xml            // layout for normal screen size
-res/layout-small/my_layout.xml      // layout for small screen size
-res/layout-large/my_layout.xml      // layout for large screen size
-res/layout-large-land/my_layout.xml // layout for large screen size in landscape mode
-res/layout-xlarge/my_layout.xml     // layout for extra large screen size
+<pre class="classic">
+res/layout/my_layout.xml             // layout for normal screen size ("default")
+res/layout-small/my_layout.xml       // layout for small screen size
+res/layout-large/my_layout.xml       // layout for large screen size
+res/layout-xlarge/my_layout.xml      // layout for extra large screen size
+res/layout-xlarge-land/my_layout.xml // layout for extra large in landscape orientation
 
-res/drawable-lhdpi/my_icon.png      // image for low density
-res/drawable-mdpi/dpi/my_icon.png   // image for medium density
-res/drawable-hdpi/my_icon.png       // image for high density
-
-res/drawable-nodpi/composite.xml    // density independent resource
+res/drawable-mdpi/my_icon.png        // bitmap for medium density
+res/drawable-hdpi/my_icon.png        // bitmap for high density
+res/drawable-xhdpi/my_icon.png       // bitmap for extra high density
 </pre>
 
-<p>For more information about how to use resource qualifiers or how the platform
-selects them, please read
+<p>For more information about how to use alternative resources and a complete list of
+configuration qualifiers (not just for screen configurations), see
 <a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">
 Providing Alternative Resources</a>.</p>
 
+  <p class="note"><strong>Tip:</strong> If you have some drawable resources that the system
+should never scale (perhaps because you perform some adjustments to the image yourself at
+runtime), you should place them in a directory with the {@code nodpi} configuration qualifier.
+Resources with this qualifier are considered density-agnostic and the system will not scale
+them.</p>
 
-<h2 id="screen-independence">Best practices for Screen Independence</h2>
 
-<p>The objective of supporting multiple screens is to create an application that
-can run properly on any display and function properly on any of the generalized
-screen configurations supported by the platform.
-</p>
+<h3 id="DesigningResources">Designing alternative layouts and drawables</h3>
 
-<p>You can easily ensure that your application will display properly on
-different screens. Here is a quick checklist:</p>
+<p>The types of alternative resources you should create depends on your application's needs.
+Usually, you should use the size and orientation qualifiers to provide alternative layout resources
+and use the density qualifiers to provide alternative bitmap drawable resources.</p>
 
-<ol>
-  <li>
-   Use {@code wrap_content}, {@code fill_parent}, or the {@code dp} unit (instead of {@code px}),
-when specifying dimensions in an XML layout file
-  </li>
-  <li>
-    Do not use {@code AbsoluteLayout}
-  </li>
-  <li>
-    Do not use hard coded pixel values in your code
-  </li>
-  <li>
-    Use density and/or resolution specific resources
-  </li>
-</ol>
+<p>The following sections summarize how you might want to use the size and density qualifiers to
+provide alternative layouts and drawables, respectively.</p>
 
-<h3 id="use-relative">1. Use wrap_content, fill_parent, or the dp unit, instead of
-absolute pixels<br> </h3>
 
-<p>When defining the <code>layout_width</code> and <code>layout_height</code> of
-views in an XML layout file, using <code>wrap_content</code>,
-<code>fill_parent</code> or the <code>dp</code> will guarantee that the view is
-given an appropriate size on the current device screen. For instance, a view
-with a <code>layout_width="100dp"</code> will measure 100 pixels wide on an
-HVGA@160 density display and 150 pixels on a WVGA@240 density display, but the
-view will occupy approximately the same physical space. </p>
+<h4>Alternative layouts</h4>
 
-<p>Similarly, you should prefer the <code>sp</code> (scale-independent pixel,
-the scale factor depends on a user setting) or <code>dp</code> (if you don't
-want to allow the user to scale the text) to define font sizes.</p>
+<p>Generally, you'll know whether you need alternative layouts for different screen sizes once
+you test your application on different screen configurations. For example:</p>
 
-<h3 id="avoid-absolute">2. Avoid AbsoluteLayout </h3>
+<ul>
+  <li>When testing on a small screen, you might discover that your layout doesn't quite fit on the
+screen. For example, a row of buttons might not fit within the width of the screen on a small screen
+device. In this case you should provide an alternative layout for small screens that adjusts the
+size or position of the buttons.</li>
+  <li>When testing on an extra large screen, you might realize that your layout doesn't make
+efficient use of the big screen and is obviously stretched to fill it.
+In this case, you should provide an alternative layout for extra large screens that provides a
+redesigned UI that is optimized for bigger screens such as tablets. 
+    <p>Although your application should work fine without an alternative layout on big screens, it's
+quite important to users that your application looks as though it's designed specifically for their
+devices. If the UI is obviously stretched, users are more likely to be unsatisfied with the
+application experience.</p></li>
+  <li>And, when testing in the landscape orientation compared to the portrait orientation, you
+might notice that UI elements placed at the bottom of the screen for the portrait orientation
+should instead be on the right side of the screen in landscape orientation.</li>
+</ul>
 
-<p>{@link android.widget.AbsoluteLayout AbsoluteLayout}
-is one of the layout containers offered by the Android UI toolkit. Unlike the
-other layouts however, <code>AbsoluteLayout</code> enforces the use of fixed
-positions which might easily lead to user interfaces that do not work well on
-different displays. Because of this, <code>AbsoluteLayout</code> was deprecated
-in Android 1.5 (API Level 3). </p>
+<p>To summarize, you should be sure that your application layout:</p>
+<ul>
+  <li>Fits on small screens (so users can actually use your application)</li>
+  <li>Is optimized for bigger screens to take advantage of the additional screen space</li>
+  <li>Is optimized for both landscape and portrait orientations</li>
+</ul>
 
-<p>You can achieve much the same layout by using a
-{@link android.widget.FrameLayout FrameLayout} instead, and setting
-<code>layout_margin</code> attributes of the children. This approach is more
-flexible and will yield better results on different screens.</p>
+<p>If your UI uses bitmaps that need to fit the size of a view even after the system scales
+the layout (such as the background image for a button), you should use <a
+href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">Nine-Patch</a> bitmap files. A
+Nine-Patch file is basically a PNG file in which you specific two-dimensional regions that are
+stretchable. When the system needs to scale the view in which the bitmap is used, the system
+stretches the Nine-Patch bitmap, but stretches only the specified regions. As such, you don't
+need to provide different drawables for different screen sizes, because the Nine-Patch bitmap can
+adjust to any size. You should, however, provide alternate versions of your Nine-Patch files for
+different screen densities.</p>
 
-<h3>3. Do not use hard-coded pixel values in your code</h3>
 
-<p>For performance reasons and to keep the code simpler, the Android framework
-API uses pixels as the standard unit for expressing dimension or coordinate
-values. That means that the dimensions of a View are always expressed in the
-code in pixels. For instance, if <code>myView.getWidth()</code> returns 10, the
-view is 10 pixels wide. In some cases, you may need to scale the pixel values
-that you use in your code. The sections below provide more information. </p>
+<h4>Alternative drawables</h4>
 
-<h4 id="dips-pels">Converting dp units to pixel units</h4>
-
-<p>In some cases, you will need to express dimensions in <code>dp</code> and
-then convert them to pixels. Imagine an application in which a scroll gesture is
-recognized after the user's finger has moved by at least 16 pixels. On a
-baseline screen, the user will have to move his finger by 16 pixels / 160
-dpi = 1/10th of an inch (or 2.5 mm) before the gesture is recognized. On a
-device with a high (240) density display, the user will move his finger by only
-16 pixels / 240 dpi = 1/15th of an inch (or 1.7 mm.) The distance is much
-shorter and the application thus appears more sensitive to the user. To fix this
-issue, the gesture threshold must be expressed in the code in <code>dp</code>
-and then converted to actual pixels.</p>
-
-<pre>// The gesture threshold expressed in dp
-private static final float GESTURE_THRESHOLD_DP = 16.0f;
-
-// Convert the dps to pixels
-final float scale = getContext().getResources().getDisplayMetrics().density;
-mGestureThreshold = (int) (GESTURE_THRESHOLD_DP * scale + 0.5f);</span>
-
-// Use mGestureThreshold as a distance in pixels
-</pre>
-
-<p>The {@link android.util.DisplayMetrics#density android.util.DisplayMetrics.density}
-field specifies the the scale factor you must use to
-convert dps to pixels according to the current screen density. You can access
-the current screen's metrics through a <code>Context</code> or
-<code>Activity</code>. On a medium (160) density screen,
-<code>DisplayMetrics.density</code> equals "1.0", whereas on a high (240)
-density screen it equals "1.5". You can refer to the documentation of the
-{@link android.util.DisplayMetrics DisplayMetrics}
-class for details.</p>
-
-<h4>Use pre-scaled configuration values</h4>
-
-<p>The {@link android.view.ViewConfiguration ViewConfiguration} class can be
-used to access the most common distances, speeds, and times used in the Android
-framework. For instance, the distance in pixels used by the framework as the
-scroll threshold can be obtained as follows:</p>
-
-<pre>ViewConfiguration.get(aContext).getScaledTouchSlop()</pre>
-
-<p>Methods starting with the <code>getScaled</code> prefix are guaranteed to return a value in pixels that will display properly regardless of the current screen density.</p>
-
-<h3>4. Use density and/or size-specific resources</h3>
-
-<div style="float: right;background-color:#fff;margin: 0;padding: 20px 0 20px 20px;">
-<img src="{@docRoot}images/screens_support/scale-test.png" style="padding:0;margin:0;">
-<p class="caption" style="margin:0;padding:0;"><strong>Figure 3.</strong> Comparison of pre-scaled and auto-scaled bitmaps.</p>
+<div class="figure" style="width:223px;margin:0">
+<img src="{@docRoot}images/screens_support/screens-densities.png" alt="" />
+<p class="img-caption"><strong>Figure 4.</strong> Relative sizes for bitmap drawables
+that support each density.</p>
 </div>
 
-<p>Even with the size- and density-compatibility features that the platform
-provides, you may still want to make adjustments to the UI of your application
-when it displayed on certain screen sizes or densities. You can do this by
-providing size- or density-specific resources &mdash; assets, layouts, strings,
-and so on. If you want, you can also take control over the scaling of images
-assets. The sections below provide more information.</p>
+<p>Almost every application should have alternative drawable resources for different screen
+densities, because almost every application has a launcher icon and that icon should look good on
+all screen densities. Likewise, if you include other bitmap drawables in your application (such
+as for menu icons or other graphics in your application), you should provide alternative versions or
+each one, for different densities.</p>
 
-<h4 id="resource-dirs">Custom resources and directories</h4>
+<p class="note"><strong>Note:</strong> You only need to provide density-specific drawables for
+bitmap files ({@code .png}, {@code .jpg}, or {@code .gif}) and Nine-Path files ({@code
+.9.png}). If you use XML files to define shapes, colors, or other <a
+href="{@docRoot}guide/topics/resources/drawable-resource.html">drawable resources</a>, you should
+put one copy in the default drawable directory ({@code drawable/}).</p>
+
+<p>To create alternative bitmap drawables for different densities, you should follow the
+<b>3:4:6:8 scaling ratio</b> between the four generalized densities. For example, if you have
+a bitmap drawable that's 48x48 pixels for medium-density screen (the size for a launcher icon),
+all the different sizes should be:</p>
+
+<ul>
+  <li>36x36 for low-density</li>
+  <li>48x48 for medium-density</li>
+  <li>72x72 for high-density</li>
+  <li>96x96 for extra high-density</li>
+</ul>
+
+<p>For more information about designing icons, see the <a
+href="{@docRoot}guide/practices/ui_guidelines/icon_design.html">Icon Design Guidelines</a>,
+which includes size information for various bitmap drawables, such as launcher icons, menu
+icons, status bar icons, tab icons, and more.</p>
+
+
+
+<h2 id="screen-independence">Best Practices</h2>
+
+<p>The objective of supporting multiple screens is to create an application that can function
+properly and look good on any of the generalized screen configurations supported by Android. The
+previous sections of this document provide information about how Android adapts your
+application to screen configurations and how you can customize the look of your application on
+different screen configurations. This section provides some additional tips and an overview of
+techniques that help ensure that your application scales properly for different screen
+configurations.</p>
+
+<p>Here is a quick checklist about how you can ensure that your application displays properly
+on different screens:</p>
+
+<ol>
+  <li>Use {@code wrap_content}, {@code fill_parent}, or {@code dp} units when specifying
+dimensions in an XML layout file</li>
+  <li>Do not use hard coded pixel values in your application code</li>
+  <li>Do not use {@code AbsoluteLayout} (it's deprecated)</li>
+  <li>Supply alternative bitmap drawables for different screen densities</li>
+</ol>
+
+<p>The following sections provide more details.</p>
+
+
+<h3 id="use-relative">1. Use wrap_content, fill_parent, or the dp unit for layout dimensions</h3>
+
+<p>When defining the <a
+href="{@docRoot}reference/android/view/ViewGroup.LayoutParams.html#attr_android:layout_width"
+>{@code android:layout_width}</a> and <a
+href="{@docRoot}reference/android/view/ViewGroup.LayoutParams.html#attr_android:layout_height"
+>{@code android:layout_height}</a> for
+views in an XML layout file, using <code>"wrap_content"</code>,
+<code>"fill_parent"</code> or <code>dp</code> units guarantees that the view is
+given an appropriate size on the current device screen.</p>
+
+<p>For instance, a view with a <code>layout_width="100dp"</code> measures 100 pixels wide on
+medium-density screen and the system scales it up to 150 pixels wide on high-density screen, so
+that the view occupies approximately the same physical space on the screen.</p>
+
+<p>Similarly, you should prefer the <code>sp</code> (scale-independent pixel) to define text
+sizes. The <code>sp</code> scale factor depends on a user setting and the system scales the
+size the same as it does for {@code dp}.</p>
+
+
+<h3>2. Do not use hard-coded pixel values in your application code</h3>
+
+<p>For performance reasons and to keep the code simpler, the Android system uses pixels as the
+standard unit for expressing dimension or coordinate values. That means that the dimensions of a
+view are always expressed in the code using pixels, but always based on the current screen density.
+For instance, if <code>myView.getWidth()</code> returns 10, the view is 10 pixels wide on the
+current screen, but on a device with a higher density screen, the value returned might be 15. If you
+use pixel values in your application code to work with bitmaps that are not pre-scaled for the
+current screen density, you might need to scale the pixel values that you use in your code to match
+the un-scaled bitmap source.</p>
+
+<p>If your application manipulates bitmaps or deals with pixel values at runtime, see the section
+below about <a href="#DensityConsiderations">Additional Density Considerations</a>.</p>
+
+
+<h3 id="avoid-absolute">3. Do not use AbsoluteLayout </h3>
+
+<p>Unlike the other layouts widgets, {@link android.widget.AbsoluteLayout} enforces
+the use of fixed positions to lay out its child views, which can easily lead to user interfaces that
+do not work well on different displays. Because of this, {@link android.widget.AbsoluteLayout} was
+deprecated in Android 1.5 (API Level 3).</p>
+
+<p>You should instead use {@link android.widget.RelativeLayout}, which uses relative positioning
+to lay out its child views. For instance, you can specify that a button widget should appear "to
+the right of" a text widget.</p>
+
+
+<h3>4. Use size and density-specific resources</h3>
+
+<p>Although the system scales your layout and drawable resources based on the current screen
+configuration, you may want to make adjustments to the UI on different screen sizes and provide
+bitmap drawables that are optimized for different densities. This essentially reiterates the
+information from earlier in this document.</p>
 
 <p>If you need to control exactly how your application will look on various
-displays, simply adjust your assets and layouts in configuration-specific
-resources directories. For example, consider an icon that you want to display on
+screen configurations, adjust your layouts and bitmap drawables in configuration-specific
+resource directories. For example, consider an icon that you want to display on
 medium and high density screens. Simply create your icon at two different sizes
 (for instance 100x100 for medium density and 150x150 for high density) and put
 the two variations in the appropriate directories, using the proper
 qualifiers:</p>
 
-<p style="margin-left:2em;"><code>res/drawable-mdpi/icon.png&nbsp;&nbsp;&nbsp;//
-for medium-density screens</code></p>
+<pre class="classic">
+res/drawable-mdpi/icon.png&nbsp;&nbsp;&nbsp;//for medium-density screens
+res/drawable-hdpi/icon.png&nbsp;&nbsp;&nbsp;//for high-density screens
+</pre>
 
-<p style="margin-left:2em;"><code>res/drawable-hdpi/icon.png&nbsp;&nbsp;&nbsp;//
-for high-density screens</code></p>
+<p class="note"><strong>Note:</strong> If a density qualifier is not defined in a directory name,
+the system assumes that the resources in that directory are designed for the baseline medium
+density and will scale for other densities as appropriate.</p>
 
-<p>If a density qualifier is not defined in a resource directory name, the
-platform assumes that the resources in that directory are designed for the
-baseline medium density. It is not recommended that you put density-specific
-resources such as images in the default directory.</p>
+<p>For more information about valid configuration qualifiers, see <a href="#qualifiers">Using
+configuration qualifiers</a>, earlier in this document.</p>
 
-<p>For more information about valid resource qualifiers, see
-<a href="#qualifiers">Resource directory qualifiers</a>, earlier in this
-document.</p>
 
-<h4 id="scaling">Pre-scaling and auto-scaling of bitmaps and nine-patches</h4>
 
-<p>When a bitmap or nine-patch image is loaded from the application's resources,
-the platform attempts to pre-scale it to match the display's density. For
-instance, if you placed a 100x100 icon in the <code>res/drawable/</code>
-directory and loaded that icon as a bitmap on a high-density screen, Android
-would automatically scale up the icon and produce a 150x150 bitmap.</p>
 
-<p>This pre-scaling mechanism works independently of the source. For instance,
-an application targeted for a high-density screen may have bitmaps only in the
-<code>res/drawable-hdpi/</code> directory. If one of the bitmaps is a 240x240
-icon and is loaded on a medium-density screen, the resulting bitmap will measure
-160x160.</p>
 
-<p>The platform pre-scales resources as needed, whether the application is
-running with density-compatibility features enabled or not (as specified by the
-value of <code>android:anyDensity</code>). However, when running with
-density-compatibility enabled, the platform continues to report the size of
-pre-scaled bitmaps and other resources as if they were loaded in a
-medium-density environment. For example, when density-compatibility is enabled,
-if you load a 76x76 image from the default resources for display on a
-high-density screen, the platform will pre-scale the image to 114x114
-internally. However, the API still reports the size of the image as 76x76. This
-discrepancy may cause unexpected behavior if your application somehow directly
-manipulates the scaled bitmap, but this was considered a reasonable trade-off to
-keep the performance of existing applications as good as possible.</p>
+<h2 id="DensityConsiderations">Additional Density Considerations</h2>
 
-<p>This does not apply for the case that an application creates an in-memory
-bitmap internally and draws something on it, for later display on the screen.
-The platform auto-scales such bitmaps on the fly, at draw time. Other side
-effects of such a case might be that fonts drawn in such a bitmap will be scaled
-at the bitmap level, when the off-screen bitmap is finally rendered to the
-display, resulting in scaling artifacts.</p>
+<p>This section describes more about how Android performs scaling for bitmap drawables on different
+screen densities and how you can further control how bitmaps are drawn on different densities. The
+information in this section shouldn't be important to most applications, unless you have encountered
+problems in your application when running on different screen densities or your application
+manipulates graphics.</p>
 
-<p>There are situations in which you may not want Android to automatically scale
-a resource. The easiest way to accomplish this is to put it in a "nodpi"
-resource directory:</p>
-
-<p style="margin-left:2em;"><code>res/drawable-nodpi/icon.png</code></p>
-
-<p>You can also take complete control of the scaling mechanism by using the
-{@link android.graphics.BitmapFactory.Options BitmapFactory.Options} class,
-which lets you define whether you want the bitmap to be pre-scaled and what the
-density of the bitmap should be. For instance, if you are loading a bitmap from
-a web server, you may want to force the bitmap's density to be high density.
-When pre-scaling is disabled, the resulting bitmap is in auto-scaling mode. The
-bitmap is associated with a density (that you may or may not have specified
-through the <code>BitmapFactory.Options</code>) which will be used to scale the
-bitmap on screen <em>at drawing time</em>.
-
-<p>Using auto-scaling instead of pre-scaling is more CPU expensive than
-pre-scaling but uses less memory. You can refer to the documentation of
-{@link android.graphics.BitmapFactory BitmapFactory},
-{@link android.graphics.Bitmap Bitmap}, and
-{@link android.graphics.Canvas Canvas} for more
-information on auto-scaling.</p>
-
-<p>Figure 3, at right, demonstrates the results of the pre-scale and auto-scale
-mechanisms when loading low (120), medium (160) and high (240) density bitmaps
-on a baseline screen. The differences are subtle, because all of the bitmaps are
-being scaled to match the current screen density, however the scaled bitmaps
-have slightly different appearances depending on whether they are pre-scaled or
-auto-scaled at draw time.</p>
-
-<h2 id="strategies">Strategies for Legacy Applications</h2>
-
-<p>If you have already developed and published an Android application based on
-Android 1.5 or earlier platform version, you need to consider how you will adapt
-your application so that it is deployable to:</p>
-
-<ul>
-<li>Existing devices, which may be running Android 1.5 (or lower) platform
-version, as well as to </li>
-<li>Newer devices that are running Android 1.6 (or higher) and offering various
-screen sizes and resolutions</li>
-</ul>
-
-<p class="note"><strong>Note:</strong> Even if your application targets Android 1.6 already, you
-should follow the same strategies below in order to support <em>xhdpi</em> and <em>xlarge</em>
-screens on Android 2.3 (API Level 9), while maintaining compatibility with older versions of
-the platform.</p>
-
-<p>To support the newer devices and the different screens they use, you might
-need to make some changes in your app, but at the same time your app may be very
-stable and so you want to minimize the changes. There are a variety of ways that
-you can extend your existing application to support new devices with multiple
-screens <em>and</em> existing devices running older platform versions. You
-should be able to make these changes to your application such that you can
-distribute a single {@code .apk} to all devices.</p>
-
-<p>The recommended strategy is to develop against the most recent version of the
-platform you are targeting, and test on the minimum platform version you want to run on.
-Here's how to do that:</p>
+<p>To better understand how you can support multiple densities when manipulating graphics at
+runtime, you should understand that the system helps ensure the proper scale for bitmaps in the
+following ways:</p>
 
 <ol>
-  <li>Maintain compatibility with existing devices by leaving your application's
-<code>android:minSdkVersion</code> attribute as it is. You <em>do not</em> need
-to increment the value of the attribute to support new devices and multiple
-screens. </li>
-  <li>Extend compatibility for Android 1.6 (and higher) devices by adding
-a new attribute &mdash; <code>android:targetSdkVersion</code> &mdash; to the
-<code>uses-sdk</code> element. Set the value of the attribute to
-<code>"4"</code>. [To support <em>xhdpi</em> and <em>xlarge</em> screens, set the value to
-<code>"9"</code>.] This allows your application to "inherit" the platform's
-multiple screens support, even though it is technically using an earlier version
-of the API. </li>
-  <li>Add an empty <code>&lt;supports-screens&gt;</code> element as a child of
-<code>&lt;manifest&gt;</code>. If you need to enable size or density attributes
-later, this is where you will add them.</li>
-  <li>Change your application's build properties, such that it compiles against
-the Android 1.6 (API Level 4) library [or against Android 2.3 (API Level 9) to support
-<em>xhdpi</em> and <em>xlarge</em> screens], rather than against the Android 1.5 (or
-earlier) library. You will not be able to compile your application against the
-older platform because of the new manifest attribute. </li>
-  <li>Set up AVDs for testing your application on Android 1.6 [or Android 2.3] and higher
-releases. Create AVDs that use the screen sizes and densities that you want to
-support. When you create the AVDs, make sure to select the Android 1.6 [or Android 2.3] or higher
-platform as the system image to run. For more information, see <a
-href="#testing">How to Test Your Application on Multiple Screens</a>,
-below.</li>
-  <li>Set up AVDs for testing your application on older versions of the platform, as low as the
-version declared by your <code>android:minSdkVersion</code>. You need AVDs running the older
-platforms you are targeting, so that
-you can test for compatibility and ensure that there are no functional
-regressions. </li>
-  <li>Compile your application against the Android 1.6 [or Android 2.3] library and run it on the
-AVDs you created. Observe the way your application looks and runs, and test all
-of the user interactions. </li>
-  <li>Debug any display or functional issues. For issues that you resolve in
-your application code, <span style="color:red">make certain not to use any APIs
-introduced later than the version declared by your <code>android:minSdkVersion</code></span>. If you
-are in doubt, refer to SDK reference documentation and look for the API Level specifier for the API
-you want to use. Using newer APIs not supported by your minimum version will mean that your
-application will no longer be compatible with devices running on that version.</li>
-  <li>For resource-related issues, you can try resolving them by:
-    <ul>
-    <li>Adding a <code>anyDensity="false"</code> attribute to
-<code>&lt;supports-screens&gt;</code>, to enable density-compatibility
-scaling.</li>
-    <li>Creating any size- or density-specific resources you need and placing
-them in directories tagged with the <a href="#qualifiers">correct
-qualifiers</a>. Qualifiers must be arranged in a proscribed order. See
-<a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">
-Providing Alternative Resources</a> for more information. </li>
-    <li>Note that if you add size- or density-specific resource directories
-tagged with any of the resource qualifiers listed in this document, you should
-make sure to also tag those directories with the <code>v&lt;api-level&gt;</code>
-qualifier (for example, <code>-v4</code> to target API Level 4). This ensures that those resources
-will be ignored when the application is run on Android 1.5 or lower platform
-versions.</p></li>
-    </ul>
-  </li>
-  <li>If your application does not offer support (such as custom layouts) for
-large screens and you want the platform to display your application in
-screen-compatibility mode on larger screens, add the
-<code>largeScreens="false"</code> and <code>xlargeScreens="false"</code> attributes to the
-<code>&lt;supports-screens&gt;</code> element in the manifest. See
-<a href="#compatibility-examples">Screen-Compatibility Examples</a> for
-illustrations of how the platform displays your application in this case.</li>
-  <li>If your application does not offer support (such as custom layouts) for
-small screens (such as on a QVGA low-density screen) and you do not want Android
-Market to offer the application to users of small-screen devices, you
-<em>must</em> add a <code>smallScreens="false"</code> attribute to the
-<code>&lt;supports-screens&gt;</code> element. </li>
-  <li>Continue testing and debugging until your application performs as expected
-on all of the platforms and screen sizes your application will support.</li>
-  <li>Export, zipalign, and sign your application using the same private key you
-used when publishing the previous version, then publish the application to users
-as an update. </li>
+<li><em>Pre-scaling of resources (such as bitmap drawables)</em>
+
+  <p>Based on the density of the current screen, the system uses any size- or density-specific
+resources from your application and displays them without scaling. If resources are not available in
+the correct density, the system loads the default resources and scales them up or down as needed to
+match the current screen's density. The system assumes that default resources (those from a
+directory without configuration qualifiers) are designed for the baseline screen density (mdpi),
+unless they are loaded from a density-specific resource directory. Pre-scaling is, thus, what the
+system does when resizing a bitmap to the appropriate size for the current screen
+density.</p>
+
+  <p>If you request the dimensions of a pre-scaled resource, the system returns values
+representing the dimensions <em>after</em> scaling. For example, a bitmap designed at 50x50 pixels
+for an mdpi screen is scaled to 75x75 pixels on an hdpi screen (if there is no alternative resource
+for hdpi) and the system reports the size as such.</p>
+
+<p>There are some situations in which you might not want Android to pre-scale
+a resource. The easiest way to avoid pre-scaling is to put the resource in a resource directory
+with the {@code nodpi} configuration qualifier. For example:</p>
+
+<pre class="classic">res/drawable-nodpi/icon.png</pre>
+
+<p>When the system uses the {@code icon.png} bitmap from this folder, it does not scale it
+based on the current device density.</p>
+</li>
+
+<li><em>Auto-scaling of pixel dimensions and coordinates</em>
+
+  <p>An application can disable pre-scaling by setting <a
+href="{@docRoot}guide/topics/manifest/supports-screens-element.html#any">{@code
+android:anyDensity}</a> to {@code "false"} in the manifest or programmatically for a {@link
+android.graphics.Bitmap} by setting {@link android.graphics.BitmapFactory.Options#inScaled} to
+{@code "false"}. In this case, the system auto-scales any absolute pixel coordinates and pixel
+dimension values at draw time. It does this to ensure that pixel-defined screen elements are still
+displayed at approximately the same physical size as they would be at the baseline screen density
+(mdpi). The system handles this scaling transparently to the application and reports the scaled
+pixel dimensions to the application, rather than physical pixel dimensions.</p>
+
+  <p>For instance, suppose a device has a WVGA high-density screen, which is 480x800 and about the
+same size as a traditional HVGA screen, but it's running an application that has disabled
+pre-scaling. In this case, the system will "lie" to the application when it queries for screen
+dimensions, and report 320x533 (the approximate mdpi translation for the screen density). Then, when
+the application does drawing operations, such as invalidating the rectangle from (10,10) to (100,
+100), the system transforms the coordinates by scaling them the appropriate amount, and actually
+invalidate the region (15,15) to (150, 150). This discrepancy may cause unexpected behavior if
+your application directly manipulates the scaled bitmap, but this is considered a reasonable
+trade-off to keep the performance of applications as good as possible. If you encounter this
+situation, read the following section about <a href="#dips-pels">Converting dp units to pixel
+units</a>.</p>
+
+  <p>Usually, <strong>you should not disable pre-scaling</strong>. The best way to support multiple
+screens is to follow the basic techniques described above in <a href="#support">How to Support
+Multiple Screens</a>.<p>
+</li>
+
 </ol>
 
-<p>In particular, remember to test your application on an AVD that emulates a
-small-screen device. Devices that offer screens with QVGA resolution at low
-density are available now. Users of those devices may want to download your
-application, so you should understand how your application will look and
-function on a small-screen device. In many cases, the reduced screen area and
-density mean that you may need to make tradeoffs in design, content, and
-function on those devices. </p>
 
-<p>Also give extra attention to testing your application on an AVD that emulates an <em>xlarge</em>
-screen. Devices with extra large screens
-are tablet-sized or larger, so you should pay close attention to how usable your application is on
-such screens. You might want to design new layouts specifically for extra large screens, to address
-usability aspects such as the location and size of buttons in your UI. To test your application on
-an extra large screen, create an AVD targeted to Android 2.3 with a high resolution, such as 1280 x
-800, and the default density of 160dpi. This AVD will use any resources you've provided with the
-<code>xlarge</code> <a href="#qualifiers">resouce qualifier</a>.</p>
+<p>If your application manipulates bitmaps or directly interacts with pixels on the screen in some
+other way, you might need to take additional steps to support different screen densities. For
+example, if you respond to touch gestures by counting the number of pixels that a finger
+crosses, you need to use the appropriate density-independent pixel values, instead of actual
+pixels.</p>
+
+
+<h3 id="scaling">Scaling Bitmap objects created at runtime</h3>
+
+<div class="figure" style="width:300px">
+<img src="{@docRoot}images/screens_support/scale-test.png" alt="" />
+<p class="img-caption"><strong>Figure 5.</strong> Comparison of pre-scaled and auto-scaled
+bitmaps, from <a
+href="resources/samples/ApiDemos/src/com/example/android/apis/graphics/DensityActivity.html">
+ApiDemos</a>.
+</p>
+</div>
+
+<p>If your application creates an in-memory bitmap (a {@link android.graphics.Bitmap} object), the
+system assumes that the bitmap is designed for the baseline medium-density screen, by default, and
+auto-scales the bitmap at draw time. The system applies "auto-scaling" to a {@link
+android.graphics.Bitmap} when the bitmap has unspecified density properties. If you don't properly
+account for the current device's screen density and specify the bitmap's density properties, the
+auto-scaling can result in scaling artifacts the same as when you don't provide alternative
+resources.</p>
+
+<p>To control whether a {@link android.graphics.Bitmap} created at runtime is scaled or not, you can
+specify the density of the bitmap with {@link android.graphics.Bitmap#setDensity setDensity()},
+passing a density constant from {@link android.util.DisplayMetrics}, such as {@link
+android.util.DisplayMetrics#DENSITY_HIGH} or {@link android.util.DisplayMetrics#DENSITY_LOW}.</p>
+
+<p>If you're creating a {@link android.graphics.Bitmap} using {@link
+android.graphics.BitmapFactory}, such as from a file or a stream, you can use {@link
+android.graphics.BitmapFactory.Options BitmapFactory.Options} to define properties of the bitmap as
+it already exists, which determine if or how the system will scale it. For example, you can use the
+{@link android.graphics.BitmapFactory.Options#inDensity} field to define the density for which the
+bitmap is designed and the {@link
+android.graphics.BitmapFactory.Options#inScaled} field to specify whether the bitmap should scale to
+match the current device's screen density.</p>
+
+<p>If you set the {@link
+android.graphics.BitmapFactory.Options#inScaled} field to {@code false}, then you disable any
+pre-scaling that the system may apply to the bitmap and the system will then auto-scale it at draw
+time. Using auto-scaling instead of pre-scaling can be more CPU expensive, but uses
+less memory.</p>
+
+<p>Figure 5 demonstrates the results of the pre-scale and auto-scale mechanisms when loading low
+(120), medium (160) and high (240) density bitmaps on a high-density screen. The differences are
+subtle, because all of the bitmaps are being scaled to match the current screen density, however the
+scaled bitmaps have slightly different appearances depending on whether they are pre-scaled or
+auto-scaled at draw time. You can find the source code for this sample application, which
+demonstrates using pre-scaled and auto-scaled bitmaps, in <a
+href="resources/samples/ApiDemos/src/com/example/android/apis/graphics/DensityActivity.html">
+ApiDemos</a>.</p>
+
+<p class="note"><strong>Note:</strong> In Android 3.0 and above, there should be no perceivable
+difference between pre-scaled and auto-scaled bitmaps, due to improvements in the graphics
+framework.</p>
+
+
+
+
+
+<h3 id="dips-pels">Converting dp units to pixel units</h3>
+
+<p>In some cases, you will need to express dimensions in <code>dp</code> and then convert them to
+pixels. Imagine an application in which a scroll or fling gesture is recognized after the user's
+finger has moved by at least 16 pixels. On a baseline screen, a user's must move by {@code 16 pixels
+/ 160 dpi}, which equals 1/10th of an inch (or 2.5 mm) before the gesture is recognized. On a device
+with a high density display (240dpi), the user's must move by {@code 16 pixels / 240 dpi}, which
+equals 1/15th of an inch (or 1.7 mm). The distance is much shorter and the application thus appears
+more sensitive to the user.</p> 
+
+<p>To fix this issue, the gesture threshold must be expressed in code in <code>dp</code> and then
+converted to actual pixels. For example:</p>
+
+<pre>// The gesture threshold expressed in dp
+private static final float GESTURE_THRESHOLD_DP = 16.0f;
+
+// Get the screen's density scale
+final float scale = {@link android.content.ContextWrapper#getResources getResources()}.{@link
+android.content.res.Resources#getDisplayMetrics getDisplayMetrics()}.density;
+// Convert the dps to pixels, based on density scale
+mGestureThreshold = (int) (GESTURE_THRESHOLD_DP * scale + 0.5f);</span>
+
+// Use mGestureThreshold as a distance in pixels...
+</pre>
+
+<p>The {@link android.util.DisplayMetrics#density DisplayMetrics.density} field specifies the scale
+factor you must use to convert {@code dp} units to pixels, according to the current screen density.
+On a medium-density screen, {@link android.util.DisplayMetrics#density DisplayMetrics.density}
+equals 1.0; on a high-density screen it equals 1.5; on an extra high-density screen, it equals 2.0;
+and on a low-density screen, it equals 0.75. This figure is the factor by which you should multiply
+the {@code dp} units on order to get the actual pixel count for the current screen. (Then add {@code
+0.5f} to round the figure up to the nearest whole number, when converting to an integer.) For more
+information, refer to the {@link android.util.DisplayMetrics DisplayMetrics} class.</p>
+
+<p>However, instead of defining an arbitrary threshold for this kind of event, you should
+use pre-scaled configuration values that are available from {@link
+android.view.ViewConfiguration}.</p>
+
+
+<h4 id="pre-scaled-values">Using pre-scaled configuration values</h4>
+
+<p>You can use the {@link android.view.ViewConfiguration} class to access common distances,
+speeds, and times used by the Android system. For instance, the
+distance in pixels used by the framework as the scroll threshold can be obtained with {@link
+android.view.ViewConfiguration#getScaledTouchSlop()}:</p>
+
+<pre>
+private static final int GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).getScaledTouchSlop();
+</pre>
+
+<p>Methods in {@link android.view.ViewConfiguration} starting with the <code>getScaled</code> prefix
+are guaranteed to return a value in pixels that will display properly regardless of the current
+screen density.</p>
+
+
+
+
 
 
 <h2 id="testing">How to Test Your Application on Multiple Screens</h2>
 
-<p>Before publishing an application that supports multiple screens, you should
-thoroughly test it in all of the targeted screen sizes and densities. You can
-test how it displays with the platform's compatibility features enabled or with
-screen-specific UI resources included in your application. The Android SDK
-includes all the tools you need to test your application on any supported
-screen.</p>
-
-<!-- You can test in any minsdk, and you can test with compatabiltiy code or
-not. Once you've tested your application and found that it displays properly on
-various screen sizes, you should make sure to add the corresponding size
-attribute(s) to your application's manifest. -->
-
-<div id="f9.5" class="figure" style="width:530px">
-  <img src="{@docRoot}images/screens_support/avds-config.png" />
-  <p class="img-caption"><strong>Figure 4.</strong>
-  A typical set of AVDs for testing screens support.</p>
+<div class="figure" style="width:500px;margin:0">
+  <img src="{@docRoot}images/screens_support/avds-config.png" alt="" />
+  <p class="img-caption"><strong>Figure 6.</strong>
+  A set of AVDs for testing screens support.</p>
 </div>
 
-<p>As a test environment for your applications, set up a series of AVDs that
-emulate the screen sizes and densities you want to support. The Android SDK
-includes several emulator skins to get you started. You can use the Android AVD
-Manager or the <code>android</code> tool to create AVDs that use the various
-emulator skins and you can also set up custom AVDs to test densities other than
-the defaults. For general information about working with AVDs, see
-<a href="{@docRoot}guide/developing/devices/index.html">Creating and Managing Virtual
-Devices</a>.</p>
+<p>Before publishing your application, you should thoroughly test it in all of the supported screen
+sizes and densities. The Android SDK includes emulator skins you can use, which
+replicate the sizes and densities of common screen configurations on which your application is
+likely to run. You can also modify the default size, density, and resolution of the emulator skins
+to replicate the characteristics of any specific screen. Using the emulator skins and additional
+custom configurations allows you to test any possible screen configuration, so you don't
+have to buy various devices just to test your application's screen support.</p>
 
-<p>The Android SDK provides a set of default emulator skins that you can use for
-testing. The skins are included as part of each Android platform that you can
-install in your SDK. The Android 1.6 platform offers these default skins:</p>
+<p>To set up an environment for testing your application's screen support, you should create a
+series of AVDs (Android Virtual Devices), using emulator skins and screen configurations that
+emulate the screen sizes and densities you want your application to support. To do so, you can use
+the Android SDK and AVD Manager to create the AVDs and launch them with a graphical interface.</p>
 
-<ul>
-  <li>
-    QVGA (240x320, low density, small screen)
-  </li>
-  <li>
-    HVGA (320x480, medium density, normal screen)
-  </li>
-  <li>
-    WVGA800 (480x800, high density, normal screen)
-  </li>
-  <li>
-    WVGA854 (480x854 high density, normal screen)
-  </li>
-</ul>
+<p>To launch the Android SDK and AVD Manager, execute the {@code
+SDK Manager.exe} from your Android SDK directory (on Windows only) or execute {@code android} from
+the {@code &lt;sdk&gt;/tools/} directory (on all platforms). Figure 6 shows the Android SDK and
+AVD Manager with a selection of AVDs, for testing various screen configurations.</p>
 
-<p>The Android 2.0 platform offers all of the Android 1.6 default skins,
-above, plus:</p>
+<p>Table 2 shows the various emulator skins that are available in the Android SDK, which you can use
+to emulate some of the most common screen configurations.</p>
 
-<ul>
-  <li>
-    WQVGA400 (240x400, low density, normal screen)
-  </li>
-  <li>
-    WQVGA432 (240x432, low density, normal screen)
-  </li>
-</ul>
+<p>For more information about creating and using AVDs to test your application, see <a
+href="{@docRoot}guide/developing/devices/managing-avds.html">Managing AVDs with AVD
+Manager</a>.</p>
 
-<p>If you are using the <code>android</code> tool command line to create your
-AVDs, here's an example of how to specify the skin you want to use:</p>
 
-<pre>android create avd ... --skin WVGA800</pre>
+<p class="table-caption" id="screens-table"><strong>Table 2.</strong> Various screen
+configurations available from emulator skins in the Android SDK (indicated in bold) and other
+representative resolutions.</p>
+
+  <table class="normal-headers">
+    <tbody>
+    <tr>
+      <th></th>
+      <th>
+        <nobr>Low density (120), <em>ldpi</em></nobr>
+      </th>
+      <th>
+        <nobr>Medium density (160), <em>mdpi</em></nobr>
+      </th>
+      <th>
+        <nobr>High density (240), <em>hdpi</em><nobr>
+      </th>
+      <th>
+        <nobr>Extra high density (320), <em>xhdpi</em><nobr>
+      </th>
+    </tr>
+    <tr>
+      <th>
+        <em>Small</em> screen
+      </th>
+      <td><strong>QVGA (240x320)</strong></td>
+      <td></td>
+      <td>480x640</td>
+      <td></td>
+    </tr>
+    <tr>
+      <th>
+        <em>Normal</em> screen
+      </th>
+      <td><strong>WQVGA400 (240x400)</strong>
+        <br><strong>WQVGA432 (240x432)</strong></td>
+      <td><strong>HVGA (320x480)</strong></td>
+      <td><strong>WVGA800 (480x800)</strong>
+        <br><strong>WVGA854 (480x854)</strong>
+        <br>600x1024</td>
+      <td>640x960</td>
+    </tr>
+    <tr>
+      <th>
+        <em>Large</em> screen
+      </th>
+      <td><strong>WVGA800** (480x800)</strong>
+        <br><strong>WVGA854** (480x854)</strong></td>
+      <td><strong>WVGA800* (480x800)</strong>
+        <br><strong>WVGA854* (480x854)</strong>
+        <br>600x1024</td>
+      <td></td>
+      <td></td>
+    </tr>
+    <tr>
+      <th>
+        <em>Extra Large</em> screen
+      </th>
+      <td>1024x600</td>
+      <td><strong>WXGA (1280x800)</strong><sup>&dagger;</sup><br>
+          1024x768<br>1280x768</td>
+      <td>1536x1152<br>1920x1152
+        <br>1920x1200</td>
+      <td>2048x1536<br>2560x1536
+        <br>2560x1600</td>
+    </tr>
+    <tr>
+      <td colspan="5" style="border:none;font-size:85%;">* To emulate this configuration, specify a
+custom density of 160 when creating an AVD that uses a WVGA800 or WVGA854 skin.<br/>
+        ** To emulate this configuration, specify a custom density of 120 when creating an AVD that
+uses a WVGA800 or WVGA854 skin.<br/>
+        &dagger; This skin is available with the Android 3.0 platform
+      </td>
+    </tr>
+</table>
+
+<p>To see the relative numbers of active devices that support any given screen configuration, see
+the <a href="{@docRoot}resources/dashboard/screens.html">Screen Sizes and Densities</a>
+dashboard.</p>
+
+<div class="figure" style="width:204px">
+  <img src="{@docRoot}images/screens_support/avd-start.png" alt="" />
+  <p class="img-caption"><strong>Figure 7.</strong>
+  Size and density options you can set, when starting an AVD from the Android SDK and AVD
+Manager.</p>
+</div>
 
 <p>We also recommend that you test your application in an emulator that is set
 up to run at a physical size that closely matches an actual device. This makes
-it a lot easier to compare the results at various resolutions and densities. To
-do so you will need to know the approximate density, in dpi, of your computer
-monitor (a 30" Dell monitor has for instance a density of about 96 dpi.). Use
-your monitor's dpi as the value of the <code>-scale</code> option, when
-launching the emulator, for example:</p>
-
-<pre>emulator -avd &lt;name&gt; -scale 96dpi</pre>
-
-<p>If you are working in Eclipse with ADT, you can specify the <code>-scale
-96dpi</code> option in the Target tab of run and debug configurations, under
-"Additional Emulator Command Line Options" field. </p>
-
-<p>Note that starting the emulator with the <code>-scale</code> option will
-scale the entire emulator display, based on both the dpi of the skin and of your
-monitor. The default emulator skins included in the Android SDK are listed
-in <a href="#screens-table">Table 1</a>, earlier in this document.</p>
-
-<div class="figure" style="width:324px">
-  <img src="{@docRoot}images/screens_support/avd-density.png" >
-  <p class="img-caption"><strong>Figure 5.</strong>
-  Resolution and density options that you can use, when creating an AVD using the AVD Manager.</p>
-</div>
-
-<p>You should also make sure to test your application on different physical
-screen sizes within a single size-density configuration. For example, to
-display this screen configuration on a 30" monitor you will need to adjust
-the value passed to <code>-scale</code> to 96*2.8/3.3 = 81dpi. You can also
-pass a float value to <code>-scale</code> to specify your own scaling factor:</p>
-
-<pre>emulator -avd &lt;name&gt; -scale 0.6</pre>
+it a lot easier to compare the results at various sizes and densities. To
+do so you need to know the approximate density, in dpi, of your computer
+monitor (for instance, a 30" Dell monitor has a density of about 96 dpi). When you launch an AVD
+from the Android SDK and AVD Manager, you can specify the screen size for the emulator and your
+monitor dpi in the Launch Options, as shown in figure 7.</p>
 
 <p>If you would like to test your application on a screen that uses a resolution
-or density not supported by the built-in skins, you can either adjust an
-existing skin, or create an AVD that uses a custom resolution or density.</p>
+or density not supported by the built-in skins, you can create an AVD that uses a custom resolution
+or density. When creating the AVD from the Android SDK and AVD Manager, specify the Resolution,
+instead of selecting a Built-in Skin.</p>
 
-<p>In the AVD Manager, you can specify a custom skin resolution or density in
-the Create New AVD dialog, as shown in Figure 5, at right.</p>
+<p>If you are launching your AVD from the command line, you can specify the scale for
+the emulator with the <code>-scale</code> option. For example:</p>
 
-<p>In the <code>android</code> tool, follow these steps to create an AVD with a
-custom resolution or density:</p>
+<pre>emulator -avd &lt;avd_name&gt; -scale 96dpi</pre>
 
-<ol>
-  <li>Use the <code>create avd</code> command to create a new AVD, specifying
-the <code>--skin</code> option with a value that references either a default
-skin name (such as "WVGA800") or a custom skin resolution (such as 240x432).
-Here's an example:
-     <pre>android create avd -n &lt;name&gt; -t &lt;targetID&gt; --skin WVGA800</pre>
-  </li>
-  <li>To specify a custom density for the skin, answer "yes" when asked whether
-you want to create a custom hardware profile for the new AVD.</li>
-  <li>Continue through the various profile settings until the tool asks you to
-specify "Abstracted LCD density" (<em>hw.lcd.density</em>). Enter an appropriate
-value, such as "120" for a low-density screen, "160" for a medium density screen,
-or "240" for a high-density screen.</li>
-  <li>Set any other hardware options and complete the AVD creation.</li>
-</ol>
+<p>To refine the size of the emulator, you can instead pass the {@code -scale} option a number
+between 0.1 and 3 that represents the desired scaling factor.</p>
 
-<p>In the example above (WVGA medium density), the new AVD will emulate a 5.8"
-WVGA screen.</p>
-
-<p>As an alternative to adjusting the emulator skin configuration, you can use
-the emulator skin's default density and add the <code>-dpi-device</code> option
-to the emulator command line when starting the AVD. For example, </p>
-
-<pre>emulator -avd WVGA800 -scale 96dpi -dpi-device 160</pre>
-
-
-<h2 id="compatibility-examples">Screen-Compatibility Examples</h2>
-
-<p>This section provides examples of how the Android platform displays an
-application written for the baseline screen configuration &mdash; HVGA (320x480)
-resolution on a 3.2" screen &mdash; with all of the platform's size- and
-density-compatibility features enabled. That is, the examples show how
-the platform displays an application that doesn't provide built-in support
-for the screen on which it is being rendered, but which instead relies completely
-on the platform.</p>
-
-<p>The platform's screen-compatibility features are designed to provide such
-an application with a virtual baseline screen environment against which to run,
-while at the same time ensuring for the user a physical display that is
-approximately the same as the baseline screen size and density. </p>
-
-<p>Legacy applications that have not been modified to support multiple
-screens would be typical examples of such applications. In most cases,
-you would want to add multiple-screens support to a legacy application and
-publish an updated version, as described in <a href="#strategies">Strategies
-for Legacy Applications</a>. However, if you did not do so, the
-platform still performs best-effort rendering of your application, as
-illustrated below.</p>
-
-<p> Internally, these are the compatibility features that the platform
-provides, based on the current device screen:</p>
-
-  <ul>
-    <li>
-      If the device's screen density is <em>not medium</em>, the application's
-layout and drawing of its content is as if the screen <em>is</em> medium density, but the
-framework scales the layout and images (if the image for the target density is
-not available) to fit the target density. It scales 1.5 times if the target
-density is high density (160-&gt;240 virtual dpi), or 0.75 times if the target
-density is low density (160 -&gt; 120 virtual dpi).
-    </li>
-    <li>
-      If the device's screen size is <em>small</em>, there are few options
-options for making Android 1.5 applications work well on such a screen, so
-Android Market will filter applications that are not known to support these
-screens from the device.
-    </li>
-    <li>
-      If the device's screen size is <em>large</em>, it limits the application's
-screen to the normal size and draws a black background around the application.
-For example, if an application supports high density, but does not support large
-screens, it only uses a 480x720 area of the screen and the rest will be filled
-with a black background (see example below).
-    </li>
-  </ul>
-
-<table style="width:10%;margin-left:.5em;">
-  <tr>
-    <td>
-      HVGA, normal size, normal density<br>
-      [ emulator -skin HVGA ]<br>
-      <img height=149 src="{@docRoot}images/screens_support/afdvfckr9j_15dcsvrscg_b.png" width=225>
-    </td>
-    <td>
-       WVGA, normal size, high density<br>
-      [emulator -skin WVGA854 -dpi-device 240]<br>
-      <img height=143 src="{@docRoot}images/screens_support/afdvfckr9j_18c6mhm3cm_b.png" width=254><br>
-      <p>The application occupies full screen as its considered to be normal size. (close to 480x720)</p>
-    </td>
-  </tr>
-  <tr>
-    <td>
-      VGA, large size, medium density<br>
-      [ emulator -skin 640x480 ]<br>
-      <img height=243 src="{@docRoot}images/screens_support/afdvfckr9j_14fj6dhsc3_b.png" width=324>
-      <p>The application occupies 320x480 of VGA.</p>
-    </td>
-    <td>
-      SVGA, large size, high density<br>
-      [ emulator -skin 800x600 -dpi-device 240]<br>
-      <img height=223 src="{@docRoot}images/screens_support/afdvfckr9j_19c743p6cr_b.png" width=294>
-      <p>The application occupies 480x720 (=1.5 x [320x480]) of 800x600.</p>
-    </td>
-  </tr>
-</table>
-
-
-<h3>Screen-compatibility limitations on small, low-density screens</h3>
-
-<p>Because these device has smaller state/resolution, there are known
-limitations when application runs in compatibility mode.</p>
-
-<h4>QVGA</h4>
-
-<p>Because QVGA (240x320) screens have less screen area available and lower
-density than normal, which is 240x360 in low density, some applications cannot
-render all their content properly on those screens.&nbsp; As a result, on a QVGA
-device, Android Market will filter out all applications that do not declare they
-support small screens.</p>
-
-<p>Examples:</p>
-
-<table style="width:10%;margin-left:.5em;">
-  <tr>
-    <td>The part of z value graph is chopped.</td>
-    <td>The lap time area is chopped.<br></td>
-  </tr>
-  <tr>
-    <td><img height=207 src="{@docRoot}images/screens_support/afdvfckr9j_16g95wjqg3_b.png" width="155"></td>
-    <td><img height=186 src="{@docRoot}images/screens_support/afdvfckr9j_17p2w4txgc_b.png" width="139"></td>
-  </tr>
-</table>
-
-
-<h4>Images with 1 pixel height/width.</h4>
-
-<p>If an image has 1 pixel height or width, it may not be shown on the screen
-due to rounding issue. This is inevitable as it just does not have enough
-pixels.</p>
-
-<p>For example, in the screen below, the divider in the menu is invisible
-because the width of the image is trancated to 0. (This particular problem is
-solvable because menu is handled inside framework, but there is no generic
-solution as it just does not have enough pixels.)</p>
-
-<img height=222 src="{@docRoot}images/screens_support/afdvfckr9j_20fvptbbdd_b.png" width=166>
-
-
-
+<p>For more information about creating AVDs from the command line, see <a
+href="{@docRoot}guide/developing/devices/managing-avds-cmdline.html">Managing AVDs from the
+Command Line</a></p>
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 2a551e9..32595a0 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -501,8 +501,8 @@
 to match the device density.</li>
         </ul>
         <p><em>Added in API Level 4.</em></p>
-        <p>There is thus a 3:4:6 scaling ratio between the three densities, so a 9x9 bitmap
-         in ldpi is 12x12 in mdpi and 18x18 in hdpi.</p>
+        <p>There is thus a 3:4:6:8 scaling ratio between the four densities, so a 9x9 bitmap
+         in ldpi is 12x12 in mdpi, 18x18 in hdpi and 24x24 in xhdpi.</p>
         <p>When Android selects which resource files to use,
          it handles screen density differently than the other qualifiers.
          In step 1 of <a href="#BestMatch">How Android finds the best
diff --git a/docs/html/guide/topics/usb/adk.jd b/docs/html/guide/topics/usb/adk.jd
new file mode 100644
index 0000000..44d7fc2
--- /dev/null
+++ b/docs/html/guide/topics/usb/adk.jd
@@ -0,0 +1,849 @@
+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>
+
+      <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>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>
+
+    <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>
+
+    <li>The Android accessory protocol library, which is located in the
+    <code>firmware/arduino_libs/AndroidAccessory</code> directory. This library defines how to
+    enumerate the bus, find a connected Android-powered device that supports accessory mode, and
+    how to setup communication with the device.</li>
+
+    <li>Other third party libraries to support the ADK board's functionality:
+
+      <ul>
+        <li><a href="http://www.arduino.cc/playground/Main/CapSense">CapSense library</a></li>
+
+        <li><a href="http://www.arduino.cc/playground/Learning/I2C">I2C / TWI (Two-Wire Interface)
+        library</a></li>
+
+        <li><a href="http://www.arduino.cc/playground/ComponentLib/Servo">Servo library</a></li>
+
+        <li><a href="http://www.arduino.cc/playground/Code/Spi">Spi library</a></li>
+
+        <li><a href="http://www.arduino.cc/en/Reference/Wire">Wire library</a></li>
+      </ul>
+    </li>
+
+    <li>An Android application, DemoKit, that communicates with the ADK board and shield. The
+    source for this project is in the <code>app/</code> directory.</li>
+  </ul>
+
+  <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>
+
+  <ul>
+    <li><a href="http://www.arduino.cc/en/Main/software">Arduino Software</a>: contains libraries
+    and an IDE for coding and installing firmware to the ADK board.</li>
+
+    <li><a href="http://www.arduino.cc/playground/Main/CapSense">CapSense library</a>: contains the
+    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>
+  </ul>
+
+  <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>
+
+    <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,
+        not just the files within) to the <code>&lt;arduino_installation_root&gt;/libraries/</code>
+        directory.</li>
+
+        <li>Create a CapSense 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>
+      </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>
+
+        <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">
+        <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>
+
+        <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>
+      </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>
+
+  <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>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>
+
+    <li>To open the firmware code (a sketch), click <strong>File &gt; Open</strong> and select
+    <code>firmware/demokit/demokit.pde</code>.</li>
+
+    <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>
+
+  <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
+  from the board such as joystick movement and temperature readings.</p>
+
+  <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>
+
+    <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.</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>
+  </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 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>
+
+  <p>In general, an accessory should carry out the following steps:</p>
+
+  <ol>
+    <li>Wait for and detect connected devices</li>
+
+    <li>Determine the device's accessory mode support</li>
+
+    <li>Attempt to start the device in accessory mode if needed</li>
+
+    <li>Establish communication with the device if it supports the Android accessory protocol</li>
+  </ol>
+
+  <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">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>
+  </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 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
+  ADB (Android Debug Bridge) protocol, which exposes a second interface with two bulk endpoints for
+  ADB. You can use these endpoints for debugging the accessory application if you are simulating
+  the accessory on a computer. In general, do not use this interface unless your accessory is
+  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>
+
+  <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>
+
+  <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>
+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>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
+index:          string ID
+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>
+manufacturer name:  1
+model name:         2
+description:        3
+version:            4
+URI:                5
+serial number:      6
+</pre>
+    </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>
+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>
+
+  <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>
+
+  <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>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>
+
+  <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>
+  <pre>
+...
+
+AndroidAccessory acc("Google, Inc.",
+                     "DemoKit",
+                     "DemoKit Arduino Board",
+                     "1.0",
+                     "http://www.android.com",
+                     "0000000012345678");
+                     
+...
+void loop()
+{
+...
+    if (acc.isConnected()) {  
+        //communicate with Android application 
+    }
+    else{
+        //set the accessory to its default state
+    }
+...
+}
+</pre>
+
+  <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
+  <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=
+  "start-a">attempts to start the device in accessory mode</a>.</p>
+  <pre>
+bool AndroidAccessory::isConnected(void)
+{
+    USB_DEVICE_DESCRIPTOR *devDesc = (USB_DEVICE_DESCRIPTOR *) descBuff;
+    byte err;
+
+    max.Task();
+    usb.Task();
+
+    if (!connected &amp;&amp;
+        usb.getUsbTaskState() &gt;= USB_STATE_CONFIGURING &amp;&amp;
+        usb.getUsbTaskState() != USB_STATE_RUNNING) {
+        Serial.print("\nDevice addressed... ");
+        Serial.print("Requesting device descriptor.");
+
+        err = usb.getDevDescr(1, 0, 0x12, (char *) devDesc);
+        if (err) {
+            Serial.print("\nDevice descriptor cannot be retrieved. Program Halted\n");
+            while(1);
+        }
+
+        if (isAccessoryDevice(devDesc)) {
+            Serial.print("found android accessory device\n");
+
+            connected = configureAndroid();
+        } else {
+            Serial.print("found possible device. switching to serial mode\n");
+            switchDevice(1);
+        }
+    } else if (usb.getUsbTaskState() == USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE) {
+        connected = false;
+    }
+
+    return connected;
+}
+</pre>
+
+  <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
+  <code>AndroidAccessory::switchDevice()</code> method takes care of this:</p>
+  <pre>
+bool AndroidAccessory::switchDevice(byte addr)
+{
+    int protocol = getProtocol(addr);
+    if (protocol == 1) {
+        Serial.print("device supports protocol 1\n");
+    } else {
+        Serial.print("could not read device protocol version\n");
+        return false;
+    }
+
+    sendString(addr, ACCESSORY_STRING_MANUFACTURER, manufacturer);
+    sendString(addr, ACCESSORY_STRING_MODEL, model);
+    sendString(addr, ACCESSORY_STRING_DESCRIPTION, description);
+    sendString(addr, ACCESSORY_STRING_VERSION, version);
+    sendString(addr, ACCESSORY_STRING_URI, uri);
+    sendString(addr, ACCESSORY_STRING_SERIAL, serial);
+
+    usb.ctrlReq(addr, 0, USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_VENDOR | USB_SETUP_RECIPIENT_DEVICE,
+                ACCESSORY_START, 0, 0, 0, 0, NULL);
+    return true;
+}
+</pre>If this method returns false, the board waits until a new device is connected. If it is
+successful, the device displays itself on the USB bus as being in accessory mode when the ADK board
+re-enumerates the bus. When the device is in accessory mode, the accessory then <a href=
+"establish-a">establishes communication with the device</a>.
+
+  <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>
+  <pre>
+...
+if (isAccessoryDevice(devDesc)) {
+            Serial.print("found android acessory device\n");
+
+            connected = configureAndroid();
+        }
+...
+</pre>
+
+  <p>which in turn calls the <code>findEndpoints()</code> function:</p>
+  <pre>
+...
+bool AndroidAccessory::configureAndroid(void)
+{
+    byte err;
+    EP_RECORD inEp, outEp;
+
+    if (!findEndpoints(1, &amp;inEp, &amp;outEp))
+        return false;
+...
+</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
+  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>
+  <pre>
+...
+
+bool AndroidAccessory::findEndpoints(byte addr, EP_RECORD *inEp, EP_RECORD *outEp)
+{
+    int len;
+    byte err;
+    uint8_t *p;
+
+    err = usb.getConfDescr(addr, 0, 4, 0, (char *)descBuff);
+    if (err) {
+        Serial.print("Can't get config descriptor length\n");
+        return false;
+    }
+
+
+    len = descBuff[2] | ((int)descBuff[3] &lt;&lt; 8);
+    if (len &gt; sizeof(descBuff)) {
+        Serial.print("config descriptor too large\n");
+            /* might want to truncate here */
+        return false;
+    }
+
+    err = usb.getConfDescr(addr, 0, len, 0, (char *)descBuff);
+    if (err) {
+        Serial.print("Can't get config descriptor\n");
+        return false;
+    }
+    
+...
+</pre>
+
+  <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>
+
+  <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
+  for ADB communication. The default communication interface is always indexed first, so finding
+  the first input and output bulk endpoints will return the default communication endpoints, which
+  is what the <code>demokit.pde</code> sketch does. If you are writing your own firmware, the logic
+  to find the appropriate endpoints for your accessory might be different.</p>
+
+  <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>
+  <pre>
+...
+    p = descBuff;
+    inEp-&gt;epAddr = 0;
+    outEp-&gt;epAddr = 0;
+    while (p &lt; (descBuff + len)){
+        uint8_t descLen = p[0];
+        uint8_t descType = p[1];
+        USB_ENDPOINT_DESCRIPTOR *epDesc;
+        EP_RECORD *ep;
+
+        switch (descType) {
+        case USB_DESCRIPTOR_CONFIGURATION:
+            Serial.print("config desc\n");
+            break;
+
+        case USB_DESCRIPTOR_INTERFACE:
+            Serial.print("interface desc\n");
+            break;
+
+        case USB_DESCRIPTOR_ENDPOINT:
+            epDesc = (USB_ENDPOINT_DESCRIPTOR *)p;
+            if (!inEp-&gt;epAddr &amp;&amp; (epDesc-&gt;bEndpointAddress &amp; 0x80))
+                ep = inEp;
+            else if (!outEp-&gt;epAddr)
+                ep = outEp;
+            else
+                ep = NULL;
+
+            if (ep) {
+                ep-&gt;epAddr = epDesc-&gt;bEndpointAddress &amp; 0x7f;
+                ep-&gt;Attr = epDesc-&gt;bmAttributes;
+                ep-&gt;MaxPktSize = epDesc-&gt;wMaxPacketSize;
+                ep-&gt;sndToggle = bmSNDTOG0;
+                ep-&gt;rcvToggle = bmRCVTOG0;
+            }
+            break;
+
+        default:
+            Serial.print("unkown desc type ");
+            Serial.println( descType, HEX);
+            break;
+        }
+
+        p += descLen;
+    }
+
+    if (!(inEp-&gt;epAddr &amp;&amp; outEp-&gt;epAddr))
+        Serial.println("can't find accessory endpoints");
+
+    return inEp-&gt;epAddr &amp;&amp; outEp-&gt;epAddr;
+}
+
+...
+</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>
+  <pre>
+bool AndroidAccessory::configureAndroid(void)
+{
+    byte err;
+    EP_RECORD inEp, outEp;
+
+    if (!findEndpoints(1, &amp;inEp, &amp;outEp))
+        return false;
+
+    memset(&amp;epRecord, 0x0, sizeof(epRecord));
+
+    epRecord[inEp.epAddr] = inEp;
+    if (outEp.epAddr != inEp.epAddr)
+        epRecord[outEp.epAddr] = outEp;
+
+    in = inEp.epAddr;
+    out = outEp.epAddr;
+
+    Serial.print("inEp: ");
+    Serial.println(inEp.epAddr, HEX);
+    Serial.print("outEp: ");
+    Serial.println(outEp.epAddr, HEX);
+
+    epRecord[0] = *(usb.getDevTableEntry(0,0));
+    usb.setDevTableEntry(1, epRecord);
+
+    err = usb.setConf( 1, 0, 1 );
+    if (err) {
+        Serial.print("Can't set config to 1\n");
+        return false;
+    }
+
+    usb.setUsbTaskState( USB_STATE_RUNNING );
+
+    return true;
+}
+</pre>
+
+  <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>
+  <pre>
+int AndroidAccessory::read(void *buff, int len, unsigned int nakLimit) { 
+  return usb.newInTransfer(1, in, len, (char *)buff, nakLimit); } 
+  
+int AndroidAccessory::write(void *buff, int len) { 
+  usb.outTransfer(1, out, len, (char *)buff); 
+  return len; }
+  
+</pre>
+
+  <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/docs/html/images/screens_support/avd-start.png b/docs/html/images/screens_support/avd-start.png
new file mode 100644
index 0000000..affb4bd
--- /dev/null
+++ b/docs/html/images/screens_support/avd-start.png
Binary files differ
diff --git a/docs/html/images/screens_support/avds-config.png b/docs/html/images/screens_support/avds-config.png
index 7db8c73..3af1c39 100644
--- a/docs/html/images/screens_support/avds-config.png
+++ b/docs/html/images/screens_support/avds-config.png
Binary files differ
diff --git a/docs/html/images/screens_support/compat-mode-on.png b/docs/html/images/screens_support/compat-mode-on.png
new file mode 100644
index 0000000..ebdbf41
--- /dev/null
+++ b/docs/html/images/screens_support/compat-mode-on.png
Binary files differ
diff --git a/docs/html/images/screens_support/density-test-bad.png b/docs/html/images/screens_support/density-test-bad.png
new file mode 100644
index 0000000..d3fe1c3
--- /dev/null
+++ b/docs/html/images/screens_support/density-test-bad.png
Binary files differ
diff --git a/docs/html/images/screens_support/density-test-good.png b/docs/html/images/screens_support/density-test-good.png
new file mode 100644
index 0000000..f2eb47c
--- /dev/null
+++ b/docs/html/images/screens_support/density-test-good.png
Binary files differ
diff --git a/docs/html/images/screens_support/icon-density-example.png b/docs/html/images/screens_support/icon-density-example.png
new file mode 100644
index 0000000..3cac5ee
--- /dev/null
+++ b/docs/html/images/screens_support/icon-density-example.png
Binary files differ
diff --git a/docs/html/images/screens_support/scale-test.png b/docs/html/images/screens_support/scale-test.png
index f4fc662..19cf34f 100644
--- a/docs/html/images/screens_support/scale-test.png
+++ b/docs/html/images/screens_support/scale-test.png
Binary files differ
diff --git a/docs/html/images/screens_support/screens-densities.png b/docs/html/images/screens_support/screens-densities.png
new file mode 100644
index 0000000..edbd1e3
--- /dev/null
+++ b/docs/html/images/screens_support/screens-densities.png
Binary files differ
diff --git a/docs/html/images/screens_support/screens-ranges.png b/docs/html/images/screens_support/screens-ranges.png
index dce6264..d8a0ffa 100644
--- a/docs/html/images/screens_support/screens-ranges.png
+++ b/docs/html/images/screens_support/screens-ranges.png
Binary files differ
diff --git a/docs/html/resources/dashboard/opengl.jd b/docs/html/resources/dashboard/opengl.jd
new file mode 100644
index 0000000..dac8ce5
--- /dev/null
+++ b/docs/html/resources/dashboard/opengl.jd
@@ -0,0 +1,79 @@
+page.title=OpenGL ES Versions
+@jd:body
+
+<style type="text/css">
+
+div.dashboard-panel {
+  max-width:1000px;
+  margin:0 0 2em 0;
+  padding:20px 20px 0 20px;
+  border:1px solid #d3ecf5;
+  border-radius:7px;
+  -moz-border-radius:7px;
+  -webkit-border-radius:7px;
+  background-color: #DAF3FC;
+  box-shadow:2px 3px 1px #eee;
+  -moz-box-shadow:2px 3px 1px #eee;
+  -webkit-box-shadow:2px 3px 1px #eee;
+  overflow:hidden;
+}
+#jd-content div.dashboard-panel img {
+  margin:0 20px 20px 0;
+  padding:0;
+  border:1px solid #ccc;
+  float:left;
+}
+#jd-content div.dashboard-panel table {
+  margin:0 0 20px 0;
+  float:left;
+  background-color:#fff;
+}
+div.dashboard-panel p {
+  clear:left;
+}
+div.dashboard-panel th {
+  white-space:nowrap;
+}
+</style>
+
+<p>This page provides data about the relative number of active devices that support a particular
+version of OpenGL ES. Note that support for one particular version of OpenGL ES also implies
+support for any lower version (for example, support for version 2.0 also implies support for
+1.1).</p>
+
+<p>To declare which version of OpenGL ES your application requires, you should use the {@code
+android:glEsVersion} attribute of the <a
+href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code &lt;uses-feature&gt;}</a>
+element. You can also use the <a
+href="{@docRoot}guide/topics/manifest/supports-gl-texture-element.html">{@code
+&lt;supports-gl-texture&gt;}</a> element to declare the GL compression formats that your application
+uses.</p>
+
+<p class="note"><strong>Note:</strong> This data is based on the number
+of Android devices that have accessed Android Market within a 7-day period
+ending on the data collection date noted below.</p>
+
+
+<div class="dashboard-panel">
+
+<img alt="" width="400" height="250"
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%20ES%201.1|GL%20ES%202.0&chd=t%3A8.9,91.1" />
+
+<table>
+<tr>
+<th scope="col">OpenGL ES Version</th>
+<th scope="col">Distribution</th>
+</tr>
+<tr>
+<td>1.1</th>
+<td>8.9%</td>
+</tr>
+<tr>
+<td>2.0</th>
+<td>91.1%</td>
+</tr>
+</table>
+
+<p><em>Data collected during a 7-day period ending on May 6, 2011</em></p>
+</div>
+
diff --git a/docs/html/resources/dashboard/screens.jd b/docs/html/resources/dashboard/screens.jd
index 90f3f1a..bdaae0d 100644
--- a/docs/html/resources/dashboard/screens.jd
+++ b/docs/html/resources/dashboard/screens.jd
@@ -36,46 +36,66 @@
 }
 </style>
 
-<p>This page provides data about the relative number of active devices
-using a particular type of screen, including the screen size and density. For information about how
-your application can support multiple screen types, see <a
-href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>.</p>
+<p> This page provides data about the relative number of active devices that have a particular
+screen configuration, defined by a combination of screen size and density. To simplify the way that
+you design your user interfaces for different screen configurations, Android divides the range of
+actual screen sizes and densities into:</p> 
+ 
+<ul> 
+<li>A set of four generalized <strong>sizes</strong>: <em>small</em>, <em>normal</em>,
+<em>large</em>, and <em>xlarge</em></em></li>
+<li>A set of four generalized <strong>densities</strong>: <em>ldpi</em> (low), <em>mdpi</em>
+(medium), <em>hdpi</em> (high), and <em>xhdpi</em> (extra high)</li>
+</ul> 
+
+<p>For information about how you can support multiple screen configurations in your
+application, see <a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple
+Screens</a>.</p>
 
 <p class="note"><strong>Note:</strong> This data is based on the number
-of Android devices that have accessed Android Market within a 14-day period
+of Android devices that have accessed Android Market within a 7-day period
 ending on the data collection date noted below.</p>
 
 
 <div class="dashboard-panel">
 
-<img alt="" width="460" height="250"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:2.3,0.4,45.9,51.2&chl=Small%20/%
-20ldpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" />
+<img alt="" width="400" height="250"
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=xlarge%20/%20mdpi|large%20/%20mdpi|normal%20/%20hdpi|normal%20/%20ldpi|normal%20/%20mdpi|small%20/%20hdpi&chd=t%3A0.5,2.6,75.5,1.2,17.1,3.2" />
 
 <table>
 <tr>
 <th></th>
-<th scope="col">Low Density</th>
-<th scope="col">Medium Density</th>
-<th scope="col">High Density</th>
+<th scope="col">ldpi</th>
+<th scope="col">mdpi</th>
+<th scope="col">hdpi</th>
+<th scope="col">xhdpi</th>
 </tr>
-<tr><th scope="row">Small</th> 
-<td class='cent hi'>2.3%</td> 
-<td></td> 
-<td></td> 
+<tr><th scope="row">small</th> 
+<td></td> <!-- ldpi -->
+<td></td> <!-- mdpi -->
+<td>3.2%</td> <!-- hdpi -->
+<td></td> <!-- xhdpi -->
 </tr> 
-<tr><th scope="row">Normal</th> 
-<td class='cent '>0.4%</td> 
-<td class='cent hi'>45.9%</td> 
-<td class='cent hi'>51.2%</td> 
+<tr><th scope="row">normal</th> 
+<td>1.2%</td> <!-- ldpi -->
+<td>17.1%</td> <!-- mdpi -->
+<td>75.5%</td> <!-- hdpi -->
+<td></td> <!-- xhdpi -->
 </tr> 
-<tr><th scope="row">Large</th> 
-<td></td> 
-<td></td> 
-<td></td> 
+<tr><th scope="row">large</th> 
+<td></td> <!-- ldpi -->
+<td>2.6%</td> <!-- mdpi -->
+<td></td> <!-- hdpi -->
+<td></td> <!-- xhdpi -->
+</tr> 
+<tr><th scope="row">xlarge</th> 
+<td></td> <!-- ldpi -->
+<td>0.5%</td> <!-- mdpi -->
+<td></td> <!-- hdpi -->
+<td></td> <!-- xhdpi -->
 </tr> 
 </table>
 
-<p><em>Data collected during two weeks ending on August 2, 2010</em></p>
+<p><em>Data collected during a 7-day period ending on May 6, 2011</em></p>
 </div>
 
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index a2580a6..c7dd916 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -81,6 +81,9 @@
       <li><a href="<?cs var:toroot ?>resources/dashboard/screens.html">
             <span class="en">Screen Sizes &amp; Densities</span>
           </a></li>
+      <li><a href="<?cs var:toroot ?>resources/dashboard/opengl.html">
+            <span class="en">OpenGL ES Versions</span>
+          </a></li>
     </ul>
   </li><?cs
   /if
diff --git a/docs/html/sdk/android-2.3.4.jd b/docs/html/sdk/android-2.3.4.jd
new file mode 100644
index 0000000..e5765d3
--- /dev/null
+++ b/docs/html/sdk/android-2.3.4.jd
@@ -0,0 +1,379 @@
+page.title=Android 2.3.4 Platform
+sdk.platform.version=2.3.4
+sdk.platform.apiLevel=10
+
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>In this document</h2>
+<ol>
+  <li><a href="#relnotes">Revisions</a></li>
+  <li><a href="#api">API Overview</a></li>
+  <li><a href="#openaccessory">Open Accessory Library</a></li>
+  <li><a href="#api-level">API Level</a></li>
+  <li><a href="#apps">Built-in Applications</a></li>
+  <li><a href="#locs">Locales</a></li>
+  <li><a href="#skins">Emulator Skins</a></li>
+</ol>
+
+<h2>Reference</h2>
+<ol>
+<li><a
+href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
+Differences Report &raquo;</a> </li>
+</ol>
+
+<h2>See Also</h2>
+<ol>
+  <li><a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a></li>
+</ol>
+
+</div>
+</div>
+
+<p>
+<em>API Level:</em>&nbsp;<strong>{@sdkPlatformApiLevel}</strong></p>
+
+<p>Android 2.3.4 is a maintenance release that adds several bug fixes and patches
+to the Android 2.3 platform, without any API changes from Android 2.3.3. Additionally, 
+Android 2.3.4 brings support for the Open Accessory API to mobile devices,
+through the optional <a href="#usb">Open Accessory Library</a>. </p>
+
+<p>For developers, the Android {@sdkPlatformVersion} platform and the Open
+Accessory Library are available together in the latest version of the Google
+APIs Add-On, a downloadable component for the Android SDK.</p>
+
+<p>To get started developing or testing against Android {@sdkPlatformVersion},
+use the Android SDK Manager to download the latest version of the Google APIs
+Add-On into your SDK. For more information, see <a
+href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>. If you
+are new to Android, <a href="{@docRoot}sdk/index.html">download the SDK Starter
+Package</a> first.</p>
+
+<p>For a high-level introduction to Android 2.3, see the <a
+href="{@docRoot}sdk/android-2.3-highlights.html">Platform Highlights</a>.</p>
+
+
+<h2 id="relnotes">Revisions</h2>
+
+<p>The sections below provide notes about successive releases of
+the Android {@sdkPlatformVersion} platform component for the Android SDK, as denoted by
+revision number. To determine what revision(s) of the Android
+{@sdkPlatformVersion} platforms are installed in your SDK environment, refer to
+the "Installed Packages" listing in the Android SDK and AVD Manager.</p>
+
+<script type="text/javascript">
+function toggleDiv(link) {
+  var toggleable = $(link).parent();
+  if (toggleable.hasClass("closed")) {
+    //$(".toggleme", toggleable).slideDown("fast");
+    toggleable.removeClass("closed");
+    toggleable.addClass("open");
+    $(".toggle-img", toggleable).attr("title", "hide").attr("src", (toRoot + "assets/images/triangle-opened.png"));
+  } else {
+    //$(".toggleme", toggleable).slideUp("fast");
+    toggleable.removeClass("open");
+    toggleable.addClass("closed");
+    $(".toggle-img", toggleable).attr("title", "show").attr("src", (toRoot + "assets/images/triangle-closed.png"));
+  }
+  return false;
+}
+</script>
+<style>
+.toggleable {
+  padding: .25em 1em 0em 1em;
+  margin-bottom: 0;
+}
+.toggleme {
+  padding: 1em 1em 0 2em;
+  line-height:1em;
+}
+.toggleable a {
+  text-decoration:none;
+}
+.toggleme a {
+  text-decoration:underline;
+}
+.toggleable.closed .toggleme {
+  display:none;
+}
+#jd-content .toggle-img {
+  margin:0;
+}
+</style>
+
+<div class="toggleable opened">
+  <a href="#" onclick="return toggleDiv(this)">
+        <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+        Android {@sdkPlatformVersion}, Revision 1</a> <em>(May 2011)</em></a>
+  <div class="toggleme">
+<dl>
+<dt>Dependencies:</dt>
+<dd>
+<p>Requires SDK Tools r11 or higher.</p>
+</dd>
+
+</dl>
+ </div>
+</div>
+
+
+<h2 id="api">API Overview</h2>
+
+<p>Android 2.3.4 provides the same framework API to applications as Android 2.3.3
+(API level 10). For a summary of the API, see the
+<a href="{@docRoot}sdk/android-2.3.3.html">Android 2.3.3 version notes</a>.</p>
+
+
+<h2 id="openaccessory">Open Accessory Library</h3>
+
+<p><em>Open Accessory</em> is a new capability for integrating
+connected peripherals with applications running on the platform. The capability
+is based on a USB (Universal Serial Bus) stack built into the platform and an
+API exposed to applications. Peripherals that attach to Android-powered devices
+as accessories connect as USB hosts. </p>
+
+<p>Open Accessory is introduced in <a
+href="{@docRoot}sdk/android-3.1.html#usb">Android 3.1</a> (API level 12), but is
+made available to devices running Android 2.3.4 by means of an optional external
+library, the Open Accessory Library. The library exposes a framework API that
+lets applications discover, communicate with, and manage a variety of device
+types connected over USB. It also provides the implementation of the API against
+parts of the Android platform that are not directly exposed to applications in
+Android 2.3.4.</p>
+
+<p>The Open Accessory Library is optional on any given device. Device
+manufacturers may choose whether to include the Open Accessory Library in their
+products or exclude it. The library is forward-compatible with Android 3.1, so
+applications developed against Android 2.3.4 will run properly on devices
+running Android 3.1, if those devices support USB accessories. </p>
+
+<p>The API provided by the Open Accessory Library is based on the Open Accessory
+API provided in Android 3.1. In most areas, you can use the same techniques and
+APIs. However, developing for the Open Accessory Library on Android 2.3.4 differs
+from the standard USB API in these ways:
+
+<ul>
+<li>Obtaining a {@link android.hardware.usb.UsbManager} object &mdash; To obtain
+a {@link android.hardware.usb.UsbManager} object when using the add-on library,
+use the helper method <code>getInstance()</code> rather than {@link
+android.content.Context#getSystemService(java.lang.String) getSystemService()}
+For example:
+
+<pre>UsbManager manager = UsbManager.getInstance(this);</pre></li>
+
+<li>Obtaining a {@link android.hardware.usb.UsbAccessory} from a filtered intent
+&mdash; When you filter for a connected device or accessory with an intent
+filter, the {@link android.hardware.usb.UsbAccessory} object is contained
+inside the intent that is passed to your application. If you are using the
+add-on library, you can get the {@link android.hardware.usb.UsbAccessory} object
+in the following manner:
+
+<pre>UsbAccessory accessory = UsbManager.getAccessory(intent)</pre></li>
+
+<li>No USB host support &mdash; Android 2.3.4 and the Open Accessory Library do
+not support USB host mode (for example, through {@link
+android.hardware.usb.UsbDevice}), although USB host mode is supported in Android
+3.1. An Android-powered device running Android 2.3.4 can not function as a USB
+host. The library enables the Android-powered device to function as
+a peripheral only, with the connected accessory functioning as USB host
+(through {@link android.hardware.usb.UsbAccessory}).</li>
+</ul>
+
+<p>To develop apps using the Open Accessory Library, you need:</p>
+
+<ul>
+<li>The latest version of the Android SDK tools</li>
+<li>The latest version of the Google APIs add-on, which includes the library
+itself (for linking)</li>
+<li>An actual hardware device running Android 2.3.4 (or Android 3.1) with USB
+accessories support, for runtime testing against connected devices</li>
+</ul>
+
+<p>For a full discussion of how to develop applications that interact with USB
+accessories, please see the related <a
+href="{@docRoot}guide/topics/usb/index.html">developer documentation</a>.</p>
+
+<p>Additionally, developers can request filtering on Android Market, such that
+their applications are not available to users whose devices do not provide the
+appropriate accessory support. To request filtering, add the element below
+to the application manifest:</p>
+
+<pre>&lt;uses-feature
+  android:name="android.hardware.usb.accessory"
+  android:required="true"&gt;</pre>
+
+
+<h2 id="api-level">API Level</h2>
+
+<p>The Android 2.3.4 platform does <em>not</em> increment the API level &mdash;
+it uses the same API level as Android 2.3.3, API level 10.
+
+<p>To use APIs introduced in API level 10 in your application,
+you need compile the application against the Android library that is provided in
+the latest version of the Google APIs Add-On, which also includes the Open
+Accessory Library.</p>
+
+<p>Depending on your needs, you might
+also need to add an <code>android:minSdkVersion="{@sdkPlatformApiLevel}"</code>
+attribute to the <code>&lt;uses-sdk&gt;</code> element in the application's
+manifest. If your application is designed to run only on Android 2.3.3 and higher,
+declaring the attribute prevents the application from being installed on earlier
+versions of the platform.</p>
+
+<p>For more information about how to use API Level, see the <a
+href="{@docRoot}guide/appendix/api-levels.html">API Levels</a> document. </p>
+
+<h2 id="apps">Built-in Applications</h2>
+
+<p>The system image included in the downloadable platform provides these
+built-in applications:</p>
+
+<table style="border:0;padding-bottom:0;margin-bottom:0;">
+<tr>
+<td style="border:0;padding-bottom:0;margin-bottom:0;">
+<ul>
+<li>Browser</li>
+<li>Calculator</li>
+<li>Camera</li>
+<li>Clock</li>
+<li>Contacts</li>
+<li>Cusom Locale</li>
+<li>Dev Tools</li>
+<li>Downloads</li>
+<li>Email</li>
+</ul>
+</td>
+<td style="border:0;padding-bottom:0;margin-bottom:0;padding-left:5em;">
+<ul>
+<li>Gallery</li>
+<li>IMEs for Japanese, Chinese, and Latin text input</li>
+<li>Messaging</li>
+<li>Music</li>
+<li>Phone</li>
+<li>Search</li>
+<li>Settings</li>
+<li>Spare Parts (developer app)</li>
+<li>Speech Recorder</li>
+</ul>
+</td>
+</tr>
+</table>
+
+
+<h2 id="locs" style="margin-top:.75em;">Locales</h2>
+
+<p>The system image included in the downloadable SDK platform provides a variety of
+built-in locales. In some cases, region-specific strings are available for the
+locales. In other cases, a default version of the language is used. The
+languages that are available in the Android {@sdkPlatformVersion} system
+image are listed below (with <em>language</em>_<em>country/region</em> locale
+descriptor).</p>
+
+<table style="border:0;padding-bottom:0;margin-bottom:0;">
+<tr>
+<td style="border:0;padding-bottom:0;margin-bottom:0;">
+<ul>
+<li>Arabic, Egypt (ar_EG)</li>
+<li>Arabic, Israel (ar_IL)</li>
+<li>Bulgarian, Bulgaria (bg_BG)</li>
+<li>Catalan, Spain (ca_ES)</li>
+<li>Czech, Czech Republic (cs_CZ)</li>
+<li>Danish, Denmark(da_DK)</li>
+<li>German, Austria (de_AT)</li>
+<li>German, Switzerland (de_CH)</li>
+<li>German, Germany (de_DE)</li>
+<li>German, Liechtenstein (de_LI)</li>
+<li>Greek, Greece (el_GR)</li>
+<li>English, Australia (en_AU)</li>
+<li>English, Canada (en_CA)</li>
+<li>English, Britain (en_GB)</li>
+<li>English, Ireland (en_IE)</li>
+<li>English, India (en_IN)</li>
+<li>English, New Zealand (en_NZ)</li>
+<li>English, Singapore(en_SG)</li>
+<li>English, US (en_US)</li>
+<li>English, Zimbabwe (en_ZA)</li>
+<li>Spanish (es_ES)</li>
+<li>Spanish, US (es_US)</li>
+<li>Finnish, Finland (fi_FI)</li>
+<li>French, Belgium (fr_BE)</li>
+<li>French, Canada (fr_CA)</li>
+<li>French, Switzerland (fr_CH)</li>
+<li>French, France (fr_FR)</li>
+<li>Hebrew, Israel (he_IL)</li>
+<li>Hindi, India (hi_IN)</li>
+</ul>
+</td>
+<td style="border:0;padding-bottom:0;margin-bottom:0;padding-left:5em;">
+<li>Croatian, Croatia (hr_HR)</li>
+<li>Hungarian, Hungary (hu_HU)</li>
+<li>Indonesian, Indonesia (id_ID)</li>
+<li>Italian, Switzerland (it_CH)</li>
+<li>Italian, Italy (it_IT)</li>
+<li>Japanese (ja_JP)</li>
+<li>Korean (ko_KR)</li>
+<li>Lithuanian, Lithuania (lt_LT)</li>
+<li>Latvian, Latvia (lv_LV)</li>
+<li>Norwegian-Bokmol, Norway(nb_NO)</li>
+<li>Dutch, Belgium (nl_BE)</li>
+<li>Dutch, Netherlands (nl_NL)</li>
+<li>Polish (pl_PL)</li>
+<li>Portuguese, Brazil (pt_BR)</li>
+<li>Portuguese, Portugal (pt_PT)</li>
+<li>Romanian, Romania (ro_RO)</li>
+<li>Russian (ru_RU)</li></li>
+<li>Slovak, Slovakia (sk_SK)</li>
+<li>Slovenian, Slovenia (sl_SI)</li>
+<li>Serbian (sr_RS)</li>
+<li>Swedish, Sweden (sv_SE)</li>
+<li>Thai, Thailand (th_TH)</li>
+<li>Tagalog, Philippines (tl_PH)</li>
+<li>Turkish, Turkey (tr_TR)</li>
+<li>Ukrainian, Ukraine (uk_UA)</li>
+<li>Vietnamese, Vietnam (vi_VN)</li>
+<li>Chinese, PRC (zh_CN)</li>
+<li>Chinese, Taiwan (zh_TW)</li>
+</td>
+</tr>
+</table>
+
+<p class="note"><strong>Note:</strong> The Android platform may support more
+locales than are included in the SDK system image. All of the supported locales
+are available in the <a href="http://source.android.com/">Android Open Source
+Project</a>.</p>
+
+<h2 id="skins">Emulator Skins</h2>
+
+<p>The downloadable platform includes a set of emulator skins that you can use
+for modeling your application in different screen sizes and resolutions. The
+emulator skins are:</p>
+
+<ul>
+  <li>
+    QVGA (240x320, low density, small screen)
+  </li>
+  <li>
+    WQVGA400 (240x400, low density, normal screen)
+  </li>
+  <li>
+    WQVGA432 (240x432, low density, normal screen)
+  </li>
+  <li>
+    HVGA (320x480, medium density, normal screen)
+  </li>
+  <li>
+    WVGA800 (480x800, high density, normal screen)
+  </li>
+  <li>
+    WVGA854 (480x854 high density, normal screen)
+  </li>
+</ul>
+
+<p>For more information about how to develop an application that displays
+and functions properly on all Android-powered devices, see <a
+href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple
+Screens</a>.</p>
diff --git a/docs/html/sdk/android-3.0-highlights.jd b/docs/html/sdk/android-3.0-highlights.jd
index d4fd002..33897de 100644
--- a/docs/html/sdk/android-3.0-highlights.jd
+++ b/docs/html/sdk/android-3.0-highlights.jd
@@ -39,10 +39,6 @@
 </style>
 
 
-
-
-
-
 <p>Welcome to Android 3.0!</p>
 
 <p>The Android 3.0 platform introduces many new and exciting features for users and developers. 
@@ -265,5 +261,3 @@
 href="{@docRoot}sdk/android-3.0.html">Android 3.0 Platform</a> document.</p>
 
 <p>For a video overview of platform features, see the Android 3.0 Sneak Peek. </p>
-
-
diff --git a/docs/html/sdk/android-3.0.jd b/docs/html/sdk/android-3.0.jd
index a81be20..d73bd63 100644
--- a/docs/html/sdk/android-3.0.jd
+++ b/docs/html/sdk/android-3.0.jd
@@ -559,7 +559,7 @@
     <p>Provides a search box that you can configure to deliver search queries to a specified
 activity and display search suggestions (in the same manner as the traditional search dialog). This
 widget is particularly useful for offering a search widget in the Action Bar. For more information,
-see <a href="{@docRoot}guide/topics/search/search-dialog.html">Creating a Search Interface</p>.</li>
+see <a href="{@docRoot}guide/topics/search/search-dialog.html">Creating a Search Interface.</p></li>
     
     <li>{@link android.widget.StackView}
     <p>A view that displays its children in a 3D stack and allows users to swipe through
diff --git a/docs/html/sdk/android-3.1-highlights.jd b/docs/html/sdk/android-3.1-highlights.jd
new file mode 100644
index 0000000..3d132a3
--- /dev/null
+++ b/docs/html/sdk/android-3.1-highlights.jd
@@ -0,0 +1,380 @@
+page.title=Android 3.1 Platform Highlights
+
+@jd:body
+
+
+<style type="text/css">
+#jd-content {
+  max-width:1200px;
+}
+#jd-content div.screenshot {
+  float:left;
+  clear:left;
+  padding:15px 30px 15px 0;
+}
+#jd-content div.video {
+  float:right;
+  padding:0 60px 40px;
+  margin-top:-15px;
+}
+#jd-content table.columns {
+  margin:0 0 1em 0;
+}
+#jd-content table.columns td {
+  padding:0;
+}
+#jd-content table.columns td+td {
+  padding:0 2em;
+}
+#jd-content table.columns td img {
+  margin:0;
+}
+#jd-content table.columns td+td>*:first-child {
+  margin-top:-2em;
+}
+.green {
+  color:#8db529;
+  font-weight:bold;
+}
+</style>
+
+<p>Welcome to Android 3.1!</p>
+
+<p>Android 3.1 is an incremental platform release that refines many of the
+features introduced in Android 3.0. It builds on the same tablet-optimized UI
+and features offered in Android 3.0 and adds several new capabilities for
+users and developers. This document provides an overview of the new features and
+technologies introduced in Android 3.1. For a more detailed look at new
+developer APIs, see the <a href="{@docRoot}sdk/android-3.1.html">API
+Overview</a> document.</p>
+
+<p>For a high-level introduction to Android 3.0, please see the <a
+href="{@docRoot}sdk/android-3.0-highlights.html">Android 3.0 Platform
+Highlights</a>.</p>
+
+<ul>
+  <li><a href="#UserFeatures">New User Features</a></li>
+  <li><a href="#DeveloperApis">New Developer Features</a></li>
+</ul>
+
+<h2 id="UserFeatures" style="clear:right">New User Features</h2>
+
+<div  style="padding-top:0em;">
+<div style="margin-right:1em;margin-left:1em;float:right;padding-top:2em;"><a href="images/3.1/home_full.png" target="_android"><img src="images/3.1/home.png" alt="" height="280" /></a>
+<div style="padding-left:1.25em;padding-bottom:1.25em;width:450px;font-size:.9em"><strong>Figure 1.</strong> An Android 3.1 Home screen.</div>
+</div>
+
+<h3>UI refinements</h3>
+
+<p>The Android 3.1 platform adds a variety of refinements to make the user
+interface more intuitive and more efficient to use.</p>
+
+<p>UI transitions are improved throughout the system and across the standard
+apps. The Launcher animation is optimized for faster, smoother transition to and
+from the Apps list. Adjustments in color, positioning, and text make UI elements
+easier to see, understand, and use. Accessibility is improved with consistent
+audible feedback throughout the UI and a new setting to let users customize the
+touch-hold interval to meet their needs.</p>
+
+<p>Navigation to and from the five home screens is now easier &mdash; touching
+the Home button in the system bar now takes you to the home screen most recently
+used. Settings offers an improved view of internal storage,
+showing the storage used by a larger set of file types. </p>
+
+<h3 id="accessories">Connectivity for USB accessories</h3>
+
+<p>Android 3.1 adds broad platform support for a variety of USB-connected
+peripherals and accessories. Users can attach many types of input devices
+(keyboards, mice, game controllers) and digital cameras. Applications can build
+on the platform’s USB support to extend connectivity to almost any type of USB
+device.</p>
+
+<p>The platform also adds new support for USB accessories &mdash; external
+hardware devices designed to attach to Android-powered devices as USB hosts. When an
+accessory is attached, the framework will look for a corresponding application
+and offer to launch it for the user.  The accessory can also present a URL 
+to the user, for downloading an appropriate application if one is not already
+installed.  Users can interact with the application to control powered accessories such
+as robotics controllers; docking stations; diagnostic and musical equipment;
+kiosks; card readers; and much more.</p>
+
+<p>The platform’s USB capabilities rely on components in device hardware, so
+support for USB on specific devices may vary and is determined by device
+manufacturers.</p>
+
+<div  style="padding-top:0em;">
+<div style="margin-right:.8em;float:left;width:200px;"><img src="{@docRoot}sdk/images/3.1/tasks.png" alt="" />
+<div style="padding-left:1em;padding-bottom:1em;xwidth:auto;font-size:.9em"><strong>Figure 2.</strong> The Recent Apps menu is now expandable and scrollable.</div>
+</div>
+
+<h3 id="recentapps">Expanded Recent Apps list</h3>
+
+<p>For improved multitasking and instant visual access to a much larger number
+of apps, the Recent Apps list is now expandable. Users can now scroll the list
+of recent apps vertically to see thumbnail images all of the tasks in progress
+and recently used apps, then touch a thumbnail to jump back into that task.</p>
+
+<h3 id="resizewidgets">Resizeable Home screen widgets</h3>
+
+<p>For more flexible Home screen customization, users can now resize their Home
+screen widgets using drag bars provided by the system. Users can expand widgets
+both horizontally and/or vertically to include more content, where supported by
+each widget.</p>
+
+
+<h3 id="keyboards">Support for external keyboards
+and pointing devices</h3>
+
+<p>Users can now attach almost any type of external keyboard or mouse to their
+Android-powered devices, to create a familiar environment and work more
+efficiently. One or more input devices can be attached to the system simultaneously
+over USB and/or Bluetooth HID, in any combination. No special configuration or
+driver is needed, in most cases. When multiple devices are connected, users can
+conveniently manage the active keyboard and IME using the keyboard settings that
+are available from the System bar.</p>
+
+<p>For pointing devices, the platform supports most types of mouse with a single
+button and optionally a scroll wheel, as well as similar devices such as
+trackballs. When these are connected, users can interact with the UI using
+point, select, drag, scroll, hover, and other standard actions.</p>
+
+<h3 id="joysticks">Support for joysticks and gamepads</h3>
+
+<p>To make the platform even better for gaming, Android 3.1 adds support for
+most PC joysticks and gamepads that are connected over USB or Bluetooth HID.</p>
+
+<p>For example, users can connect Sony Playstation&trade; 3 and XBox 360&trade; game
+controllers over USB (but not Bluetooth), Logitech Dual Action&trade; gamepads and
+flight sticks, or a car racing controller. Game controllers that use proprietary
+networking or pairing are not supported by default, but in general, the platform
+supports most PC-connectible joysticks and gamepads.</p>
+
+<h3 id="wifi">Robust Wi-Fi networking</h3>
+
+<p>Android 3.1 adds robust Wi-Fi features, to make sure that users and their
+apps can take full advantage of higher-speed Wi-Fi access at home, at work, and
+while away.</p>
+
+<p>A new high-performance Wi-Fi lock lets applications maintain
+high-performance Wi-Fi connections even when the device screen is off. Users can
+take advantage of this to play continuous streamed music, video, and voice
+services for long periods, even when the device is otherwise idle and the screen
+is off. </p>
+
+<p>Users can now configure an HTTP proxy for each individual Wi-Fi access
+point, by touch-hold of the access point in Settings. The browser uses the HTTP
+proxy when communicating with the network over the access point and other apps
+may also choose to do so. The platform also provides backup and restore of the
+user-defined IP and proxy settings.</p>
+<p>The platform adds support for Preferred Network Offload (PNO), a background
+scanning capability that conserves battery power savings in cases where Wi-Fi
+needs to be available continuously for long periods of time.</p>
+
+<h3 id="apps">Updated set of standard apps</h3>
+
+<p>The Android 3.1 platform includes an updated set of standard applications
+that are optimized for use on larger screen devices. The sections below
+highlight some of the new features.</p>
+
+<div  style="padding-top:0em;">
+<div style="margin-right:1em;float:right;margin-left:1em;margin-top:1.5em;margin-bottom:0;padding-bottom:0;"><img src="{@docRoot}sdk/images/3.1/controls.png" alt="" height="280px" />
+<div style="padding-left:1.25em;padding-bottom:1.25em;margin-top:0;padding-top:0;font-size:.9em"><strong>Figure 3.</strong> Quick Controls menu in the Browser.</div>
+</div>
+</div>
+
+<p><strong>Browser</strong></p>
+
+<p>The Browser app includes a variety of new features and UI improvements that
+make viewing web content simpler, faster, and more convenient.</p>
+
+<p>The Quick Controls UI, accessible from Browser Settings, is extended and
+redesigned. Users can now use the controls to view thumbnails of open tabs and
+close the active tab, as well as access the overflow menu for instant access to
+Settings and other controls.</p>
+
+<p>To ensure a consistent viewing experience, the Browser extends it's support
+for popular web standards such as CSS 3D, animations, and CSS fixed
+positioning to all sites, mobile or desktop. It also adds support for embedded
+playback of HTML5 video content. To make it easier to manage favorite
+content, users can now save a web page locally for offline viewing, including
+all styling and images. For convenience when visiting Google sites, an improved
+auto-login UI lets users sign in quickly and manage access when multiple users
+are sharing a device.</p>
+
+<p>For best performance, the Browser adds support for plugins that use hardware
+accelerated rendering. Page zoom performance is also dramatically improved,
+making it faster to navigate and view web pages.</p>
+
+<p><strong>Gallery</strong></p>
+
+<p>The Gallery app now supports Picture Transfer Protocol (PTP), so that users
+can connect their cameras over USB and import their pictures to Gallery with a
+single touch. The app also copies the pictures to local storage and provides an
+indicator to let users see how much space is available.</p>
+
+<div  style="padding-top:0em;">
+<div style="margin-right:1em;float:left;margin-left:0em;"><img src="{@docRoot}sdk/images/3.1/resizeable.png" alt="" width="170"  target="_android" style="margin-bottom:0;" />
+<div style="padding-left:1.4em;padding-bottom:1em;width:180px;font-size:.9em"><strong>Figure 
+4.</strong> Home screen widgets can now be resized.</div></div>
+
+<p><strong>Calendar</strong></p>
+
+<p>Calendar grids are larger, for better readability and more accurate
+touch-targeting. Additionally, users can create a larger viewing area for grids
+by hiding the calendar list controls. Controls in the date picker are
+redesigned, making them easier to see and use.</li>
+</ul>
+
+<p><strong>Contacts</strong></p>
+
+<p>The Contacts app now lets you locate contacts more easily using full text
+search. Search returns matching results from all fields that are stored for a
+contact.
+</p>
+
+<p><strong>Email</strong></p>
+
+<p>When replying or forwarding an HTML message, The Email app now sends both
+plain text and HTML bodies as a multi-part mime message. This ensures that the
+message will be formatted properly for all recipients. Folder prefixes for IMAP
+accounts are now easier to define and manage. To conserve battery power and
+minimize cell data usage, the application now prefetches email from the server
+only when the device is connected to a Wi-Fi access point. </p>
+
+<p>An updated Home screen widget give users quick access to more email. Users
+can touch Email icon at the top of the widget to cycle through labels such as
+Inbox, Unread, and Starred. The widget itself is now resizable, both
+horizontally and vertically.</p>
+
+<h3 id="enterprise">Enterprise support</h3>
+
+<p>Users can now configure an HTTP proxy for each connected Wi-Fi access point.
+This lets administrators work with users to set a proxy hostname, port, and any
+bypass subdomains. This proxy configuration is automatically used by the Browser
+when the Wi-Fi access point is connected, and may optionally be used by other
+apps. The proxy and IP configuration is now backed up and restored across system
+updates and resets.</p>
+
+<p>To meet the needs of tablet users, the platform now allows a "encrypted
+storage card" device policy to be accepted on devices with emulated storage
+cards and encrypted primary storage.</p>
+
+
+<h2 id="DeveloperApis" style="clear:both">New Developer Features</h2>
+
+<p>The Android 3.1 platform adds refinements and new capabilities that
+developers can build on, to create powerful and engaging application experiences
+on tablets and other large-screen devices. </p>
+
+<h3 id="accessory">Open Accessory API for rich interaction with
+peripherals</h3>
+
+<p>Android 3.1 introduces a new API for integrating hardware accessories with
+applications running on the platform. The API provides a way to interact across
+a wide range of peripherals, from robotics controllers to musical equipment,
+exercise bicycles, and more.</p>
+
+<p>The API is based on a new USB (Universal Serial Bus) stack and services
+that are built into the platform. The platform provides services for discovering
+and identifying connected hardware, as well as for notifying interested
+applications that the hardware is available.</p>
+
+<p>When a user plugs in a USB accessory, the platform receives
+identifying information such as product name, accessory type, manufacturer, and
+version. The platform sets up communication with the accessory and uses its
+information to notify and launch a targeted app, if one is available. Optionally,
+an accessory can provide a URL that lets users find and download an
+app that works with the accessory. These discovery features make
+first-time setup easier for the user and ensure that an appropriate application
+is available for interacting with the connected hardware. </p>
+
+<p>For application developers and accessory manufacturers, accessory mode offers
+many new ways to engage users and build powerful interaction experiences with
+connected hardware.</p>
+
+<p>To learn more about how to develop applications that interact with
+accessories, see the <a href="{@docRoot}guide/topics/usb/accessory.html">USB
+Accessory</a> documentation.</p>
+
+<h3 id="host">USB host API</h3>
+
+<p>Android 3.1 provides built-in platform support for USB host mode and exposes
+an API that lets applications manage connected peripherals. On devices that
+support host mode, applications can use the API to identify and communicate with
+connected devices such as audio devices. input devices, communications devices,
+hubs, cameras, and more.</p>
+
+<p>To learn more about how to develop applications that interact with
+USB devices, see the <a href="{@docRoot}guide/topics/usb/host.html">USB
+Host</a> documentation.</p>
+
+<h3 id="inputdevices">Input from mice, joysticks, and gamepads</h3>
+
+<p>Android 3.1 extends the input event system to support a variety of new input
+sources and motion events, across all views and windows. Developers can build on
+these capabilities to let users interact with their applications using mice,
+trackballs, joysticks, gamepads, and other devices, in addition to keyboards and
+touchscreens.</p>
+
+<p>For mouse and trackball input, the platform supports two new motion event
+actions: scroll (horizontal or vertical) such as from a scrollwheel; and hover,
+which reports the location of the mouse when no buttons are pressed.
+Applications can handle these events in any way needed.</p>
+
+<p>For joysticks and gamepads, the platform provides a large number of motion
+axes that applications can use from a given input source, such as X, Y, Hat X,
+Hat Y, rotation, throttle, pressure, size, touch, tool, orientation, and others.
+Developers can also define custom axes if needed, to capture motion in
+additional ways. The platform provides motion events to applications as a batch,
+and applications can query the details of the movements included in the batch,
+for more efficient and precise handling of events.</p>
+
+<p>Applications can query for the list of connected input devices and the motion
+ranges (axes) supported by each device. Applications can also handle multiple
+input and motion events from a single input device. For example, an application
+can use mouse and joystick and mouse event sources from a single input
+device.</p>
+
+<h3 id="resizewidgetsapp">Resizable Home screen widgets</h3>
+
+<p>Developers can now create Home screen widgets that users can resize
+horizontally, vertically, or both. By simply adding an attribute to the
+declaration of a widget, the widget becomes resizable horizontally, vertically,
+or both. This lets users customize the display of the widget content and display
+more of it on their Home screens.</p>
+
+<h3 id="mtp">MTP API for integrating with external cameras</h3>
+
+<p>In Android 3.1, a new MTP (Media Transfer Protocol) API lets developers write
+apps that interact directly with connected cameras and other PTP devices. The
+new API makes it easy for applications to receive notifications when devices are
+attached and removed, manage files and storage on those devices, and transfer
+files and metadata to and from them. The MTP API implements the PTP (Picture
+Transfer Protocol) subset of the MTP specification.</p>
+
+<h3 id="rtp">RTP API, for control over audio streaming sessions</h3>
+
+<p>Android 3.1 exposes an API to its built-in RTP (Real-time Transport Protocol)
+stack, which applications can use to directly manage on-demand or interactive
+data streaming. In particular, apps that provide VOIP, push-to-talk,
+conferencing, and audio streaming can use the API to initiate sessions and
+transmit or receive data streams over any available network.</p>
+
+<h3 id="performance">Performance optimizations</h3>
+
+<p>Android 3.1 includes a variety of performance optimizations that help make
+applications faster and more responsive. Some of the optimizations include:</p>
+
+<ul>
+<li>A new LRU cache class lets applications benefit from efficient caching.
+Applications can use the class to reduce the time spent computing or downloading
+data from the network, while maintaining a sensible memory footprint for the
+cached data.</li>
+<li>The UI framework now supports partial invalidates in hardware-accelerated
+Views, which makes drawing operations in those Views more efficient.</li>
+<li>A new graphics method, {@link android.graphics.Bitmap#setHasAlpha(boolean)
+setHasAlpha()}, allows apps to hint that a given bitmap is opaque. This provides
+an extra performance boost for some types of blits and is especially useful for
+applications that use ARGB_8888 bitmaps.</li>
+</ul>
+
diff --git a/docs/html/sdk/android-3.1.jd b/docs/html/sdk/android-3.1.jd
new file mode 100644
index 0000000..57fe1eb
--- /dev/null
+++ b/docs/html/sdk/android-3.1.jd
@@ -0,0 +1,1053 @@
+page.title=Android 3.1 Platform
+sdk.platform.version=3.1
+sdk.platform.apiLevel=12
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>In this document</h2>
+<ol>
+  <li><a href="#relnotes">Revisions</a></li>
+  <li><a href="#api">API Overview</a></li>
+  <li><a href="#api-level">API Level</a></li>
+  <li><a href="#apps">Built-in Applications</a></li>
+  <li><a href="#locs">Locales</a></li>
+  <li><a href="#skins">Emulator Skins</a></li>
+</ol>
+
+<h2>Reference</h2>
+<ol>
+<li><a
+href="{@docRoot}sdk/api_diff/11/changes.html">API
+Differences Report &raquo;</a> </li>
+</ol>
+
+<h2>See Also</h2>
+<ol>
+  <li><a href="{@docRoot}guide/practices/optimizing-for-3.0.html">Optimizing
+Apps for Android 3.0</a></li>
+</ol>
+
+</div>
+</div>
+
+
+<p><em>API Level:</em>&nbsp;<strong>{@sdkPlatformApiLevel}</strong></p>
+
+<p>For developers, the Android {@sdkPlatformVersion} platform is available as a
+downloadable component for the Android SDK. The downloadable platform includes
+an Android library and system image, as well as a set of emulator skins and
+more. The downloadable platform includes no external libraries.</p>
+
+<p>To get started developing or testing against Android {@sdkPlatformVersion},
+use the Android SDK Manager to download the platform into your SDK. For more
+information, see <a href="{@docRoot}sdk/adding-components.html">Adding SDK
+Components</a>. If you are new to Android, <a
+href="{@docRoot}sdk/index.html">download the SDK Starter Package</a> first.</p>
+
+<p>For a high-level introduction to Android {@sdkPlatformVersion}, see the <a
+href="{@docRoot}sdk/android-{@sdkPlatformVersion}-highlights.html">Platform
+Highlights</a>.</p>
+
+<p class="note"><strong>Reminder:</strong> If you've already published an
+Android application, please test and optimize your application on Android 3.0
+and Android 3.1 as soon as possible. You should do so to be sure your
+application provides the best experience possible on the latest Android-powered
+devices. For information about what you can do, read <a
+href="{@docRoot}guide/practices/optimizing-for-3.0.html">Optimizing Apps for
+Android 3.0</a>.</p>
+
+
+<h2 id="relnotes">Revisions</h2>
+
+<p>To determine what revision of the Android {@sdkPlatformVersion} platform you
+have installed, refer to the "Installed Packages" listing in the Android SDK and
+AVD Manager.</p>
+
+
+<div class="toggle-content opened" style="padding-left:1em;">
+
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-opened.png"
+class="toggle-content-img" alt="" />
+    Android {@sdkPlatformVersion}, Revision 1</a> <em>(May 2011)</em>
+  </a></p>
+
+  <div class="toggle-content-toggleme" style="padding-left:2em;">
+
+<dl>
+
+<dt>Dependencies:</dt>
+<dd>
+<p>Requires <a href="{@docRoot}sdk/tools-notes.html">SDK Tools r11</a> or
+higher.</p>
+</dd>
+
+</dl>
+
+  </div>
+</div>
+
+
+<h2 id="#api" style="margin-top:1.5em;">API Overview</h2>
+
+<p>The sections below provide a technical overview of what's new for developers
+in Android 3.1, including new features and changes in the framework API since
+the previous version.</p>
+
+<h3 id="usb">USB APIs</h3>
+
+<p>Android 3.1 introduces powerful new APIs for
+integrating connected peripherals with applications running on the platform.
+The APIs are based on a USB (Universal Serial Bus) stack and services that are
+built into the platform, including support for both USB host and device
+interactions. Using the APIs, developers can create applications that are able to
+discover, communicate with, and manage a variety of device types connected over
+USB. </p>
+
+<p>The stack and APIs distinguish two basic types of USB hardware, based on
+whether the platform iself is acting as host or the external hardware is acting
+as host: </p>
+
+<ul>
+<li>A <em>USB device</em> is a piece of connected hardware that depends on the
+Android-powered device to serve as host. For example, most input devices, mice,
+and joysticks are USB devices, as are many cameras, hubs, and so on.</li>
+<li>A <em>USB accessory</em> is a piece of connected hardware that has a USB
+host controller, provides power, and is designed to communicate with
+Android-powered devices over USB, A variety of peripherals can connect as
+accessories, from robotics controllers to musical equipment, exercise bicycles,
+and more.</li>
+</ul>
+
+<p>For both types &mdash; USB devices and USB accessories &mdash; the
+platform's USB APIs support discovery by intent broadcast when attached or
+detached, as well as standard interfaces, endpoints, and transfer modes
+(control, bulk, and interrupt).</p>
+
+<p>The USB APIs are available in the package {@link android.hardware.usb}. The
+central class is {@link android.hardware.usb.UsbManager}, which provides
+helper methods for identifying and communicating with
+both USB devices and USB accessories. Applications can acquire an instance of
+{@link android.hardware.usb.UsbManager} and then query for the list of attached
+devices or accessories and then communicate with or manage them.
+{@link android.hardware.usb.UsbManager} also declares intent actions that the
+system broadcasts, to announce when a USB device or accessory is attached or
+detached.</p>
+
+<p>Other classes include:</p>
+
+<ul>
+<li>{@link android.hardware.usb.UsbDevice}, a class representing external
+hardware connected as a USB device (with the Android-powered device acting as
+host).</li>
+<li>{@link android.hardware.usb.UsbAccessory}, representing external hardware
+connected as the USB host (with the Android-powered device acting as a USB
+device).</li>
+<li>{@link android.hardware.usb.UsbInterface} and {@link
+android.hardware.usb.UsbEndpoint}, which provide access to standard USB
+interfaces and endpoints for a device.</li>
+<li>{@link android.hardware.usb.UsbDeviceConnection} and {@link
+android.hardware.usb.UsbRequest}, for sending and receiving data and control
+messages to or from a USB device, sychronously and asynchronously.
+<li>{@link android.hardware.usb.UsbConstants}, which provides constants for
+declaring endpoint types, device classes, and so on.</li>
+</ul>
+
+<p>Note that although the USB stack is built into the platform, actual support
+for USB host and open accessory modes on specific devices is determined by
+their manufacturers. In particular, host mode relies on appropriate USB
+controller hardware in the Android-powered device. </p>
+
+<p>Additionally, developers can request filtering on Android Market, such that
+their applications are not availabe to users whose devices do not provide the
+appropriate USB support. To request filtering, add one or both of the elements
+below to the application manifest, as appropriate: </p>
+
+<ul>
+<li>If the application should only be visible to devices that support USB
+host mode (connection of USB devices), declare this element:
+  <p style="margin-left:1.5em;"><code>&lt;uses-feature
+  android:name="android.hardware.usb.host"
+  android:required="true"&gt;</code></p>
+</li>
+<li>If the application should only be visible to devices that support USB
+accessories (connection of USB hosts), declare this element:
+  <p style="margin-left:1.5em;"><code>&lt;uses-feature
+  android:name="android.hardware.usb.accessory"
+  android:required="true"&gt;</code></p>
+</li>
+</ul>
+
+<p>For complete information about how to develop applications that interact with
+USB accessories, please see the
+<a href="{@docRoot}guide/topics/usb/index.html">developer documentation</a>.</p>
+
+<p class="note">To look at sample applications that use the USB host API, see <a
+href="{@docRoot}resources/samples/USB/AdbTest/index.html">ADB Test</a> and <a
+href="{@docRoot}resources/samples/USB/MissileLauncher/index.html">Missile
+Launcher</a></p>
+
+<h3>MTP/PTP API</h3>
+
+<p>Android 3.1 exposes a new MTP API that lets applications interact directly
+with connected cameras and other PTP devices. The new API makes it easy for an
+application to receive notifications when devices are attached and removed,
+manage files and storage on those devices, and transfer files and metadata to
+and from them. The MTP API implements the PTP (Picture Transfer Protocol) subset
+of the MTP (Media Transfer Protocol) specification.</p>
+
+<p>The MTP API is available in the {@link android.mtp} package and provides
+these classes: </p>
+
+<ul>
+  <li>The {@link android.mtp.MtpDevice} encapsulates an MTP device that is
+connected over the USB host bus. An application can instantiate an object of
+this type and then use its methods to get information about the device and
+objects stored on it, as well as opening the connection and transferring data.
+Some of the methods include:
+    <ul>
+      <li>{@link android.mtp.MtpDevice#getObjectHandles(int, int, int)
+getObjectHandles()} returns a list of handles for all objects on the device that
+match a specified format and parent. To get information about an object, an
+application can pass a handle to {@link android.mtp.MtpDevice#getObjectInfo(int)
+getObjectInfo()}.</li>
+      <li>{@link android.mtp.MtpDevice#importFile(int, java.lang.String)
+importFile()} lets an application copy data for an object to a file in external
+storage. This call may block for an arbitrary amount of time depending on the
+size of the data and speed of the devices, so should be made from a spearate
+thread.</li>
+      <li>{@link
+android.mtp.MtpDevice#open(android.hardware.usb.UsbDeviceConnection) open()}
+lets an application open a connected MTP/PTP device. </li>
+      <li>{@link android.mtp.MtpDevice#getThumbnail(int) getThumbnail()} returns
+the thumbnail of the object as a byte array. </li>
+    </ul>
+  </li>
+  <li>{@link android.mtp.MtpStorageInfo} holds information about about a storage
+unit on an MTP device, corresponding to the StorageInfo Dataset described in
+section 5.2.2 of the MTP  specification. Methods in the class let an application
+get a storage unit’s description string, free space, maximum storage capacity,
+storage ID, and volume identifier.</li>
+  <li>{@link android.mtp.MtpDeviceInfo}  holds information about an MTP device
+corresponding to the DeviceInfo Dataset described in section 5.1.1 of the MTP
+specification. Methods in the class let applications get a device’s
+manufacturer, model, serial number, and version.</li>
+  <li>{@link android.mtp.MtpObjectInfo} holds information about an object stored
+on an MTP device, corresponding to the ObjectInfo Dataset described in section
+5.3.1 of the MTP specification. Methods in the class let applications get an
+object’s size, data format, association type, creation date, and thumbnail
+information.</li>
+  <li>{@link android.mtp.MtpConstants} provides constants for declaring MTP file
+format codes, association type, and protection status.</li>
+</ul>
+
+<h3 id="motionevents">Support for new input devices and motion events</h3>
+
+<p>Android 3.1 extends the input subsystem to support new input devices and new
+types of motion events, across all views and windows. Developers can build on
+these capabilities to let users interact with their applications using mice,
+trackballs, joysticks, gamepads, and other devices, in addition to keyboards and
+touchscreens. </p>
+
+<p>For handling mouse, scrollwheel, and trackball input, the platform supports
+two new motion event actions:</p>
+<ul>
+<li>{@link android.view.MotionEvent#ACTION_SCROLL}, which describes the pointer
+location at which a non-touch scroll motion, such as from a mouse scroll wheel,
+took place. In the MotionEvent, the value of the {@link
+android.view.MotionEvent#AXIS_HSCROLL} and {@link
+android.view.MotionEvent#AXIS_VSCROLL} axes specify the relative scroll
+movement. </li>
+<li>{@link android.view.MotionEvent#ACTION_HOVER_MOVE}, reports the current
+position of the mouse when no buttons are pressed, as well as any intermediate
+points since the last <code>HOVER_MOVE</code> event. Hover enter and exit
+notifications are not yet supported.</li>
+</ul>
+
+<p>To support joysticks and gamepads, the {@link android.view.InputDevice} class
+includes these new input device sources:</p>
+<ul>
+<li>{@link android.view.InputDevice#SOURCE_CLASS_JOYSTICK} &mdash; the source
+device has joystick axes.</li>
+<li>{@link android.view.InputDevice#SOURCE_CLASS_BUTTON} &mdash; the source
+device has buttons or keys.</li>
+<li>{@link android.view.InputDevice#SOURCE_GAMEPAD}  &mdash;  the source device
+has gamepad buttons such as {@link android.view.KeyEvent#KEYCODE_BUTTON_A}
+or {@link android.view.KeyEvent#KEYCODE_BUTTON_B}. Implies
+{@link android.view.InputDevice#SOURCE_CLASS_BUTTON}</li>
+<li>{@link android.view.InputDevice#SOURCE_JOYSTICK} &mdash; the source device
+has joystick axes.  Implies SOURCE_CLASS_JOYSTICK.</li>
+</ul>
+
+<p>To describe motion events from these new sources, as well as those from mice
+and trackballs, the platform now defines axis codes on {@link
+android.view.MotionEvent}, similar to how it defines key codes on {@link
+android.view.KeyEvent}. New axis codes for joysticks
+and game controllers include
+{@link android.view.MotionEvent#AXIS_HAT_X}, {@link
+android.view.MotionEvent#AXIS_HAT_Y}, {@link
+android.view.MotionEvent#AXIS_RTRIGGER}, {@link
+android.view.MotionEvent#AXIS_ORIENTATION}, {@link
+android.view.MotionEvent#AXIS_THROTTLE}, and many others. 
+Existing {@link android.view.MotionEvent} axes are represented by {@link
+android.view.MotionEvent#AXIS_X}, {@link android.view.MotionEvent#AXIS_Y},
+{@link android.view.MotionEvent#AXIS_PRESSURE}, {@link
+android.view.MotionEvent#AXIS_SIZE}, {@link
+android.view.MotionEvent#AXIS_TOUCH_MAJOR}, {@link
+android.view.MotionEvent#AXIS_TOUCH_MINOR}, {@link
+android.view.MotionEvent#AXIS_TOOL_MAJOR}, {@link
+android.view.MotionEvent#AXIS_TOOL_MINOR}, and {@link
+android.view.MotionEvent#AXIS_ORIENTATION}.</p>
+
+<p>Additionally, {@link android.view.MotionEvent} defines a number of generic
+axis codes that are used when the framework does not know how to map a
+particular axis. Specific devices can use the generic axis codes to pass custom
+motion data to applications. For a full list of axes and their intended
+interpretations, see the {@link android.view.MotionEvent} class documentation.
+</p>
+
+<p>The platform provides motion events to applications in batches, so a single
+event may contain a current position and multiple so-called historical movements.
+Applications should use {@link android.view.MotionEvent#getHistorySize()} to get
+the number of historical samples, then retrieve and process all historical
+samples in order using {@link
+android.view.MotionEvent#getHistoricalAxisValue(int, int, int)
+getHistoricalAxisValue()}.  After that, applications should process the current
+sample using {@link android.view.MotionEvent#getAxisValue(int) getAxisValue()}.
+</p>
+
+<p>Some axes can be retrieved using special accessor methods.  For example,
+instead of calling {@link android.view.MotionEvent#getAxisValue(int)
+getAxisValue()}, applications can call {@link android.view.MotionEvent#getX(int)
+getX()}.  Axes that have built-in accessors include {@link
+android.view.MotionEvent#AXIS_X}, {@link android.view.MotionEvent#AXIS_Y},
+{@link android.view.MotionEvent#AXIS_PRESSURE}, {@link
+android.view.MotionEvent#AXIS_SIZE}, {@link
+android.view.MotionEvent#AXIS_TOUCH_MAJOR}, {@link
+android.view.MotionEvent#AXIS_TOUCH_MINOR}, {@link
+android.view.MotionEvent#AXIS_TOOL_MAJOR}, {@link
+android.view.MotionEvent#AXIS_TOOL_MINOR}, and {@link
+android.view.MotionEvent#AXIS_ORIENTATION}.</p>
+
+<p>Each input device has a unique, system-assigned ID and may also provide
+multiple sources. When a device provides multiple sources, more than one source
+can provide axis data using the same axis. For example, a touch event coming
+from the touch source uses the X axis for screen position data, while a joystick
+event coming from the joystick source will use the X axis for the stick position
+instead. For this reason, it's important for applications to interpret axis
+values according to the source from which they originate. When handling a motion
+event, applications should use methods on the {@link android.view.InputDevice}
+class to determine the axes supported by a device or source. Specifically,
+applications can use {@link android.view.InputDevice#getMotionRanges()
+getMotionRanges()} to query for all axes of a device or all axes of a given
+source of the device. In both cases, the range information for axes returned in
+the {@link android.view.InputDevice.MotionRange} object specifies the source for
+each axis value.</p>
+
+<p>Finally, since the motion events from joysticks, gamepads, mice, and
+trackballs are not touch events, the platform adds a new callback method for
+passing them to a {@link android.view.View} as "generic" motion events.
+Specifically, it reports the non-touch motion events to 
+{@link android.view.View}s through a call to {@link
+android.view.View#onGenericMotionEvent(android.view.MotionEvent)
+onGenericMotionEvent()}, rather than to {@link
+android.view.View#onTouchEvent(android.view.MotionEvent)
+onTouchEvent()}.</p>
+
+<p>The platform dispatches generic motion events differently, depending on the
+event source class. {@link android.view.InputDevice#SOURCE_CLASS_POINTER} events
+go to the {@link android.view.View} under the pointer, similar to how touch
+events work.  All others go to the currently focused {@link android.view.View}.
+For example, this means a {@link android.view.View} must take focus in order to
+receive joystick events. If needed, applications can handle these events at the
+level of Activity or Dialog by implementing {@link
+android.view.View#onGenericMotionEvent(android.view.MotionEvent)
+onGenericMotionEvent()} there instead.</p>
+
+<p class="note">To look at a sample application that uses joystick motion
+events, see <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/
+GameControllerInput.html">GameControllerInput</a> and <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/
+GameView.html">GameView</a>.</p>
+
+<h3>RTP API</h3>
+
+<p>Android 3.1 exposes an API to its built-in RTP (Real-time Transport Protocol)
+stack, which applications can use to manage on-demand or interactive data
+streaming. In particular, apps that provide VOIP, push-to-talk, conferencing,
+and audio streaming can use the API to initiate sessions and transmit or receive
+data streams over any available network.</p>
+
+<p>The RTP API is available in the {@link android.net.rtp} package. Classes
+include: </p>
+<ul>
+<li>{@link android.net.rtp.RtpStream}, the base class of streams that send and
+receive network packets with media payloads over RTP.</li>
+<li>{@link android.net.rtp.AudioStream}, a subclass of {@link
+android.net.rtp.RtpStream} that carries audio payloads over RTP.</li>
+<li>{@link android.net.rtp.AudioGroup}, a local audio hub for managing and
+mixing the device speaker, microphone, and {@link android.net.rtp.AudioStream}.</li>
+<li>{@link android.net.rtp.AudioCodec}, which holds a collection of codecs that
+you define for an {@link android.net.rtp.AudioStream}.</li>
+</ul>
+
+<p>To support audio conferencing and similar usages, an application instantiates
+two classes as endpoints for the stream:</p>
+
+<ul>
+<li>{@link android.net.rtp.AudioStream} specifies a remote endpoint and consists
+of network mapping and a configured {@link android.net.rtp.AudioCodec}.</li>
+<li>{@link android.net.rtp.AudioGroup} represents the local endpoint for one
+or more {@link android.net.rtp.AudioStream}s. The {@link android.net.rtp.AudioGroup} mixes
+all the {@link android.net.rtp.AudioStream}s and optionally interacts with the device
+speaker and the microphone at the same time.</li>
+</ul>
+
+<p>The simplest usage involves a single remote endpoint and local endpoint.
+For more complex usages, please refer to the limitations described for
+{@link android.net.rtp.AudioGroup}.</p>
+
+<p>To use the RTP API, applications must request permission from the user by
+declaring <code>&lt;uses-permission
+android:name="android.permission.INTERNET"&gt;</code>
+in their manifest files. To acquire the device microphone, the <code>&lt;uses-permission
+android:name="android.permission.RECORD_AUDIO"&gt;</code> permission is also required.</p>
+
+<h3 id="resizewidgets">Resizable app widgets</h3>
+
+<p>Starting in Android 3.1, developers can make their homescreen widgets
+resizeable &mdash; horizontally, vertically, or on both axes. Users touch-hold a
+widget to show its resize handles, then drag the horizontal and/or vertical
+handles to change the size on the layout grid. </p>
+
+<p>Developers can make any Home screen widget resizeable by defining a
+<code>resizeMode</code> attribute in the widget's {@link
+android.appwidget.AppWidgetProviderInfo} metadata. Values for the
+<code>resizeMode</code> attribute include "horizontal", "vertical", and "none".
+To declare a widget as resizeable horizontally and vertically, supply the value
+"horizontal|vertical".
+
+<p>Here's an example: </p>
+
+<pre>&lt;appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="294dp"
+    android:minHeight="72dp"
+    android:updatePeriodMillis="86400000"
+    android:previewImage="@drawable/preview"
+    android:initialLayout="@layout/example_appwidget"
+    android:configure="com.example.android.ExampleAppWidgetConfigure"
+    android:resizeMode="horizontal|vertical" >
+&lt;/appwidget-provider></pre>
+
+<p>For more information about Home screen widgets, see the <a
+href="{@docRoot}guide/topics/appwidgets/index.html">App Widgets</a>
+documentation.</p>
+
+<h3 id="animation" style="margin-top:1.25em;">Animation framework</h3>
+
+<ul>
+<li>New ViewPropertyAnimator class
+  <ul>
+    <li>A new {@link android.view.ViewPropertyAnimator} class provides a
+convenient
+way for developers to animate select properties on {@link android.view.View} objects. The class
+automaties and optimizes the animation of the properties and makes it easier to
+manage multiple simulataneous animations on a {@link android.view.View} object.
+<p>Using the {@link android.view.ViewPropertyAnimator} is straightforward. To animate properties for
+a {@link android.view.View}, call {@link android.view.View#animate()} to
+construct a {@link android.view.ViewPropertyAnimator} object for that {@link android.view.View}. Use the
+methods on the {@link android.view.ViewPropertyAnimator} to specify what property to
+animate and how to animate it. For example, to fade the {@link android.view.View} to transparent,
+call <code>alpha(0);</code>. The {@link android.view.ViewPropertyAnimator} object
+handles the details of configuring the underlying {@link
+android.animation.Animator} class and starting it, then rendering the
+animation.</p></li>
+  </ul>
+</li>
+<li>Animation background color
+  <ul>
+    <li>New {@link android.view.animation.Animation#getBackgroundColor()} and
+    {@link android.view.animation.Animation#setBackgroundColor(int)} methods let
+    you get/set the background color behind animations, for window animations
+only. Currently the background must be black, with any desired alpha level.</li>
+  </ul>
+</li>
+<li>Getting animated fraction from <code>ViewAnimator</code>
+  <ul>
+    <li>A new {@link android.animation.ValueAnimator#getAnimatedFraction()}
+method
+lets you get the current animation fraction &mdash; the elapsed/interpolated
+fraction used in the most recent frame update &mdash; from a {@link
+android.animation.ValueAnimator}.</li>
+  </ul>
+</li>
+</ul>
+
+<h3 "ui">UI framework</h3>
+<ul>
+<li>Forced rendering of a layer
+  <ul>
+    <li>A new {@link android.view.View#buildLayer()} method lets an application
+force a View's layer to be created and the View rendered into it immediately.
+For example, an application could use this method to render a View into its
+layer before starting an animation. If the View is complex, rendering it into
+the layer before starting the animation will avoid skipping frames.</li>
+  </ul>
+</li>
+<li>Camera distance
+  <ul>
+    <li>Applications can use a new method
+{@link android.view.View#setCameraDistance(float)} to set the distance from the
+camera
+to a View. This gives applications improved control over 3D transformations of
+the View, such as rotations. </li>
+  </ul>
+</li>
+<li>Getting a calendar view from a DatePicker
+  <ul>
+    <li>A new {@link android.widget.DatePicker#getCalendarView()} method
+    lets you get a {@link android.widget.CalendarView} from a {@link
+android.widget.DatePicker}
+    instance.</li>
+  </ul>
+</li>
+<li>Getting callbacks when views are detached
+  <ul>
+    <li>A new {@link android.view.View.OnAttachStateChangeListener} lets you
+receive
+callbacks when a View is attached or detached from its window. Use {@link
+android.view.View#addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener) addOnAttachStateChangeListener()}
+to add a listener and {@link
+android.view.View#removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener) addOnAttachStateChangeListener()} to remove it.</li>
+  </ul>
+</li>
+<li>Fragment breadcrumb listener, new onInflate() signature
+  <ul>
+    <li>A new method, {@link
+android.app.FragmentBreadCrumbs#setOnBreadCrumbClickListener(android.app.FragmentBreadCrumbs.OnBreadCrumbClickListener) setOnBreadCrumbClickListener()},
+provides a hook to let
+applications intercept fragment-breadcrumb clicks and take any action needed
+before going to the backstack entry or fragment that was clicked. </li>
+    <li>In the {@link android.app.Fragment} class, {@link
+android.app.Fragment#onInflate(android.util.AttributeSet, android.os.Bundle)
+onInflate(attrs, savedInstanceState)} is deprecated. Please use {@link
+android.app.Fragment#onInflate(android.app.Activity, android.util.AttributeSet,
+android.os.Bundle) onInflate(activity, attrs, savedInstanceState)} instead.</li>
+  </ul>
+</li>
+<li>Display search result in new tab
+  <ul>
+    <li>An {@link android.app.SearchManager#EXTRA_NEW_SEARCH} data key for {@link
+android.content.Intent#ACTION_WEB_SEARCH} intents lets you open a search in a
+new browser tab, rather than in an existing one.</li>
+  </ul>
+</li>
+
+<li>Drawable text cursor
+  <ul>
+<li>You can now specify a drawable to use as the text cursor using the new
+resource attribute {@link android.R.attr#textCursorDrawable}.</li>
+  </ul>
+</li>
+<li>Setting displayed child in remote views
+  <ul>
+    <li>A new convenience method, {@link
+android.widget.RemoteViews#setDisplayedChild(int, int) setDisplayedChild(viewId,
+childIndex)}, is available in {@link android.widget.RemoteViews} subclasses, to
+let you set the child displayed in {@link android.widget.ViewAnimator} and
+{@link android.widget.AdapterViewAnimator} subclasses such as {@link
+android.widget.AdapterViewFlipper}, {@link android.widget.StackView}, {@link
+android.widget.ViewFlipper}, and {@link android.widget.ViewSwitcher}.</li>
+  </ul>
+</li>
+<li>Generic keys for gamepads and other input devices
+  <ul>
+    <li>{@link android.view.KeyEvent} adds a range of generic keycodes to
+    accommodate gamepad buttons. The class also adds
+    {@link android.view.KeyEvent#isGamepadButton(int)} and several other
+    helper methods for working with keycodes.</li>
+  </ul>
+</li>
+</ul>
+
+<h3 id="graphics" style="margin-top:1.3em;">Graphics</h3>
+
+<ul>
+<li>Helpers for managing bitmaps
+  <ul>
+  <li>{@link android.graphics.Bitmap#setHasAlpha(boolean)} lets an app indicate that
+all of the pixels in a Bitmap are known to be opaque (false) or that some of the
+pixels may contain non-opaque alpha values (true). Note, for some configs (such
+as RGB_565) this call is ignored, since it does not support per-pixel alpha
+values. This is meant as a drawing hint, as in some cases a bitmap that is known
+to be opaque can take a faster drawing case than one that may have non-opaque
+per-pixel alpha values. </li>
+  <li>{@link android.graphics.Bitmap#getByteCount()} gets a Bitmap's size in
+bytes.</li>
+  <li>{@link android.graphics.Bitmap#getGenerationId()} lets an application find
+out whether a Bitmap has been modified, such as for caching.</li>
+  <li>{@link android.graphics.Bitmap#sameAs(android.graphics.Bitmap)} determines
+whether a given Bitmap differs from the current Bitmap, in dimension,
+configuration, or pixel data. </li>
+  </ul>
+</li>
+<li>Setting camera location and rotation
+  <ul>
+  <li>{@link android.graphics.Camera} adds two new methods {@link
+android.graphics.Camera#rotate(float, float, float) rotate()} and {@link
+android.graphics.Camera#setLocation(float, float, float) setLocation()} for
+control of the
+camera's location, for 3D transformations.</li>
+</ul>
+</li>
+</ul>
+
+<h3 id="network" style="margin-top:1.25em;">Network</h3>
+
+<ul>
+<li>High-performance Wi-Fi lock
+  <ul>
+    <li>A new high-performance Wi-Fi lock lets applications maintain
+high-performance Wi-Fi connections even when the device screen is off.
+Applications that stream music, video, or voice for long periods can acquire the
+high-performance Wi-Fi lock to ensure streaming performance even when the screen
+is off. Because it uses more power, applications should acquire the
+high-performance Wi-Fi when there is a need for a long-running active
+connection.
+<p>To create a high-performance lock, pass {@link
+android.net.wifi.WifiManager#WIFI_MODE_FULL_HIGH_PERF} as the lock mode in a
+call to {@link android.net.wifi.WifiManager#createWifiLock(int,
+java.lang.String) createWifiLock()}.</p></li>
+  </ul>
+</li>
+<li>More traffic stats
+  <ul>
+    <li>Applications can now access statistics about more types of network usage
+using new methods in {@link android.net.TrafficStats}. Applications can use the
+methods to get UDP stats, packet count, TCP transmit/receive payload bytes and
+segments for a given UID.</li>
+  </ul>
+</li>
+<li>SIP auth username
+  <ul>
+    <li>Applications can now get and set the SIP auth username for a profile
+using
+the new methods {@link android.net.sip.SipProfile#getAuthUserName()
+getAuthUserName()} and {@link
+android.net.sip.SipProfile.Builder#setAuthUserName(java.lang.String)
+setAuthUserName()}.</li>
+  </ul>
+</li>
+</ul>
+
+
+<h3 id="download" style="margin-top:1.25em;">Download Manager</h3>
+<ul>
+<li>Handling of completed downloads
+  <ul>
+    <li>Applications can now initiate downloads that notify users only on
+completion. To initiate this type of download, applications pass {@link
+android.app.DownloadManager.Request#VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION}
+in the {@link
+android.app.DownloadManager.Request#setNotificationVisibility(int)
+setNotificationVisibility()} method of
+the a request object.</li>
+    <li>A new method, {@link
+android.app.DownloadManager#addCompletedDownload(java.lang.String,
+java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean)
+addCompletedDownload()}, lets an application add a file to the
+downloads database, so that it can be managed by the Downloads application.</li>
+  </ul>
+</li>
+<li>Show downloads sorted by size
+  <ul>
+    <li>Applications can start the Downloads application in sort-by-size mode by
+adding the new extra {@link
+android.app.DownloadManager#INTENT_EXTRAS_SORT_BY_SIZE} to an {@link
+android.app.DownloadManager#ACTION_VIEW_DOWNLOADS} intent.</li>
+    </ul>
+</li>
+</ul>
+
+<h3 id="ime" style="margin-top:1.25em;">IME framework</h3>
+
+<ul>
+<li>Getting an input method's extra value key
+  <ul><li>The {@link android.view.inputmethod.InputMethodSubtype} adds the
+method
+{@link
+android.view.inputmethod.InputMethodSubtype#containsExtraValueKey(java.lang.String) containsExtraValueKey()} to check whether an ExtraValue string is stored
+for the subtype and
+the method {@link
+android.view.inputmethod.InputMethodSubtype#getExtraValueOf(java.lang.String)
+getExtraValueOf()} to extract a specific key value from the ExtraValue hashmap.
+</li>
+  </ul>
+</li>
+</ul>
+
+<h3 id="media" style="margin-top:1.25em;">Media</h3>
+
+<ul>
+<li>New streaming audio formats
+  <ul>
+    <li>The media framework adds built-in support for raw ADTS AAC content, for
+improved streaming audio, as well as support for FLAC audio, for highest quality
+(lossless) compressed audio content. See the <a
+href="{@docRoot}guide/appendix/media-formats.html">Supported Media Formats</a>
+document for more information.</p></li>
+  </ul>
+</li>
+</ul>
+
+<h3 id="launchcontrols" style="margin-top:1.25em;">Launch controls on stopped
+applications</h3>
+
+<p>Starting from Android 3.1, the system's package manager keeps track of
+applications that are in a stopped state and provides a means of controlling
+their launch from background processes and other applications.</p>
+
+<p>Note that an application's stopped state is not the same as an Activity's
+stopped state. The system manages those two stopped states separately.</p>
+
+<p>The platform defines two new intent flags that let a sender specify
+whether the Intent should be allowed to activate components in stopped
+application.</p>
+
+<ul>
+<li>{@link android.content.Intent#FLAG_INCLUDE_STOPPED_PACKAGES} &mdash;
+Include intent filters of stopped applications in the list of potential targets
+to resolve against. </li>
+<li>{@link android.content.Intent#FLAG_EXCLUDE_STOPPED_PACKAGES} &mdash;
+Exclude intent filters of stopped applications from the list of potential
+targets.</li>
+</ul>
+
+<p>When neither or both of these flags is defined in an intent, the default
+behavior is to include filters of stopped applications in the list of
+potential targets.</p>
+
+<p>Note that the system adds {@link
+android.content.Intent#FLAG_EXCLUDE_STOPPED_PACKAGES} <em>to all broadcast
+intents</em>. It does this to prevent broadcasts from background services from
+inadvertently or unnecessarily launching components of stoppped applications.
+A background service or application can override this behavior by adding the
+{@link android.content.Intent#FLAG_INCLUDE_STOPPED_PACKAGES} flag to broadcast
+intents that should be allowed to activate stopped applications.</p>
+
+<p>Applications are in a stopped state when they are first installed but are not
+yet launched and when they are manually stopped by the user (in Manage
+Applications).</p>
+
+<h3 id="installnotification">Notification of application first launch and upgrade</h3>
+
+<p>The platform adds improved notification of application first launch and
+upgrades through two new intent actions:</p>
+
+<ul>
+<li>{@link android.content.Intent#ACTION_PACKAGE_FIRST_LAUNCH} &mdash; Sent to
+the installer package of an application when that application is first launched
+(that is, the first time it is moved out of a stopped state). The data
+contains the name of the package. </li>
+
+<li>{@link android.content.Intent#ACTION_MY_PACKAGE_REPLACED} &mdash; Notifies
+an application that it was updated, with a new version was installed over
+an existing version.  This is only sent to the application that was replaced. It
+does not contain any additional data. To receive it, declare an intent filter
+for this action. You can use the intent to trigger code that helps get your
+application back in proper running shape after an upgrade.
+
+<p>This intent is sent directly to the application, but only if the application
+was upgraded while it was in started state (not in a stopped state).</p></li>
+
+</ul>
+
+<h3 if="other">Core utilities</h3>
+
+<ul>
+<li>LRU cache
+  <ul>
+    <li>A new {@link android.util.LruCache} class lets your applications benefit
+from efficient caching. Applications can use the class to reduce the time spent
+computing or downloading data from the network, while maintaining a sensible
+memory footprint for the cached data.{@link android.util.LruCache} is a cache
+that holds strong references to a limited number of values. Each time a value is
+accessed, it is moved to the head of a queue. When a value is added to a full
+cache, the value at the end of that queue is evicted and may become eligible for
+garbage collection.</li>
+  </ul>
+</li>
+<li>File descriptor as <code>int</code>
+  <ul>
+    <li>You can now get the native file descriptor int for a {@link
+android.os.ParcelFileDescriptor} using either of the new methods {@link
+android.os.ParcelFileDescriptor#getFd()} or {@link
+android.os.ParcelFileDescriptor#detachFd()}. </li>
+  </ul>
+</li>
+</ul>
+
+
+
+
+
+
+<h3 id="webkit" style="margin-top:1.25em;">WebKit</h3>
+
+<ul>
+
+<li>File scheme cookies
+  <ul>
+    <li>The {@link android.webkit.CookieManager} now supports cookies that use
+the
+<code>file:</code> URI scheme. You can use {@link
+android.webkit.CookieManager#setAcceptFileSchemeCookies(boolean)
+setAcceptFileSchemeCookies()} to
+enable/disable support for file scheme cookies, before constructing an instance
+of <code>WebView</code> or <code>CookieManager</code>. In a
+<code>CookieManager</code> instance, you can check whether file scheme cookies
+is enabled by calling {@link
+android.webkit.CookieManager#allowFileSchemeCookies()}.</li>
+  </ul>
+</li>
+<li>Notification of login request
+  <ul>
+  <li>To support the browser autologin features introduced in Android 3.0, the
+new
+method {@link
+android.webkit.WebViewClient#onReceivedLoginRequest(android.webkit.WebView,java.lang.String, java.lang.String, java.lang.String) onReceivedLoginRequest()}
+notifies the host
+application that an autologin request for the user was processed. </li>
+  </ul>
+</li>
+<li>Removed classes and interfaces
+  <ul>
+    <li>Several classes and interfaces were removed from the public API, after
+previously being in deprecated state. See the <a
+href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
+Differences Report</a> for more information.</p></li>
+  </ul>
+  </li>
+</ul>
+
+
+
+<h3 id="browser" style="margin-top:1.25em;">Browser</h3>
+
+<p>The Browser application adds the following features to support web
+applications:</p>
+
+<ul>
+<li>Support for inline playback of video embedded in HTML5
+<code>&lt;video&gt;</code> tag. Playback is hardware-accelerated where possible.
+</li>
+<li>Layer support for fixed position elements for all sites (mobile and
+desktop).</li>
+</ul>
+
+
+
+
+
+<h3 id="features">New feature constants</h3>
+
+<p>The platform adds new hardware feature constants that developers can declare
+in their application manifests, to inform external entities such as Android
+Market of the application's requirement for new hardware capabilities supported
+in this version of the platform. Developers declare these and other feature
+constants in <a
+href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code
+&lt;uses-feature&gt;}</a> manifest elements.
+
+<ul>
+  <li>{@link android.content.pm.PackageManager#FEATURE_USB_ACCESSORY
+android.hardware.usb.accessory} &mdash; The application uses the <a href="#usb">USB
+API</a> to communicate with external hardware devices connected over USB and
+function as hosts.</li>
+  <li>{@link android.content.pm.PackageManager#FEATURE_USB_HOST
+android.hardware.usb.host} &mdash; The application uses the <a href="#usb">USB API</a>
+to communicate with external hardware devices connected over USB and function as
+devices.</li>
+</ul>
+
+<p>Android Market filters applications based on features declared in <a
+href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code
+&lt;uses-feature&gt;}</a> manifest elements. For more information about
+declaring features in an application manifest, read <a
+href="{docRoot}guide/appendix/market-filters.html">Android Market
+Filters</a>.</p>
+
+
+
+<h3 id="api-diff">API Differences Report</h3>
+
+<p>For a detailed view of all API changes in Android {@sdkPlatformVersion} (API
+Level
+{@sdkPlatformApiLevel}), see the <a
+href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
+Differences Report</a>.</p>
+
+
+
+
+
+<h2 id="api-level">API Level</h2>
+
+<p>The Android {@sdkPlatformVersion} platform delivers an updated version of
+the framework API. The Android {@sdkPlatformVersion} API
+is assigned an integer identifier &mdash;
+<strong>{@sdkPlatformApiLevel}</strong> &mdash; that is
+stored in the system itself. This identifier, called the "API Level", allows the
+system to correctly determine whether an application is compatible with
+the system, prior to installing the application. </p>
+
+<p>To use APIs introduced in Android {@sdkPlatformVersion} in your application,
+you need compile the application against the Android library that is provided in
+the Android {@sdkPlatformVersion} SDK platform. Depending on your needs, you
+might
+also need to add an <code>android:minSdkVersion="{@sdkPlatformApiLevel}"</code>
+attribute to the <code>&lt;uses-sdk&gt;</code> element in the application's
+manifest.</p>
+
+<p>For more information about how to use API Level, see the <a
+href="{@docRoot}guide/appendix/api-levels.html">API Levels</a> document. </p>
+
+
+<h2 id="apps">Built-in Applications</h2>
+
+<p>The system image included in the downloadable platform provides these
+built-in applications:</p>
+
+<table style="border:0;padding-bottom:0;margin-bottom:0;">
+<tr>
+<td style="border:0;padding-bottom:0;margin-bottom:0;">
+<ul>
+<li>API Demos</li>
+<li>Browser</li>
+<li>Calculator</li>
+<li>Camera</li>
+<li>Clock</li>
+<li>Contacts</li>
+<li>Custom Locale</li>
+<li>Dev Tools</li>
+<li>Downloads</li>
+<li>Email</li>
+</ul>
+</td>
+<td style="border:0;padding-bottom:0;margin-bottom:0;padding-left:5em;">
+<ul>
+<li>Gallery</li>
+<li>Gestures Builder</li>
+<li>Messaging</li>
+<li>Music</li>
+<li>Search</li>
+<li>Settings</li>
+<li>Spare Parts</li>
+<li>Speech Recorder</li>
+<li>Widget Preview</li>
+</ul>
+</td>
+</tr>
+</table>
+
+
+<h2 id="locs" style="margin-top:.75em;">Locales</h2>
+
+<p>The system image included in the downloadable SDK platform provides a variety
+of
+built-in locales. In some cases, region-specific strings are available for the
+locales. In other cases, a default version of the language is used. The
+languages that are available in the Android 3.0 system
+image are listed below (with <em>language</em>_<em>country/region</em> locale
+descriptor).</p>
+
+<table style="border:0;padding-bottom:0;margin-bottom:0;">
+<tr>
+<td style="border:0;padding-bottom:0;margin-bottom:0;">
+<ul>
+<li>Arabic, Egypt (ar_EG)</li>
+<li>Arabic, Israel (ar_IL)</li>
+<li>Bulgarian, Bulgaria (bg_BG)</li>
+<li>Catalan, Spain (ca_ES)</li>
+<li>Czech, Czech Republic (cs_CZ)</li>
+<li>Danish, Denmark(da_DK)</li>
+<li>German, Austria (de_AT)</li>
+<li>German, Switzerland (de_CH)</li>
+<li>German, Germany (de_DE)</li>
+<li>German, Liechtenstein (de_LI)</li>
+<li>Greek, Greece (el_GR)</li>
+<li>English, Australia (en_AU)</li>
+<li>English, Canada (en_CA)</li>
+<li>English, Britain (en_GB)</li>
+<li>English, Ireland (en_IE)</li>
+<li>English, India (en_IN)</li>
+<li>English, New Zealand (en_NZ)</li>
+<li>English, Singapore(en_SG)</li>
+<li>English, US (en_US)</li>
+<li>English, Zimbabwe (en_ZA)</li>
+<li>Spanish (es_ES)</li>
+<li>Spanish, US (es_US)</li>
+<li>Finnish, Finland (fi_FI)</li>
+<li>French, Belgium (fr_BE)</li>
+<li>French, Canada (fr_CA)</li>
+<li>French, Switzerland (fr_CH)</li>
+<li>French, France (fr_FR)</li>
+<li>Hebrew, Israel (he_IL)</li>
+<li>Hindi, India (hi_IN)</li>
+</ul>
+</td>
+<td style="border:0;padding-bottom:0;margin-bottom:0;padding-left:5em;">
+<li>Croatian, Croatia (hr_HR)</li>
+<li>Hungarian, Hungary (hu_HU)</li>
+<li>Indonesian, Indonesia (id_ID)</li>
+<li>Italian, Switzerland (it_CH)</li>
+<li>Italian, Italy (it_IT)</li>
+<li>Japanese (ja_JP)</li>
+<li>Korean (ko_KR)</li>
+<li>Lithuanian, Lithuania (lt_LT)</li>
+<li>Latvian, Latvia (lv_LV)</li>
+<li>Norwegian bokmål, Norway (nb_NO)</li>
+<li>Dutch, Belgium (nl_BE)</li>
+<li>Dutch, Netherlands (nl_NL)</li>
+<li>Polish (pl_PL)</li>
+<li>Portuguese, Brazil (pt_BR)</li>
+<li>Portuguese, Portugal (pt_PT)</li>
+<li>Romanian, Romania (ro_RO)</li>
+<li>Russian (ru_RU)</li></li>
+<li>Slovak, Slovakia (sk_SK)</li>
+<li>Slovenian, Slovenia (sl_SI)</li>
+<li>Serbian (sr_RS)</li>
+<li>Swedish, Sweden (sv_SE)</li>
+<li>Thai, Thailand (th_TH)</li>
+<li>Tagalog, Philippines (tl_PH)</li>
+<li>Turkish, Turkey (tr_TR)</li>
+<li>Ukrainian, Ukraine (uk_UA)</li>
+<li>Vietnamese, Vietnam (vi_VN)</li>
+<li>Chinese, PRC (zh_CN)</li>
+<li>Chinese, Taiwan (zh_TW)</li>
+</td>
+</tr>
+</table>
+
+<p class="note"><strong>Note:</strong> The Android platform may support more
+locales than are included in the SDK system image. All of the supported locales
+are available in the <a href="http://source.android.com/">Android Open Source
+Project</a>.</p>
+
+<h2 id="skins">Emulator Skins</h2>
+
+<p>The downloadable platform includes the following emulator skin:</p>
+
+<ul>
+  <li>
+    WXGA (1280x800, medium density, xlarge screen)
+  </li>
+</ul>
+
+<p>For more information about how to develop an application that displays
+and functions properly on all Android-powered devices, see <a
+href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple
+Screens</a>.</p>
\ No newline at end of file
diff --git a/docs/html/sdk/images/3.1/controls.png b/docs/html/sdk/images/3.1/controls.png
new file mode 100644
index 0000000..e0ca1f8
--- /dev/null
+++ b/docs/html/sdk/images/3.1/controls.png
Binary files differ
diff --git a/docs/html/sdk/images/3.1/home.png b/docs/html/sdk/images/3.1/home.png
new file mode 100644
index 0000000..ea0a75a
--- /dev/null
+++ b/docs/html/sdk/images/3.1/home.png
Binary files differ
diff --git a/docs/html/sdk/images/3.1/home_full.png b/docs/html/sdk/images/3.1/home_full.png
new file mode 100644
index 0000000..2b8e85e
--- /dev/null
+++ b/docs/html/sdk/images/3.1/home_full.png
Binary files differ
diff --git a/docs/html/sdk/images/3.1/resizeable.png b/docs/html/sdk/images/3.1/resizeable.png
new file mode 100644
index 0000000..c9f5e8e
--- /dev/null
+++ b/docs/html/sdk/images/3.1/resizeable.png
Binary files differ
diff --git a/docs/html/sdk/images/3.1/tasks.png b/docs/html/sdk/images/3.1/tasks.png
new file mode 100644
index 0000000..89d69e5
--- /dev/null
+++ b/docs/html/sdk/images/3.1/tasks.png
Binary files differ
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 7f6f0105..7c1c86d 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -1,21 +1,21 @@
 page.title=Android SDK
 sdk.redirect=0
 
-sdk.win_installer=installer_r10-windows.exe
-sdk.win_installer_bytes=32878481
-sdk.win_installer_checksum=8ffa2dd734829d0bbd3ea601b50b36c7
+sdk.win_installer=installer_r11-windows.exe
+sdk.win_installer_bytes=32883649
+sdk.win_installer_checksum=3dc8a29ae5afed97b40910ef153caa2b
 
-sdk.win_download=android-sdk_r10-windows.zip
-sdk.win_bytes=32832260
-sdk.win_checksum=1e42b8f528d9ca6d9b887c58c6f1b9a2
+sdk.win_download=android-sdk_r11-windows.zip
+sdk.win_bytes=32837554
+sdk.win_checksum=0a2c52b8f8d97a4871ce8b3eb38e3072
 
-sdk.mac_download=android-sdk_r10-mac_x86.zip
-sdk.mac_bytes=28847132
-sdk.mac_checksum=e3aa5578a6553b69cc36659c9505be3f
+sdk.mac_download=android-sdk_r11-mac_x86.zip
+sdk.mac_bytes=28844968
+sdk.mac_checksum=85bed5ed25aea51f6a447a674d637d1e
 
-sdk.linux_download=android-sdk_r10-linux_x86.tgz
-sdk.linux_bytes=26981997
-sdk.linux_checksum=c022dda3a56c8a67698e6a39b0b1a4e0
+sdk.linux_download=android-sdk_r11-linux_x86.tgz
+sdk.linux_bytes=26984929
+sdk.linux_checksum=026c67f82627a3a70efb197ca3360d0a
 
 @jd:body
 
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 3240b57..11f29b1 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -77,37 +77,59 @@
     </ul>
     <ul>
       <li class="toggle-list">
+        <div><a href="<?cs var:toroot ?>sdk/android-3.1.html">
+        <span class="en">Android 3.1 Platform</span></a> <span class="new">new!</span></div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>sdk/android-3.1-highlights.html">Platform Highlights</a></li> 
+          <li><a href="<?cs var:toroot ?>sdk/api_diff/12/changes.html">API Differences Report &raquo;</a></li>
+        </ul>
+      </li>
+      <li class="toggle-list">
         <div><a href="<?cs var:toroot ?>sdk/android-3.0.html">
-        <span class="en">Android 3.0 Platform</span></a> <span class="new">new!</span></div>
+        <span class="en">Android 3.0 Platform</span></a></div>
         <ul>
           <li><a href="<?cs var:toroot ?>sdk/android-3.0-highlights.html">Platform Highlights</a></li> 
           <li><a href="<?cs var:toroot ?>sdk/api_diff/11/changes.html">API Differences Report &raquo;</a></li>
         </ul>
       </li>
+      <li><a href="<?cs var:toroot ?>sdk/android-2.3.4.html">Android 2.3.4 Platform</span></a> <span class="new">new!</span></li>
       <li class="toggle-list">
       <div><a href="<?cs var:toroot ?>sdk/android-2.3.3.html">
-      <span class="en">Android 2.3.3 Platform</span></a> <span class="new">new!</span></div>
+      <span class="en">Android 2.3.3 Platform</span></a></div>
         <ul>
           <li><a href="<?cs var:toroot ?>sdk/api_diff/10/changes.html">API Differences Report &raquo;</a></li> 
         </ul>
       </li>
       <li class="toggle-list">
-      <div><a href="<?cs var:toroot ?>sdk/android-2.3.html">
-      <span class="en">Android 2.3 Platform</span></a></div>
+        <div><a href="<?cs var:toroot ?>sdk/android-2.2.html">
+        <span class="en">Android 2.2 Platform</span></a></div>
         <ul>
-          <li><a href="<?cs var:toroot ?>sdk/android-2.3-highlights.html">Platform Highlights</a></li> 
-          <li><a href="<?cs var:toroot ?>sdk/api_diff/9/changes.html">API Differences Report &raquo;</a></li> 
+          <li><a href="<?cs var:toroot ?>sdk/android-2.2-highlights.html">Platform Highlights</a></li> 
+          <li><a href="<?cs var:toroot ?>sdk/api_diff/8/changes.html">API Differences Report &raquo;</a></li>
         </ul>
       </li>
-      <li><a href="<?cs var:toroot ?>sdk/android-2.2.html">Android 2.2 Platform</a></li>
-      <li><a href="<?cs var:toroot ?>sdk/android-2.1.html">Android 2.1 Platform</a></li>
-      <li><a href="<?cs var:toroot ?>sdk/android-1.6.html">Android 1.6 Platform</a></li>
-      <li><a href="<?cs var:toroot ?>sdk/android-1.5.html">Android 1.5 Platform</a></li>
       <li class="toggle-list">
-        <div><a href="#" onclick="toggle(this.parentNode.parentNode,true); return false;">Older Platforms</a></div>
+        <div><a href="<?cs var:toroot ?>sdk/android-2.1.html">
+        <span class="en">Android 2.1 Platform</span></a></div>
         <ul>
+          <li><a href="<?cs var:toroot ?>sdk/api_diff/7/changes.html">API Differences Report &raquo;</a></li>
+        </ul>
+      </li>
+      <li class="toggle-list">
+        <div><a href="#" onclick="toggle(this.parentNode.parentNode,true); return false;">Other Platforms</a></div>
+        <ul>
+          <li class="toggle-list">
+            <div><a href="<?cs var:toroot ?>sdk/android-2.3.html">
+            <span class="en">Android 2.3 Platform</span></a></div>
+              <ul>
+                <li><a href="<?cs var:toroot ?>sdk/android-2.3-highlights.html">Platform Highlights</a></li> 
+                <li><a href="<?cs var:toroot ?>sdk/api_diff/9/changes.html">API Differences Report &raquo;</a></li> 
+              </ul>
+          </li>
           <li><a href="<?cs var:toroot ?>sdk/android-2.0.1.html">Android 2.0.1 Platform</a></li>
           <li><a href="<?cs var:toroot ?>sdk/android-2.0.html">Android 2.0 Platform</a></li>
+          <li><a href="<?cs var:toroot ?>sdk/android-1.6.html">Android 1.6 Platform</a></li>
+          <li><a href="<?cs var:toroot ?>sdk/android-1.5.html">Android 1.5 Platform</a></li>
           <li><a href="<?cs var:toroot ?>sdk/android-1.1.html">Android 1.1 Platform</a></li>
         </ul>
       </li>
diff --git a/docs/html/sdk/tools-notes.jd b/docs/html/sdk/tools-notes.jd
index 354fec9..64c8f2a 100644
--- a/docs/html/sdk/tools-notes.jd
+++ b/docs/html/sdk/tools-notes.jd
@@ -65,7 +65,7 @@
 <div class="toggleable opened">
   <a href="#" onclick="return toggleDiv(this)">
         <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
-SDK Tools, Revision 10</a> <em>(February 2011)</em>
+SDK Tools, Revision 11</a> <em>(May 2011)</em>
   <div class="toggleme">
   <dl>
 <dt>Dependencies:</dt>
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 445f4b5..2eaedaa 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -320,8 +320,7 @@
     }
     jint id = (jint)rsElementCreate2(con,
                                      (RsElement *)ids, fieldCount,
-                                     nameArray, fieldCount,
-                                     sizeArray, fieldCount,
+                                     nameArray, fieldCount * sizeof(size_t),  sizeArray,
                                      (const uint32_t *)arraySizes, fieldCount);
     for (int ct=0; ct < fieldCount; ct++) {
         jstring s = (jstring)_env->GetObjectArrayElement(_names, ct);
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/media/mediarecorder.h b/include/media/mediarecorder.h
index 18a3c6a..59093c9 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -159,6 +159,26 @@
     MEDIA_RECORDER_TRACK_INFO_LIST_START           = 1000,
     MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS    = 1000,
     MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME     = 1001,
+    MEDIA_RECORDER_TRACK_INFO_TYPE                 = 1002,
+    MEDIA_RECORDER_TRACK_INFO_DURATION_MS          = 1003,
+
+    // The time to measure the max chunk duration
+    MEDIA_RECORDER_TRACK_INFO_MAX_CHUNK_DUR_MS     = 1004,
+
+    MEDIA_RECORDER_TRACK_INFO_ENCODED_FRAMES       = 1005,
+
+    // The time to measure how well the audio and video
+    // track data is interleaved.
+    MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS       = 1006,
+
+    // The time to measure system response. Note that
+    // the delay does not include the intentional delay
+    // we use to eliminate the recording sound.
+    MEDIA_RECORDER_TRACK_INFO_INITIAL_DELAY_MS     = 1007,
+
+    // The time used to compensate for initial A/V sync.
+    MEDIA_RECORDER_TRACK_INFO_START_OFFSET_MS      = 1008,
+
     MEDIA_RECORDER_TRACK_INFO_LIST_END             = 2000,
 };
 
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 15f86ea..4b5674e 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -108,6 +108,13 @@
     struct ChunkInfo {
         Track               *mTrack;        // Owner
         List<Chunk>         mChunks;        // Remaining chunks to be written
+
+        // Previous chunk timestamp that has been written
+        int64_t mPrevChunkTimestampUs;
+
+        // Max time interval between neighboring chunks
+        int64_t mMaxInterChunkDurUs;
+
     };
 
     bool            mIsFirstChunk;
@@ -159,6 +166,10 @@
     bool isFileStreamable() const;
     void trackProgressStatus(size_t trackId, int64_t timeUs, status_t err = OK);
     void writeCompositionMatrix(int32_t degrees);
+    void writeMvhdBox(int64_t durationUs);
+    void writeMoovBox(int64_t durationUs);
+    void writeFtypBox(const MetaData *param);
+    void sendSessionSummary();
 
     MPEG4Writer(const MPEG4Writer &);
     MPEG4Writer &operator=(const MPEG4Writer &);
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 4044c5d..a300f0e 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -154,16 +154,16 @@
             int32_t left, int32_t top,
             int32_t right, int32_t bottom);
 
-    bool findCString(uint32_t key, const char **value);
-    bool findInt32(uint32_t key, int32_t *value);
-    bool findInt64(uint32_t key, int64_t *value);
-    bool findFloat(uint32_t key, float *value);
-    bool findPointer(uint32_t key, void **value);
+    bool findCString(uint32_t key, const char **value) const;
+    bool findInt32(uint32_t key, int32_t *value) const;
+    bool findInt64(uint32_t key, int64_t *value) const;
+    bool findFloat(uint32_t key, float *value) const;
+    bool findPointer(uint32_t key, void **value) const;
 
     bool findRect(
             uint32_t key,
             int32_t *left, int32_t *top,
-            int32_t *right, int32_t *bottom);
+            int32_t *right, int32_t *bottom) const;
 
     bool setData(uint32_t key, uint32_t type, const void *data, size_t size);
 
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 93b5d24..70daafa 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -214,6 +214,7 @@
 
     void setAMRFormat(bool isWAMR, int32_t bitRate);
     void setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bitRate);
+    void setG711Format(int32_t numChannels);
 
     status_t setVideoPortFormatType(
             OMX_U32 portIndex,
diff --git a/include/media/stagefright/openmax/OMX_Video.h b/include/media/stagefright/openmax/OMX_Video.h
index 2738bdc..4f8485d 100644
--- a/include/media/stagefright/openmax/OMX_Video.h
+++ b/include/media/stagefright/openmax/OMX_Video.h
@@ -85,6 +85,7 @@
     OMX_VIDEO_CodingRV,         /**< all versions of Real Video */
     OMX_VIDEO_CodingAVC,        /**< H.264/AVC */
     OMX_VIDEO_CodingMJPEG,      /**< Motion JPEG */
+    OMX_VIDEO_CodingVPX,        /**< Google VPX, formerly known as On2 VP8 */
     OMX_VIDEO_CodingKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ 
     OMX_VIDEO_CodingVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */
     OMX_VIDEO_CodingMax = 0x7FFFFFFF
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 9b92c73..c7ebf56 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -216,6 +216,8 @@
     status_t setAxisValue(int32_t axis, float value);
     float* editAxisValue(int32_t axis);
 
+    void scale(float scale);
+
 #ifdef HAVE_ANDROID_OS
     status_t readFromParcel(Parcel* parcel);
     status_t writeToParcel(Parcel* parcel) const;
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/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index f6a21d4..80a7bed 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1144,7 +1144,7 @@
  * are set up for each individual segment.
  */
 void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords,
-        GLvoid* lengthCoords, float strokeWidth) {
+        GLvoid* lengthCoords, float boundaryWidthProportion) {
     mCaches.unbindMeshBuffer();
     glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
             gAAVertexStride, vertices);
@@ -1155,11 +1155,10 @@
     glEnableVertexAttribArray(lengthSlot);
     glVertexAttribPointer(lengthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, lengthCoords);
     int boundaryWidthSlot = mCaches.currentProgram->getUniform("boundaryWidth");
+    glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
     // Setting the inverse value saves computations per-fragment in the shader
     int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth");
-    float boundaryWidth = (1 - strokeWidth) / 2;
-    glUniform1f(boundaryWidthSlot, boundaryWidth);
-    glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidth));
+    glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion));
 }
 
 void OpenGLRenderer::finishDrawTexture() {
@@ -1458,21 +1457,66 @@
     }
 }
 
+/**
+ * We draw lines as quads (tristrips). Using GL_LINES can be difficult because the rasterization
+ * rules for those lines produces some unexpected results, and may vary between hardware devices.
+ * The basics of lines-as-quads is easy; we simply find the normal to the line and position the
+ * corners of the quads on either side of each line endpoint, separated by the strokeWidth
+ * of the line. Hairlines are more involved because we need to account for transform scaling
+ * to end up with a one-pixel-wide line in screen space..
+ * Anti-aliased lines add another factor to the approach. We use a specialized fragment shader
+ * in combination with values that we calculate and pass down in this method. The basic approach
+ * is that the quad we create contains both the core line area plus a bounding area in which
+ * the translucent/AA pixels are drawn. The values we calculate tell the shader what
+ * proportion of the width and the length of a given segment is represented by the boundary
+ * region. The quad ends up being exactly .5 pixel larger in all directions than the non-AA quad.
+ * The bounding region is actually 1 pixel wide on all sides (half pixel on the outside, half pixel
+ * on the inside). This ends up giving the result we want, with pixels that are completely
+ * 'inside' the line area being filled opaquely and the other pixels being filled according to
+ * how far into the boundary region they are, which is determined by shader interpolation.
+ */
 void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
     if (mSnapshot->isIgnored()) return;
 
     const bool isAA = paint->isAntiAlias();
-    float strokeWidth = paint->getStrokeWidth() * 0.5f;
+    // We use half the stroke width here because we're going to position the quad
+    // corner vertices half of the width away from the line endpoints
+    float halfStrokeWidth = paint->getStrokeWidth() * 0.5f;
     // A stroke width of 0 has a special meaning in Skia:
     // it draws a line 1 px wide regardless of current transform
     bool isHairLine = paint->getStrokeWidth() == 0.0f;
+    float inverseScaleX = 1.0f;
+    float inverseScaleY = 1.0f;
+    bool scaled = false;
     int alpha;
     SkXfermode::Mode mode;
     int generatedVerticesCount = 0;
     int verticesCount = count;
     if (count > 4) {
-        // Polyline: account for extra vertices needed for continous tri-strip
-        verticesCount += (count -4);
+        // Polyline: account for extra vertices needed for continuous tri-strip
+        verticesCount += (count - 4);
+    }
+
+    if (isHairLine || isAA) {
+        // The quad that we use for AA and hairlines needs to account for scaling. For hairlines
+        // the line on the screen should always be one pixel wide regardless of scale. For
+        // AA lines, we only want one pixel of translucent boundary around the quad.
+        if (!mSnapshot->transform->isPureTranslate()) {
+            Matrix4 *mat = mSnapshot->transform;
+            float m00 = mat->data[Matrix4::kScaleX];
+            float m01 = mat->data[Matrix4::kSkewY];
+            float m02 = mat->data[2];
+            float m10 = mat->data[Matrix4::kSkewX];
+            float m11 = mat->data[Matrix4::kScaleX];
+            float m12 = mat->data[6];
+            float scaleX = sqrt(m00*m00 + m01*m01);
+            float scaleY = sqrt(m10*m10 + m11*m11);
+            inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
+            inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
+            if (inverseScaleX != 1.0f || inverseScaleY != 1.0f) {
+                scaled = true;
+            }
+        }
     }
 
     getAlphaAndMode(paint, &alpha, &mode);
@@ -1496,11 +1540,10 @@
 
     if (isHairLine) {
         // Set a real stroke width to be used in quad construction
-        strokeWidth = .5;
-    }
-    if (isAA) {
+        halfStrokeWidth = isAA? 1 : .5;
+    } else if (isAA && !scaled) {
         // Expand boundary to enable AA calculations on the quad border
-        strokeWidth += .5f;
+        halfStrokeWidth += .5f;
     }
     Vertex lines[verticesCount];
     Vertex* vertices = &lines[0];
@@ -1511,46 +1554,32 @@
     } else {
         void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset;
         void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset;
-        // innerProportion is the ratio of the inner (non-AA) port of the line to the total
+        // innerProportion is the ratio of the inner (non-AA) part of the line to the total
         // AA stroke width (the base stroke width expanded by a half pixel on either side).
         // This value is used in the fragment shader to determine how to fill fragments.
-        float innerProportion = fmax(strokeWidth - 1.0f, 0) / (strokeWidth + .5f);
-        setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, innerProportion);
+        // We will need to calculate the actual width proportion on each segment for
+        // scaled non-hairlines, since the boundary proportion may differ per-axis when scaled.
+        float boundaryWidthProportion = 1 / (2 * halfStrokeWidth);
+        setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, boundaryWidthProportion);
     }
 
-    AAVertex *prevAAVertex = NULL;
-    Vertex *prevVertex = NULL;
-    float inverseScaleX = 1.0f;
-    float inverseScaleY = 1.0f;
-
-    if (isHairLine) {
-        // The quad that we use for AA hairlines needs to account for scaling because the line
-        // should always be one pixel wide regardless of scale.
-        if (!mSnapshot->transform->isPureTranslate()) {
-            Matrix4 *mat = mSnapshot->transform;
-            float m00 = mat->data[Matrix4::kScaleX];
-            float m01 = mat->data[Matrix4::kSkewY];
-            float m02 = mat->data[2];
-            float m10 = mat->data[Matrix4::kSkewX];
-            float m11 = mat->data[Matrix4::kScaleX];
-            float m12 = mat->data[6];
-            float scaleX = sqrt(m00*m00 + m01*m01);
-            float scaleY = sqrt(m10*m10 + m11*m11);
-            inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
-            inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
-        }
-    }
+    AAVertex* prevAAVertex = NULL;
+    Vertex* prevVertex = NULL;
 
     int boundaryLengthSlot = -1;
     int inverseBoundaryLengthSlot = -1;
+    int boundaryWidthSlot = -1;
+    int inverseBoundaryWidthSlot = -1;
     for (int i = 0; i < count; i += 4) {
         // a = start point, b = end point
         vec2 a(points[i], points[i + 1]);
         vec2 b(points[i + 2], points[i + 3]);
         float length = 0;
+        float boundaryLengthProportion = 0;
+        float boundaryWidthProportion = 0;
 
         // Find the normal to the line
-        vec2 n = (b - a).copyNormalized() * strokeWidth;
+        vec2 n = (b - a).copyNormalized() * halfStrokeWidth;
         if (isHairLine) {
             if (isAA) {
                 float wideningFactor;
@@ -1561,8 +1590,20 @@
                 }
                 n *= wideningFactor;
             }
-            n.x *= inverseScaleX;
-            n.y *= inverseScaleY;
+            if (scaled) {
+                n.x *= inverseScaleX;
+                n.y *= inverseScaleY;
+            }
+        } else if (scaled) {
+            // Extend n by .5 pixel on each side, post-transform
+            vec2 extendedN = n.copyNormalized();
+            extendedN /= 2;
+            extendedN.x *= inverseScaleX;
+            extendedN.y *= inverseScaleY;
+            float extendedNLength = extendedN.length();
+            // We need to set this value on the shader prior to drawing
+            boundaryWidthProportion = extendedNLength / (halfStrokeWidth + extendedNLength);
+            n += extendedN;
         }
         float x = n.x;
         n.x = -n.y;
@@ -1573,6 +1614,15 @@
             vec2 abVector = (b - a);
             length = abVector.length();
             abVector.normalize();
+            if (scaled) {
+                abVector.x *= inverseScaleX;
+                abVector.y *= inverseScaleY;
+                float abLength = abVector.length();
+                boundaryLengthProportion = abLength / (length + abLength);
+            } else {
+                boundaryLengthProportion = .5 / (length + 1);
+            }
+            abVector /= 2;
             a -= abVector;
             b += abVector;
         }
@@ -1607,15 +1657,25 @@
                 prevVertex = vertices - 1;
                 generatedVerticesCount += 4;
             } else {
+                if (!isHairLine && scaled) {
+                    // Must set width proportions per-segment for scaled non-hairlines to use the
+                    // correct AA boundary dimensions
+                    if (boundaryWidthSlot < 0) {
+                        boundaryWidthSlot =
+                                mCaches.currentProgram->getUniform("boundaryWidth");
+                        inverseBoundaryWidthSlot =
+                                mCaches.currentProgram->getUniform("inverseBoundaryWidth");
+                    }
+                    glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
+                    glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion));
+                }
                 if (boundaryLengthSlot < 0) {
                     boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
                     inverseBoundaryLengthSlot =
                             mCaches.currentProgram->getUniform("inverseBoundaryLength");
                 }
-                float innerProportion = (length) / (length + 2);
-                float boundaryLength = (1 - innerProportion) / 2;
-                glUniform1f(boundaryLengthSlot, boundaryLength);
-                glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryLength));
+                glUniform1f(boundaryLengthSlot, boundaryLengthProportion);
+                glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryLengthProportion));
 
                 if (prevAAVertex != NULL) {
                     // Issue two repeat vertices to create degenerate triangles to bridge
diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk
index c336c0d..ef00b30 100644
--- a/libs/rs/Android.mk
+++ b/libs/rs/Android.mk
@@ -91,6 +91,7 @@
 	rsDevice.cpp \
 	rsElement.cpp \
 	rsFBOCache.cpp \
+	rsFifoSocket.cpp \
 	rsFileA3D.cpp \
 	rsFont.cpp \
 	rsLocklessFifo.cpp \
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 5370e25..00e3a0a 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -176,7 +176,6 @@
 ElementCreate2 {
 	param const RsElement * elements
 	param const char ** names
-	param const size_t * nameLengths
 	param const uint32_t * arraySize
 	ret RsElement
 	}
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index d5d5ca5..2b58e9e 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -347,13 +347,15 @@
     return (RsElement)e;
 }
 
+
 RsElement rsi_ElementCreate2(Context *rsc,
                              const RsElement * ein,
                              size_t ein_length,
+
                              const char ** names,
-                             size_t names_length,
-                             const size_t * nameLengths,
                              size_t nameLengths_length,
+                             const size_t * nameLengths,
+
                              const uint32_t * arraySizes,
                              size_t arraySizes_length) {
     const Element *e = Element::create(rsc, ein_length, (const Element **)ein, names, nameLengths, arraySizes);
diff --git a/libs/rs/rsFifo.cpp b/libs/rs/rsFifo.cpp
new file mode 100644
index 0000000..3d5d8c4
--- /dev/null
+++ b/libs/rs/rsFifo.cpp
@@ -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.
+ */
+
+#include "rsFifoSocket.h"
+#include "utils/Timers.h"
+#include "utils/StopWatch.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+Fifo::Fifo() {
+
+}
+
+Fifo::~Fifo() {
+
+}
+
diff --git a/libs/rs/rsFifo.h b/libs/rs/rsFifo.h
new file mode 100644
index 0000000..f924b95
--- /dev/null
+++ b/libs/rs/rsFifo.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_FIFO_H
+#define ANDROID_RS_FIFO_H
+
+
+#include "rsUtils.h"
+
+namespace android {
+namespace renderscript {
+
+
+// A simple FIFO to be used as a producer / consumer between two
+// threads.  One is writer and one is reader.  The common cases
+// will not require locking.  It is not threadsafe for multiple
+// readers or writers by design.
+
+class Fifo {
+protected:
+    Fifo();
+    virtual ~Fifo();
+
+public:
+    void virtual writeAsync(const void *data, size_t bytes) = 0;
+    void virtual writeWaitReturn(void *ret, size_t retSize) = 0;
+    size_t virtual read(void *data, size_t bytes) = 0;
+    void virtual readReturn(const void *data, size_t bytes) = 0;
+
+    void virtual flush() = 0;
+
+};
+
+}
+}
+#endif
diff --git a/libs/rs/rsFifoSocket.cpp b/libs/rs/rsFifoSocket.cpp
new file mode 100644
index 0000000..1ce57b9
--- /dev/null
+++ b/libs/rs/rsFifoSocket.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "rsFifoSocket.h"
+#include "utils/Timers.h"
+#include "utils/StopWatch.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+FifoSocket::FifoSocket() {
+    sequence = 1;
+}
+
+FifoSocket::~FifoSocket() {
+
+}
+
+bool FifoSocket::init() {
+    int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+    return false;
+}
+
+void FifoSocket::shutdown() {
+}
+
+void FifoSocket::writeAsync(const void *data, size_t bytes) {
+    size_t ret = ::write(sv[0], data, bytes);
+    rsAssert(ret == bytes);
+}
+
+void FifoSocket::writeWaitReturn(void *retData, size_t retBytes) {
+    size_t ret = ::read(sv[1], retData, retBytes);
+    rsAssert(ret == retBytes);
+}
+
+size_t FifoSocket::read(void *data, size_t bytes) {
+    size_t ret = ::read(sv[0], data, bytes);
+    rsAssert(ret == bytes);
+    return ret;
+}
+
+void FifoSocket::readReturn(const void *data, size_t bytes) {
+    size_t ret = ::write(sv[1], data, bytes);
+    rsAssert(ret == bytes);
+}
+
+
+void FifoSocket::flush() {
+}
+
+
diff --git a/libs/rs/rsFifoSocket.h b/libs/rs/rsFifoSocket.h
new file mode 100644
index 0000000..7df2b67
--- /dev/null
+++ b/libs/rs/rsFifoSocket.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_FIFO_SOCKET_H
+#define ANDROID_RS_FIFO_SOCKET_H
+
+
+#include "rsFifo.h"
+
+namespace android {
+namespace renderscript {
+
+
+class FifoSocket {
+public:
+    FifoSocket();
+    virtual ~FifoSocket();
+
+    bool init();
+    void shutdown();
+
+
+
+    void virtual writeAsync(const void *data, size_t bytes);
+    void virtual writeWaitReturn(void *ret, size_t retSize);
+    size_t virtual read(void *data, size_t bytes);
+    void virtual readReturn(const void *data, size_t bytes);
+
+    void virtual flush();
+
+protected:
+    int sv[2];
+    uint32_t sequence;
+
+
+};
+
+}
+}
+
+#endif
diff --git a/libs/rs/rsg_generator.c b/libs/rs/rsg_generator.c
index 1d8b9b5..d550712 100644
--- a/libs/rs/rsg_generator.c
+++ b/libs/rs/rsg_generator.c
@@ -189,6 +189,7 @@
     fprintf(f, "#include \"rsThreadIO.h\"\n");
     //fprintf(f, "#include \"rsgApiStructs.h\"\n");
     fprintf(f, "#include \"rsgApiFuncDecl.h\"\n");
+    fprintf(f, "#include \"rsFifo.h\"\n");
     fprintf(f, "\n");
     fprintf(f, "using namespace android;\n");
     fprintf(f, "using namespace android::renderscript;\n");
@@ -290,11 +291,103 @@
 
             if (api->ret.typeName[0]) {
                 fprintf(f, "    return reinterpret_cast<");
-                printVarTypeAndName(f, &api->ret);
+                printVarType(f, &api->ret);
                 fprintf(f, ">(io->mToCoreRet);\n");
             }
         }
         fprintf(f, "};\n\n");
+
+
+        fprintf(f, "static ");
+        printFuncDecl(f, api, "RF_", 0, 0);
+        fprintf(f, "\n{\n");
+        fprintf(f, "    Fifo *f = NULL;\n");
+        fprintf(f, "    RS_CMD_%s cmd;\n", api->name);
+        fprintf(f, "    const uint32_t cmdSize = sizeof(cmd);\n");
+        fprintf(f, "    const uint32_t cmdID = RS_CMD_ID_%s;\n", api->name);
+        fprintf(f, "    f->writeAsync(&cmdID, sizeof(cmdID));\n");
+
+        if (api->handcodeApi) {
+            fprintf(f, "    rsHCAPI_%s(rsc", api->name);
+            for (ct2=0; ct2 < api->paramCount; ct2++) {
+                const VarType *vt = &api->params[ct2];
+                if (ct2 > 0 || !api->nocontext) {
+                    fprintf(f, ", ");
+                }
+                fprintf(f, "%s", vt->name);
+            }
+            fprintf(f, ");\n");
+        } else {
+            fprintf(f, "    intptr_t offset = cmdSize;\n");
+            fprintf(f, "    uint32_t dataSize = 0;\n");
+            for (ct2=0; ct2 < api->paramCount; ct2++) {
+                const VarType *vt = &api->params[ct2];
+                if (vt->isConst && vt->ptrLevel) {
+                    switch(vt->ptrLevel) {
+                    case 1:
+                        fprintf(f, "    dataSize += %s_length;\n", vt->name);
+                        break;
+                    case 2:
+                        fprintf(f, "    for (size_t ct = 0; ct < (%s_length_length / sizeof(%s_length)); ct++) {\n", vt->name, vt->name);
+                        fprintf(f, "        dataSize += %s_length[ct];\n", vt->name);
+                        fprintf(f, "    }\n");
+                        break;
+                    default:
+                        printf("pointer level not handled!!");
+                    }
+                }
+            }
+            fprintf(f, "\n");
+
+            for (ct2=0; ct2 < api->paramCount; ct2++) {
+                const VarType *vt = &api->params[ct2];
+                switch(vt->ptrLevel) {
+                case 0:
+                    fprintf(f, "    cmd.%s = %s;\n", vt->name, vt->name);
+                    break;
+                case 1:
+                    fprintf(f, "    cmd.%s = (", vt->name);
+                    printVarType(f, vt);
+                    fprintf(f, ")offset;\n");
+                    fprintf(f, "    offset += %s_length;\n", vt->name);
+                    break;
+                case 2:
+                    fprintf(f, "    cmd.%s = (", vt->name);
+                    printVarType(f, vt);
+                    fprintf(f, ")offset;\n");
+                    fprintf(f, "    for (size_t ct = 0; ct < (%s_length_length / sizeof(%s_length)); ct++) {\n", vt->name, vt->name);
+                    fprintf(f, "        offset += %s_length[ct];\n", vt->name);
+                    fprintf(f, "    }\n");
+                    break;
+                default:
+                    printf("pointer level not handled!!");
+                }
+            }
+            fprintf(f, "\n");
+
+            fprintf(f, "    f->writeAsync(&cmd, cmdSize);\n");
+            for (ct2=0; ct2 < api->paramCount; ct2++) {
+                const VarType *vt = &api->params[ct2];
+                if (vt->ptrLevel == 1) {
+                    fprintf(f, "    f->writeAsync(%s, %s_length);\n", vt->name, vt->name);
+                }
+                if (vt->ptrLevel == 2) {
+                    fprintf(f, "    for (size_t ct = 0; ct < (%s_length_length / sizeof(%s_length)); ct++) {\n", vt->name, vt->name);
+                    fprintf(f, "        f->writeAsync(%s, %s_length[ct]);\n", vt->name, vt->name);
+                    fprintf(f, "        offset += %s_length[ct];\n", vt->name);
+                    fprintf(f, "    }\n");
+                }
+            }
+
+            if (api->ret.typeName[0]) {
+                fprintf(f, "    ");
+                printVarType(f, &api->ret);
+                fprintf(f, " retValue;\n");
+                fprintf(f, "    f->writeWaitReturn(&retValue, sizeof(retValue));\n");
+                fprintf(f, "    return retValue;\n");
+            }
+        }
+        fprintf(f, "}\n\n");
     }
 
     fprintf(f, "\n");
@@ -304,8 +397,14 @@
     }
     fprintf(f, "};\n");
 
-    fprintf(f, "static RsApiEntrypoints_t *s_CurrentTable = &s_LocalTable;\n\n");
+    fprintf(f, "\n");
+    fprintf(f, "static RsApiEntrypoints_t s_RemoteTable = {\n");
+    for (ct=0; ct < apiCount; ct++) {
+        fprintf(f, "    RF_%s,\n", apis[ct].name);
+    }
+    fprintf(f, "};\n");
 
+    fprintf(f, "static RsApiEntrypoints_t *s_CurrentTable = &s_LocalTable;\n\n");
     for (ct=0; ct < apiCount; ct++) {
         int needFlush = 0;
         const ApiEntry * api = &apis[ct];
@@ -357,8 +456,7 @@
             continue;
         }
 
-        fprintf(f, "void rsp_%s(Context *con, const void *vp, size_t cmdSizeBytes)\n", api->name);
-        fprintf(f, "{\n");
+        fprintf(f, "void rsp_%s(Context *con, const void *vp, size_t cmdSizeBytes) {\n", api->name);
 
         //fprintf(f, "    LOGE(\"play command %s\\n\");\n", api->name);
         fprintf(f, "    const RS_CMD_%s *cmd = static_cast<const RS_CMD_%s *>(vp);\n", api->name, api->name);
@@ -377,7 +475,63 @@
         fprintf(f, "};\n\n");
     }
 
-    fprintf(f, "RsPlaybackFunc gPlaybackFuncs[%i] = {\n", apiCount + 1);
+    for (ct=0; ct < apiCount; ct++) {
+        const ApiEntry * api = &apis[ct];
+
+        fprintf(f, "void rspr_%s(Context *con, Fifo *f, uint8_t *scratch, size_t scratchSize) {\n", api->name);
+
+        //fprintf(f, "    LOGE(\"play command %s\\n\");\n", api->name);
+        fprintf(f, "    RS_CMD_%s cmd;\n", api->name);
+        fprintf(f, "    f->read(&cmd, sizeof(cmd));\n");
+
+        for (ct2=0; ct2 < api->paramCount; ct2++) {
+            const VarType *vt = &api->params[ct2];
+            if (vt->ptrLevel == 1) {
+                fprintf(f, "    cmd.%s = (", vt->name);
+                printVarType(f, vt);
+                fprintf(f, ")scratch;\n");
+                fprintf(f, "    f->read(scratch, cmd.%s_length);\n", vt->name);
+                fprintf(f, "    scratch += cmd.%s_length;\n", vt->name);
+            }
+            if (vt->ptrLevel == 2) {
+                fprintf(f, "    size_t sum_%s = 0;\n", vt->name);
+                fprintf(f, "    for (size_t ct = 0; ct < (cmd.%s_length_length / sizeof(cmd.%s_length)); ct++) {\n", vt->name, vt->name);
+                fprintf(f, "        ((size_t *)scratch)[ct] = cmd.%s_length[ct];\n", vt->name);
+                fprintf(f, "        sum_%s += cmd.%s_length[ct];\n", vt->name, vt->name);
+                fprintf(f, "    }\n");
+                fprintf(f, "    f->read(scratch, sum_%s);\n", vt->name);
+                fprintf(f, "    scratch += sum_%s;\n", vt->name);
+            }
+        }
+        fprintf(f, "\n");
+
+        if (api->ret.typeName[0]) {
+            fprintf(f, "    ");
+            printVarType(f, &api->ret);
+            fprintf(f, " ret =\n");
+        }
+
+        fprintf(f, "    rsi_%s(", api->name);
+        if (!api->nocontext) {
+            fprintf(f, "con");
+        }
+        for (ct2=0; ct2 < api->paramCount; ct2++) {
+            const VarType *vt = &api->params[ct2];
+            if (ct2 > 0 || !api->nocontext) {
+                fprintf(f, ",\n");
+            }
+            fprintf(f, "           cmd.%s", vt->name);
+        }
+        fprintf(f, ");\n");
+
+        if (api->ret.typeName[0]) {
+            fprintf(f, "    f->readReturn(&ret, sizeof(ret));\n");
+        }
+
+        fprintf(f, "};\n\n");
+    }
+
+    fprintf(f, "RsPlaybackLocalFunc gPlaybackFuncs[%i] = {\n", apiCount + 1);
     fprintf(f, "    NULL,\n");
     for (ct=0; ct < apiCount; ct++) {
         if (apis[ct].direct) {
@@ -388,6 +542,13 @@
     }
     fprintf(f, "};\n");
 
+    fprintf(f, "RsPlaybackRemoteFunc gPlaybackRemoteFuncs[%i] = {\n", apiCount + 1);
+    fprintf(f, "    NULL,\n");
+    for (ct=0; ct < apiCount; ct++) {
+        fprintf(f, "    %s%s,\n", "rspr_", apis[ct].name);
+    }
+    fprintf(f, "};\n");
+
     fprintf(f, "};\n");
     fprintf(f, "};\n");
 }
@@ -422,14 +583,21 @@
         {
             fprintf(f, "\n");
             fprintf(f, "#include \"rsContext.h\"\n");
+            fprintf(f, "#include \"rsFifo.h\"\n");
             fprintf(f, "\n");
             fprintf(f, "namespace android {\n");
             fprintf(f, "namespace renderscript {\n");
             printStructures(f);
             printFuncDecls(f, "rsi_", 1);
             printPlaybackFuncs(f, "rsp_");
-            fprintf(f, "\n\ntypedef void (*RsPlaybackFunc)(Context *, const void *, size_t sizeBytes);\n");
-            fprintf(f, "extern RsPlaybackFunc gPlaybackFuncs[%i];\n", apiCount + 1);
+            fprintf(f, "\n\ntypedef struct RsPlaybackRemoteHeaderRec {\n");
+            fprintf(f, "    uint32_t command;\n");
+            fprintf(f, "    uint32_t size;\n");
+            fprintf(f, "} RsPlaybackRemoteHeader;\n\n");
+            fprintf(f, "typedef void (*RsPlaybackLocalFunc)(Context *, const void *, size_t sizeBytes);\n");
+            fprintf(f, "typedef void (*RsPlaybackRemoteFunc)(Context *, Fifo *, uint8_t *scratch, size_t scratchSize);\n");
+            fprintf(f, "extern RsPlaybackLocalFunc gPlaybackFuncs[%i];\n", apiCount + 1);
+            fprintf(f, "extern RsPlaybackRemoteFunc gPlaybackRemoteFuncs[%i];\n", apiCount + 1);
 
             fprintf(f, "}\n");
             fprintf(f, "}\n");
diff --git a/libs/rs/spec.l b/libs/rs/spec.l
index dcd4435..a24bfd3 100644
--- a/libs/rs/spec.l
+++ b/libs/rs/spec.l
@@ -21,15 +21,26 @@
    int typeNextState;
 
    void checkPointerType() {
-       VarType *lastType = currType;
-       if (lastType->ptrLevel) {
+       VarType *baseType = currType;
+       int curPtrLevel = 0;
+       while (curPtrLevel < baseType->ptrLevel) {
            currType = &apis[apiCount].params[apis[apiCount].paramCount];
            currType->type = 4;
+           currType->ptrLevel = curPtrLevel;
+           if (currType->ptrLevel > 0) {
+              currType->isConst = 1;
+           }
            sprintf(currType->typeName, "%s", "size_t");
-           if (lastType->name[0]) {
-               sprintf(currType->name, "%s_length", lastType->name);
+           switch(baseType->ptrLevel - curPtrLevel) {
+           case 1:
+              sprintf(currType->name, "%s_length", baseType->name);
+              break;
+           case 2:
+              sprintf(currType->name, "%s_length_length", baseType->name);
+              break;
            }
            apis[apiCount].paramCount++;
+           curPtrLevel ++;
        }
    }
 
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index a95f432..d811dd7 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -302,6 +302,24 @@
     return &values[index];
 }
 
+static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) {
+    float* value = c.editAxisValue(axis);
+    if (value) {
+        *value *= scaleFactor;
+    }
+}
+
+void PointerCoords::scale(float scaleFactor) {
+    // No need to scale pressure or size since they are normalized.
+    // No need to scale orientation since it is meaningless to do so.
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor);
+    scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor);
+}
+
 #ifdef HAVE_ANDROID_OS
 status_t PointerCoords::readFromParcel(Parcel* parcel) {
     bits = parcel->readInt64();
@@ -436,11 +454,9 @@
     float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
     switch (axis) {
     case AMOTION_EVENT_AXIS_X:
-        value += mXOffset;
-        break;
+        return value + mXOffset;
     case AMOTION_EVENT_AXIS_Y:
-        value += mYOffset;
-        break;
+        return value + mYOffset;
     }
     return value;
 }
@@ -460,11 +476,9 @@
     float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
     switch (axis) {
     case AMOTION_EVENT_AXIS_X:
-        value += mXOffset;
-        break;
+        return value + mXOffset;
     case AMOTION_EVENT_AXIS_Y:
-        value += mYOffset;
-        break;
+        return value + mYOffset;
     }
     return value;
 }
@@ -484,13 +498,6 @@
     mYOffset += yOffset;
 }
 
-static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) {
-    float* value = c.editAxisValue(axis);
-    if (value) {
-        *value *= scaleFactor;
-    }
-}
-
 void MotionEvent::scale(float scaleFactor) {
     mXOffset *= scaleFactor;
     mYOffset *= scaleFactor;
@@ -499,15 +506,7 @@
 
     size_t numSamples = mSamplePointerCoords.size();
     for (size_t i = 0; i < numSamples; i++) {
-        PointerCoords& c = mSamplePointerCoords.editItemAt(i);
-        // No need to scale pressure or size since they are normalized.
-        // No need to scale orientation since it is meaningless to do so.
-        scaleAxisValue(c, AMOTION_EVENT_AXIS_X, scaleFactor);
-        scaleAxisValue(c, AMOTION_EVENT_AXIS_Y, scaleFactor);
-        scaleAxisValue(c, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor);
-        scaleAxisValue(c, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor);
-        scaleAxisValue(c, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor);
-        scaleAxisValue(c, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor);
+        mSamplePointerCoords.editItemAt(i).scale(scaleFactor);
     }
 }
 
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/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index e52f6d1..4189354 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1644,7 +1644,7 @@
     if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) {
         componentName = "OMX.Nvidia.h264.decode";
     } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
-        componentName = "OMX.Nvidia.aac.decoder";
+        componentName = "OMX.google.aac.decoder";
     } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_MPEG)) {
         componentName = "OMX.Nvidia.mp3.decoder";
     } else {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 10ce00c9..d8cf810 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -3,6 +3,8 @@
 
 include frameworks/base/media/libstagefright/codecs/common/Config.mk
 
+BUILD_WITH_SOFTWARE_DECODERS := true
+
 LOCAL_SRC_FILES:=                         \
         ACodec.cpp                        \
         AACExtractor.cpp                  \
@@ -45,7 +47,6 @@
         ShoutcastSource.cpp               \
         StagefrightMediaScanner.cpp       \
         StagefrightMetadataRetriever.cpp  \
-        ThreadedSource.cpp                \
         ThrottledSource.cpp               \
         TimeSource.cpp                    \
         TimedEventQueue.cpp               \
@@ -82,28 +83,39 @@
 
 LOCAL_STATIC_LIBRARIES := \
         libstagefright_color_conversion \
-        libstagefright_aacdec \
         libstagefright_aacenc \
-        libstagefright_amrnbdec \
         libstagefright_amrnbenc \
-        libstagefright_amrwbdec \
         libstagefright_amrwbenc \
-        libstagefright_avcdec \
         libstagefright_avcenc \
-        libstagefright_m4vh263dec \
         libstagefright_m4vh263enc \
-        libstagefright_mp3dec \
-        libstagefright_vorbisdec \
         libstagefright_matroska \
-        libstagefright_vpxdec \
         libvpx \
         libstagefright_mpeg2ts \
         libstagefright_httplive \
         libstagefright_rtsp \
         libstagefright_id3 \
-        libstagefright_g711dec \
         libFLAC \
 
+ifeq ($(BUILD_WITH_SOFTWARE_DECODERS),true)
+
+LOCAL_SRC_FILES += \
+        ThreadedSource.cpp                \
+
+LOCAL_STATIC_LIBRARIES += \
+        libstagefright_aacdec \
+        libstagefright_amrnbdec \
+        libstagefright_amrwbdec \
+        libstagefright_avcdec \
+        libstagefright_g711dec \
+        libstagefright_mp3dec \
+        libstagefright_m4vh263dec \
+        libstagefright_vorbisdec \
+        libstagefright_vpxdec \
+        libvpx \
+
+endif
+
+
 ################################################################################
 
 # The following was shamelessly copied from external/webkit/Android.mk and
@@ -180,6 +192,10 @@
 
 LOCAL_CFLAGS += -Wno-multichar
 
+ifeq ($(BUILD_WITH_SOFTWARE_DECODERS),true)
+    LOCAL_CFLAGS += -DHAVE_SOFTWARE_DECODERS
+endif
+
 LOCAL_MODULE:= libstagefright
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 69f9c23..dd69e6b 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -84,7 +84,13 @@
 
     CHECK(mFirstBuffer == NULL);
 
-    mFirstBufferResult = mSource->read(&mFirstBuffer);
+    MediaSource::ReadOptions options;
+    if (mSeeking) {
+        options.setSeekTo(mSeekTimeUs);
+        mSeeking = false;
+    }
+
+    mFirstBufferResult = mSource->read(&mFirstBuffer, &options);
     if (mFirstBufferResult == INFO_FORMAT_CHANGED) {
         LOGV("INFO_FORMAT_CHANGED!!!");
 
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index cccd0b7..b45e5d3 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -831,6 +831,8 @@
     if (!(mFlags & AUDIOPLAYER_STARTED)) {
         mFlags |= AUDIOPLAYER_STARTED;
 
+        bool wasSeeking = mAudioPlayer->isSeeking();
+
         // We've already started the MediaSource in order to enable
         // the prefetcher to read its data.
         status_t err = mAudioPlayer->start(
@@ -840,6 +842,13 @@
             notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
             return err;
         }
+
+        if (wasSeeking) {
+            CHECK(!mAudioPlayer->isSeeking());
+
+            // We will have finished the seek while starting the audio player.
+            postAudioSeekComplete_l();
+        }
     } else {
         mAudioPlayer->resume();
     }
@@ -933,7 +942,9 @@
     // before creating a new one.
     IPCThreadState::self()->flushCommands();
 
-    if (USE_SURFACE_ALLOC && strncmp(component, "OMX.", 4) == 0) {
+    if (USE_SURFACE_ALLOC
+            && !strncmp(component, "OMX.", 4)
+            && strncmp(component, "OMX.google.", 11)) {
         // Hardware decoders avoid the CPU color conversion by decoding
         // directly to ANativeBuffers, so we must use a renderer that
         // just pushes those buffers to the ANativeWindow.
@@ -1957,6 +1968,10 @@
 
 void AwesomePlayer::postAudioSeekComplete() {
     Mutex::Autolock autoLock(mLock);
+    postAudioSeekComplete_l();
+}
+
+void AwesomePlayer::postAudioSeekComplete_l() {
     postCheckAudioStatusEvent_l(0 /* delayUs */);
 }
 
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index e13b67e9..fd60f06 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -45,6 +45,7 @@
 static const int64_t kMax32BitFileSize = 0x007fffffffLL;
 static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
 static const uint8_t kNalUnitTypePicParamSet = 0x08;
+static const int64_t kInitialDelayTimeUs     = 700000LL;
 
 // Using longer adjustment period to suppress fluctuations in
 // the audio encoding paths
@@ -63,12 +64,13 @@
 
     int64_t getDurationUs() const;
     int64_t getEstimatedTrackSizeBytes() const;
-    void writeTrackHeader(int32_t trackID, bool use32BitOffset = true);
+    void writeTrackHeader(bool use32BitOffset = true);
     void bufferChunk(int64_t timestampUs);
     bool isAvc() const { return mIsAvc; }
     bool isAudio() const { return mIsAudio; }
     bool isMPEG4() const { return mIsMPEG4; }
     void addChunkOffset(off64_t offset);
+    int32_t getTrackId() const { return mTrackId; }
     status_t dump(int fd, const Vector<String16>& args) const;
 
 private:
@@ -84,6 +86,7 @@
     bool mIsMPEG4;
     int32_t mTrackId;
     int64_t mTrackDurationUs;
+    int64_t mMaxChunkDurationUs;
 
     // For realtime applications, we need to adjust the media clock
     // for video track based on the audio media clock
@@ -156,6 +159,8 @@
 
     bool mReachedEOS;
     int64_t mStartTimestampUs;
+    int64_t mStartTimeRealUs;
+    int64_t mFirstSampleTimeRealUs;
     int64_t mPreviousTrackTimeUs;
     int64_t mTrackEveryTimeDurationUs;
 
@@ -187,12 +192,9 @@
     const uint8_t *parseParamSet(
         const uint8_t *data, size_t length, int type, size_t *paramSetLen);
 
-    status_t makeAVCCodecSpecificData(
-            const uint8_t *data, size_t size);
-    status_t copyAVCCodecSpecificData(
-            const uint8_t *data, size_t size);
-    status_t parseAVCCodecSpecificData(
-            const uint8_t *data, size_t size);
+    status_t makeAVCCodecSpecificData(const uint8_t *data, size_t size);
+    status_t copyAVCCodecSpecificData(const uint8_t *data, size_t size);
+    status_t parseAVCCodecSpecificData(const uint8_t *data, size_t size);
 
     // Track authoring progress status
     void trackProgressStatus(int64_t timeUs, status_t err = OK);
@@ -214,6 +216,31 @@
     void addOneStscTableEntry(size_t chunkId, size_t sampleId);
     void addOneStssTableEntry(size_t sampleId);
     void addOneSttsTableEntry(size_t sampleCount, int64_t durationUs);
+    void sendTrackSummary(bool hasMultipleTracks);
+
+    // Write the boxes
+    void writeStcoBox(bool use32BitOffset);
+    void writeStscBox();
+    void writeStszBox();
+    void writeStssBox();
+    void writeSttsBox();
+    void writeD263Box();
+    void writePaspBox();
+    void writeAvccBox();
+    void writeUrlBox();
+    void writeDrefBox();
+    void writeDinfBox();
+    void writeDamrBox();
+    void writeMdhdBox(time_t now);
+    void writeSmhdBox();
+    void writeVmhdBox();
+    void writeHdlrBox();
+    void writeTkhdBox(time_t now);
+    void writeMp4aEsdsBox();
+    void writeMp4vEsdsBox();
+    void writeAudioFourCCBox();
+    void writeVideoFourCCBox();
+    void writeStblBox(bool use32BitOffset);
 
     Track(const Track &);
     Track &operator=(const Track &);
@@ -450,20 +477,7 @@
     mMoovBoxBuffer = NULL;
     mMoovBoxBufferOffset = 0;
 
-    beginBox("ftyp");
-      {
-        int32_t fileType;
-        if (param && param->findInt32(kKeyFileType, &fileType) &&
-            fileType != OUTPUT_FORMAT_MPEG_4) {
-            writeFourcc("3gp4");
-        } else {
-            writeFourcc("isom");
-        }
-      }
-      writeInt32(0);
-      writeFourcc("isom");
-      writeFourcc("3gp4");
-    endBox();
+    writeFtypBox(param);
 
     mFreeBoxOffset = mOffset;
 
@@ -643,43 +657,12 @@
     }
     lseek64(mFd, mOffset, SEEK_SET);
 
-    time_t now = time(NULL);
     const off64_t moovOffset = mOffset;
     mWriteMoovBoxToMemory = true;
     mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
     mMoovBoxBufferOffset = 0;
     CHECK(mMoovBoxBuffer != NULL);
-    int32_t duration = (maxDurationUs * mTimeScale + 5E5) / 1E6;
-
-    beginBox("moov");
-
-      beginBox("mvhd");
-        writeInt32(0);             // version=0, flags=0
-        writeInt32(now);           // creation time
-        writeInt32(now);           // modification time
-        writeInt32(mTimeScale);    // mvhd timescale
-        writeInt32(duration);
-        writeInt32(0x10000);       // rate: 1.0
-        writeInt16(0x100);         // volume
-        writeInt16(0);             // reserved
-        writeInt32(0);             // reserved
-        writeInt32(0);             // reserved
-        writeCompositionMatrix(0); // matrix
-        writeInt32(0);             // predefined
-        writeInt32(0);             // predefined
-        writeInt32(0);             // predefined
-        writeInt32(0);             // predefined
-        writeInt32(0);             // predefined
-        writeInt32(0);             // predefined
-        writeInt32(mTracks.size() + 1);  // nextTrackID
-      endBox();  // mvhd
-
-      int32_t id = 1;
-      for (List<Track *>::iterator it = mTracks.begin();
-           it != mTracks.end(); ++it, ++id) {
-          (*it)->writeTrackHeader(id, mUse32BitOffset);
-      }
-    endBox();  // moov
+    writeMoovBox(maxDurationUs);
 
     mWriteMoovBoxToMemory = false;
     if (mStreamableFile) {
@@ -709,9 +692,73 @@
     mFd = -1;
     mInitCheck = NO_INIT;
     mStarted = false;
+
     return err;
 }
 
+void MPEG4Writer::writeMvhdBox(int64_t durationUs) {
+    time_t now = time(NULL);
+    beginBox("mvhd");
+    writeInt32(0);             // version=0, flags=0
+    writeInt32(now);           // creation time
+    writeInt32(now);           // modification time
+    writeInt32(mTimeScale);    // mvhd timescale
+    int32_t duration = (durationUs * mTimeScale + 5E5) / 1E6;
+    writeInt32(duration);
+    writeInt32(0x10000);       // rate: 1.0
+    writeInt16(0x100);         // volume
+    writeInt16(0);             // reserved
+    writeInt32(0);             // reserved
+    writeInt32(0);             // reserved
+    writeCompositionMatrix(0); // matrix
+    writeInt32(0);             // predefined
+    writeInt32(0);             // predefined
+    writeInt32(0);             // predefined
+    writeInt32(0);             // predefined
+    writeInt32(0);             // predefined
+    writeInt32(0);             // predefined
+    writeInt32(mTracks.size() + 1);  // nextTrackID
+    endBox();  // mvhd
+}
+
+void MPEG4Writer::writeMoovBox(int64_t durationUs) {
+    beginBox("moov");
+    writeMvhdBox(durationUs);
+    int32_t id = 1;
+    for (List<Track *>::iterator it = mTracks.begin();
+        it != mTracks.end(); ++it, ++id) {
+        (*it)->writeTrackHeader(mUse32BitOffset);
+    }
+    endBox();  // moov
+}
+
+void MPEG4Writer::writeFtypBox(const MetaData *param) {
+    beginBox("ftyp");
+
+    int32_t fileType;
+    if (param && param->findInt32(kKeyFileType, &fileType) &&
+        fileType != OUTPUT_FORMAT_MPEG_4) {
+        writeFourcc("3gp4");
+    } else {
+        writeFourcc("isom");
+    }
+
+    writeInt32(0);
+    writeFourcc("isom");
+    writeFourcc("3gp4");
+    endBox();
+}
+
+void MPEG4Writer::sendSessionSummary() {
+    for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
+         it != mChunkInfos.end(); ++it) {
+        int trackNum = it->mTrack->getTrackId() << 28;
+        notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+                trackNum | MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS,
+                it->mMaxInterChunkDurUs);
+    }
+}
+
 status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) {
     mInterleaveDurationUs = durationUs;
     return OK;
@@ -1156,18 +1203,13 @@
 void MPEG4Writer::writeAllChunks() {
     LOGV("writeAllChunks");
     size_t outstandingChunks = 0;
-    while (!mChunkInfos.empty()) {
-        List<ChunkInfo>::iterator it = mChunkInfos.begin();
-        while (!it->mChunks.empty()) {
-            Chunk chunk;
-            if (findChunkToWrite(&chunk)) {
-                writeChunkToFile(&chunk);
-                ++outstandingChunks;
-            }
-        }
-        it->mTrack = NULL;
-        mChunkInfos.erase(it);
+    Chunk chunk;
+    while (findChunkToWrite(&chunk)) {
+        ++outstandingChunks;
     }
+
+    sendSessionSummary();
+
     mChunkInfos.clear();
     LOGD("%d chunks are written in the last batch", outstandingChunks);
 }
@@ -1175,8 +1217,6 @@
 bool MPEG4Writer::findChunkToWrite(Chunk *chunk) {
     LOGV("findChunkToWrite");
 
-    // Find the smallest timestamp, and write that chunk out
-    // XXX: What if some track is just too slow?
     int64_t minTimestampUs = 0x7FFFFFFFFFFFFFFFLL;
     Track *track = NULL;
     for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
@@ -1205,6 +1245,13 @@
             *chunk = *(it->mChunks.begin());
             it->mChunks.erase(it->mChunks.begin());
             CHECK_EQ(chunk->mTrack, track);
+
+            int64_t interChunkTimeUs =
+                chunk->mTimeStampUs - it->mPrevChunkTimestampUs;
+            if (interChunkTimeUs > it->mPrevChunkTimestampUs) {
+                it->mMaxInterChunkDurUs = interChunkTimeUs;
+            }
+
             return true;
         }
     }
@@ -1248,6 +1295,8 @@
          it != mTracks.end(); ++it) {
         ChunkInfo info;
         info.mTrack = *it;
+        info.mPrevChunkTimestampUs = 0;
+        info.mMaxInterChunkDurUs = 0;
         mChunkInfos.push_back(info);
     }
 
@@ -1271,6 +1320,7 @@
     if (params == NULL || !params->findInt64(kKeyTime, &startTimeUs)) {
         startTimeUs = 0;
     }
+    mStartTimeRealUs = startTimeUs;
 
     int32_t rotationDegrees;
     if (!mIsAudio && params && params->findInt32(kKeyRotation, &rotationDegrees)) {
@@ -1298,7 +1348,7 @@
          * Ideally, this platform-specific value should be defined
          * in media_profiles.xml file
          */
-        startTimeUs += 700000;
+        startTimeUs += kInitialDelayTimeUs;
     }
 
     meta->setInt64(kKeyTime, startTimeUs);
@@ -1329,6 +1379,7 @@
     mPrevMediaTimeAdjustSample = 0;
     mTotalDriftTimeToAdjustUs = 0;
     mPrevTotalAccumDriftTimeUs = 0;
+    mMaxChunkDurationUs = 0;
 
     pthread_create(&mThread, &attr, ThreadWrapper, this);
     pthread_attr_destroy(&attr);
@@ -1766,6 +1817,7 @@
 status_t MPEG4Writer::Track::threadEntry() {
     int32_t count = 0;
     const int64_t interleaveDurationUs = mOwner->interleaveDuration();
+    const bool hasMultipleTracks = (mOwner->numTracks() > 1);
     int64_t chunkTimestampUs = 0;
     int32_t nChunks = 0;
     int32_t nZeroLengthFrames = 0;
@@ -1903,7 +1955,8 @@
         LOGV("%s timestampUs: %lld", mIsAudio? "Audio": "Video", timestampUs);
 
 ////////////////////////////////////////////////////////////////////////////////
-        if (mSampleSizes.empty()) {
+        if (mNumSamples == 0) {
+            mFirstSampleTimeRealUs = systemTime() / 1000;
             mStartTimestampUs = timestampUs;
             mOwner->setStartTimestampUs(mStartTimestampUs);
             previousPausedDurationUs = mStartTimestampUs;
@@ -1998,7 +2051,7 @@
             }
             trackProgressStatus(timestampUs);
         }
-        if (mOwner->numTracks() == 1) {
+        if (!hasMultipleTracks) {
             off64_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy)
                                  : mOwner->addSample_l(copy);
             if (mChunkOffsets.empty()) {
@@ -2017,7 +2070,11 @@
             if (chunkTimestampUs == 0) {
                 chunkTimestampUs = timestampUs;
             } else {
-                if (timestampUs - chunkTimestampUs > interleaveDurationUs) {
+                int64_t chunkDurationUs = timestampUs - chunkTimestampUs;
+                if (chunkDurationUs > interleaveDurationUs) {
+                    if (chunkDurationUs > mMaxChunkDurationUs) {
+                        mMaxChunkDurationUs = chunkDurationUs;
+                    }
                     ++nChunks;
                     if (nChunks == 1 ||  // First chunk
                         (--(mStscTableEntries.end()))->samplesPerChunk !=
@@ -2040,7 +2097,7 @@
     mOwner->trackProgressStatus(mTrackId, -1, err);
 
     // Last chunk
-    if (mOwner->numTracks() == 1) {
+    if (!hasMultipleTracks) {
         addOneStscTableEntry(1, mNumSamples);
     } else if (!mChunkSamples.empty()) {
         addOneStscTableEntry(++nChunks, mChunkSamples.size());
@@ -2067,6 +2124,9 @@
 
     mTrackDurationUs += lastDurationUs;
     mReachedEOS = true;
+
+    sendTrackSummary(hasMultipleTracks);
+
     LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s",
             count, nZeroLengthFrames, mNumSamples, mIsAudio? "audio": "video");
     if (mIsAudio) {
@@ -2079,6 +2139,44 @@
     return err;
 }
 
+void MPEG4Writer::Track::sendTrackSummary(bool hasMultipleTracks) {
+    int trackNum = (mTrackId << 28);
+
+    mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+                    trackNum | MEDIA_RECORDER_TRACK_INFO_TYPE,
+                    mIsAudio? 0: 1);
+
+    mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+                    trackNum | MEDIA_RECORDER_TRACK_INFO_DURATION_MS,
+                    mTrackDurationUs / 1000);
+
+    mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+                    trackNum | MEDIA_RECORDER_TRACK_INFO_ENCODED_FRAMES,
+                    mNumSamples);
+
+    // The system delay time excluding the requested initial delay that
+    // is used to eliminate the recording sound.
+    int64_t initialDelayUs =
+        mFirstSampleTimeRealUs - mStartTimeRealUs - kInitialDelayTimeUs;
+    mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+                    trackNum | MEDIA_RECORDER_TRACK_INFO_INITIAL_DELAY_MS,
+                    (initialDelayUs) / 1000);
+
+    if (hasMultipleTracks) {
+        mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+                    trackNum | MEDIA_RECORDER_TRACK_INFO_MAX_CHUNK_DUR_MS,
+                    mMaxChunkDurationUs / 1000);
+
+        int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
+        if (mStartTimestampUs != moovStartTimeUs) {
+            int64_t startTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
+            mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
+                    trackNum | MEDIA_RECORDER_TRACK_INFO_START_OFFSET_MS,
+                    startTimeOffsetUs / 1000);
+        }
+    }
+}
+
 void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
     LOGV("trackProgressStatus: %lld us", timeUs);
     if (mTrackEveryTimeDurationUs > 0 &&
@@ -2169,388 +2267,459 @@
     return OK;
 }
 
-void MPEG4Writer::Track::writeTrackHeader(
-        int32_t trackID, bool use32BitOffset) {
-    const char *mime;
-    bool success = mMeta->findCString(kKeyMIMEType, &mime);
-    CHECK(success);
+void MPEG4Writer::Track::writeTrackHeader(bool use32BitOffset) {
 
     LOGV("%s track time scale: %d",
         mIsAudio? "Audio": "Video", mTimeScale);
 
     time_t now = time(NULL);
-    int32_t mvhdTimeScale = mOwner->getTimeScale();
+    mOwner->beginBox("trak");
+        writeTkhdBox(now);
+        mOwner->beginBox("mdia");
+            writeMdhdBox(now);
+            writeHdlrBox();
+            mOwner->beginBox("minf");
+                if (mIsAudio) {
+                    writeSmhdBox();
+                } else {
+                    writeVmhdBox();
+                }
+                writeDinfBox();
+                writeStblBox(use32BitOffset);
+            mOwner->endBox();  // minf
+        mOwner->endBox();  // mdia
+    mOwner->endBox();  // trak
+}
+
+void MPEG4Writer::Track::writeStblBox(bool use32BitOffset) {
+    mOwner->beginBox("stbl");
+    mOwner->beginBox("stsd");
+    mOwner->writeInt32(0);               // version=0, flags=0
+    mOwner->writeInt32(1);               // entry count
+    if (mIsAudio) {
+        writeAudioFourCCBox();
+    } else {
+        writeVideoFourCCBox();
+    }
+    mOwner->endBox();  // stsd
+    writeSttsBox();
+    if (!mIsAudio) {
+        writeStssBox();
+    }
+    writeStszBox();
+    writeStscBox();
+    writeStcoBox(use32BitOffset);
+    mOwner->endBox();  // stbl
+}
+
+void MPEG4Writer::Track::writeVideoFourCCBox() {
+    const char *mime;
+    bool success = mMeta->findCString(kKeyMIMEType, &mime);
+    CHECK(success);
+    if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+        mOwner->beginBox("mp4v");
+    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
+        mOwner->beginBox("s263");
+    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+        mOwner->beginBox("avc1");
+    } else {
+        LOGE("Unknown mime type '%s'.", mime);
+        CHECK(!"should not be here, unknown mime type.");
+    }
+
+    mOwner->writeInt32(0);           // reserved
+    mOwner->writeInt16(0);           // reserved
+    mOwner->writeInt16(1);           // data ref index
+    mOwner->writeInt16(0);           // predefined
+    mOwner->writeInt16(0);           // reserved
+    mOwner->writeInt32(0);           // predefined
+    mOwner->writeInt32(0);           // predefined
+    mOwner->writeInt32(0);           // predefined
+
+    int32_t width, height;
+    success = mMeta->findInt32(kKeyWidth, &width);
+    success = success && mMeta->findInt32(kKeyHeight, &height);
+    CHECK(success);
+
+    mOwner->writeInt16(width);
+    mOwner->writeInt16(height);
+    mOwner->writeInt32(0x480000);    // horiz resolution
+    mOwner->writeInt32(0x480000);    // vert resolution
+    mOwner->writeInt32(0);           // reserved
+    mOwner->writeInt16(1);           // frame count
+    mOwner->write("                                ", 32);
+    mOwner->writeInt16(0x18);        // depth
+    mOwner->writeInt16(-1);          // predefined
+
+    CHECK(23 + mCodecSpecificDataSize < 128);
+
+    if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+        writeMp4vEsdsBox();
+    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
+        writeD263Box();
+    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+        writeAvccBox();
+    }
+
+    writePaspBox();
+    mOwner->endBox();  // mp4v, s263 or avc1
+}
+
+void MPEG4Writer::Track::writeAudioFourCCBox() {
+    const char *mime;
+    bool success = mMeta->findCString(kKeyMIMEType, &mime);
+    CHECK(success);
+    const char *fourcc = NULL;
+    if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
+        fourcc = "samr";
+    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
+        fourcc = "sawb";
+    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
+        fourcc = "mp4a";
+    } else {
+        LOGE("Unknown mime type '%s'.", mime);
+        CHECK(!"should not be here, unknown mime type.");
+    }
+
+    mOwner->beginBox(fourcc);        // audio format
+    mOwner->writeInt32(0);           // reserved
+    mOwner->writeInt16(0);           // reserved
+    mOwner->writeInt16(0x1);         // data ref index
+    mOwner->writeInt32(0);           // reserved
+    mOwner->writeInt32(0);           // reserved
+    int32_t nChannels;
+    CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels));
+    mOwner->writeInt16(nChannels);   // channel count
+    mOwner->writeInt16(16);          // sample size
+    mOwner->writeInt16(0);           // predefined
+    mOwner->writeInt16(0);           // reserved
+
+    int32_t samplerate;
+    success = mMeta->findInt32(kKeySampleRate, &samplerate);
+    CHECK(success);
+    mOwner->writeInt32(samplerate << 16);
+    if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
+        writeMp4aEsdsBox();
+    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) ||
+               !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
+        writeDamrBox();
+    }
+    mOwner->endBox();
+}
+
+void MPEG4Writer::Track::writeMp4aEsdsBox() {
+    mOwner->beginBox("esds");
+    CHECK(mCodecSpecificData);
+    CHECK(mCodecSpecificDataSize > 0);
+
+    // Make sure all sizes encode to a single byte.
+    CHECK(mCodecSpecificDataSize + 23 < 128);
+
+    mOwner->writeInt32(0);     // version=0, flags=0
+    mOwner->writeInt8(0x03);   // ES_DescrTag
+    mOwner->writeInt8(23 + mCodecSpecificDataSize);
+    mOwner->writeInt16(0x0000);// ES_ID
+    mOwner->writeInt8(0x00);
+
+    mOwner->writeInt8(0x04);   // DecoderConfigDescrTag
+    mOwner->writeInt8(15 + mCodecSpecificDataSize);
+    mOwner->writeInt8(0x40);   // objectTypeIndication ISO/IEC 14492-2
+    mOwner->writeInt8(0x15);   // streamType AudioStream
+
+    mOwner->writeInt16(0x03);  // XXX
+    mOwner->writeInt8(0x00);   // buffer size 24-bit
+    mOwner->writeInt32(96000); // max bit rate
+    mOwner->writeInt32(96000); // avg bit rate
+
+    mOwner->writeInt8(0x05);   // DecoderSpecificInfoTag
+    mOwner->writeInt8(mCodecSpecificDataSize);
+    mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+
+    static const uint8_t kData2[] = {
+        0x06,  // SLConfigDescriptorTag
+        0x01,
+        0x02
+    };
+    mOwner->write(kData2, sizeof(kData2));
+
+    mOwner->endBox();  // esds
+}
+
+void MPEG4Writer::Track::writeMp4vEsdsBox() {
+    CHECK(mCodecSpecificData);
+    CHECK(mCodecSpecificDataSize > 0);
+    mOwner->beginBox("esds");
+
+    mOwner->writeInt32(0);    // version=0, flags=0
+
+    mOwner->writeInt8(0x03);  // ES_DescrTag
+    mOwner->writeInt8(23 + mCodecSpecificDataSize);
+    mOwner->writeInt16(0x0000);  // ES_ID
+    mOwner->writeInt8(0x1f);
+
+    mOwner->writeInt8(0x04);  // DecoderConfigDescrTag
+    mOwner->writeInt8(15 + mCodecSpecificDataSize);
+    mOwner->writeInt8(0x20);  // objectTypeIndication ISO/IEC 14492-2
+    mOwner->writeInt8(0x11);  // streamType VisualStream
+
+    static const uint8_t kData[] = {
+        0x01, 0x77, 0x00,
+        0x00, 0x03, 0xe8, 0x00,
+        0x00, 0x03, 0xe8, 0x00
+    };
+    mOwner->write(kData, sizeof(kData));
+
+    mOwner->writeInt8(0x05);  // DecoderSpecificInfoTag
+
+    mOwner->writeInt8(mCodecSpecificDataSize);
+    mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+
+    static const uint8_t kData2[] = {
+        0x06,  // SLConfigDescriptorTag
+        0x01,
+        0x02
+    };
+    mOwner->write(kData2, sizeof(kData2));
+
+    mOwner->endBox();  // esds
+}
+
+void MPEG4Writer::Track::writeTkhdBox(time_t now) {
+    mOwner->beginBox("tkhd");
+    // Flags = 7 to indicate that the track is enabled, and
+    // part of the presentation
+    mOwner->writeInt32(0x07);          // version=0, flags=7
+    mOwner->writeInt32(now);           // creation time
+    mOwner->writeInt32(now);           // modification time
+    mOwner->writeInt32(mTrackId);
+    mOwner->writeInt32(0);             // reserved
     int64_t trakDurationUs = getDurationUs();
+    int32_t mvhdTimeScale = mOwner->getTimeScale();
+    int32_t tkhdDuration =
+        (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
+    mOwner->writeInt32(tkhdDuration);  // in mvhd timescale
+    mOwner->writeInt32(0);             // reserved
+    mOwner->writeInt32(0);             // reserved
+    mOwner->writeInt16(0);             // layer
+    mOwner->writeInt16(0);             // alternate group
+    mOwner->writeInt16(mIsAudio ? 0x100 : 0);  // volume
+    mOwner->writeInt16(0);             // reserved
+
+    mOwner->writeCompositionMatrix(mRotation);       // matrix
+
+    if (mIsAudio) {
+        mOwner->writeInt32(0);
+        mOwner->writeInt32(0);
+    } else {
+        int32_t width, height;
+        bool success = mMeta->findInt32(kKeyWidth, &width);
+        success = success && mMeta->findInt32(kKeyHeight, &height);
+        CHECK(success);
+
+        mOwner->writeInt32(width << 16);   // 32-bit fixed-point value
+        mOwner->writeInt32(height << 16);  // 32-bit fixed-point value
+    }
+    mOwner->endBox();  // tkhd
+}
+
+void MPEG4Writer::Track::writeVmhdBox() {
+    mOwner->beginBox("vmhd");
+    mOwner->writeInt32(0x01);        // version=0, flags=1
+    mOwner->writeInt16(0);           // graphics mode
+    mOwner->writeInt16(0);           // opcolor
+    mOwner->writeInt16(0);
+    mOwner->writeInt16(0);
+    mOwner->endBox();
+}
+
+void MPEG4Writer::Track::writeSmhdBox() {
+    mOwner->beginBox("smhd");
+    mOwner->writeInt32(0);           // version=0, flags=0
+    mOwner->writeInt16(0);           // balance
+    mOwner->writeInt16(0);           // reserved
+    mOwner->endBox();
+}
+
+void MPEG4Writer::Track::writeHdlrBox() {
+    mOwner->beginBox("hdlr");
+    mOwner->writeInt32(0);             // version=0, flags=0
+    mOwner->writeInt32(0);             // component type: should be mhlr
+    mOwner->writeFourcc(mIsAudio ? "soun" : "vide");  // component subtype
+    mOwner->writeInt32(0);             // reserved
+    mOwner->writeInt32(0);             // reserved
+    mOwner->writeInt32(0);             // reserved
+    // Removing "r" for the name string just makes the string 4 byte aligned
+    mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle");  // name
+    mOwner->endBox();
+}
+
+void MPEG4Writer::Track::writeMdhdBox(time_t now) {
+    int64_t trakDurationUs = getDurationUs();
+    mOwner->beginBox("mdhd");
+    mOwner->writeInt32(0);             // version=0, flags=0
+    mOwner->writeInt32(now);           // creation time
+    mOwner->writeInt32(now);           // modification time
+    mOwner->writeInt32(mTimeScale);    // media timescale
+    int32_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6;
+    mOwner->writeInt32(mdhdDuration);  // use media timescale
+    // Language follows the three letter standard ISO-639-2/T
+    // 'e', 'n', 'g' for "English", for instance.
+    // Each character is packed as the difference between its ASCII value and 0x60.
+    // For "English", these are 00101, 01110, 00111.
+    // XXX: Where is the padding bit located: 0x15C7?
+    mOwner->writeInt16(0);             // language code
+    mOwner->writeInt16(0);             // predefined
+    mOwner->endBox();
+}
+
+void MPEG4Writer::Track::writeDamrBox() {
+    // 3gpp2 Spec AMRSampleEntry fields
+    mOwner->beginBox("damr");
+    mOwner->writeCString("   ");  // vendor: 4 bytes
+    mOwner->writeInt8(0);         // decoder version
+    mOwner->writeInt16(0x83FF);   // mode set: all enabled
+    mOwner->writeInt8(0);         // mode change period
+    mOwner->writeInt8(1);         // frames per sample
+    mOwner->endBox();
+}
+
+void MPEG4Writer::Track::writeUrlBox() {
+    // The table index here refers to the sample description index
+    // in the sample table entries.
+    mOwner->beginBox("url ");
+    mOwner->writeInt32(1);  // version=0, flags=1 (self-contained)
+    mOwner->endBox();  // url
+}
+
+void MPEG4Writer::Track::writeDrefBox() {
+    mOwner->beginBox("dref");
+    mOwner->writeInt32(0);  // version=0, flags=0
+    mOwner->writeInt32(1);  // entry count (either url or urn)
+    writeUrlBox();
+    mOwner->endBox();  // dref
+}
+
+void MPEG4Writer::Track::writeDinfBox() {
+    mOwner->beginBox("dinf");
+    writeDrefBox();
+    mOwner->endBox();  // dinf
+}
+
+void MPEG4Writer::Track::writeAvccBox() {
+    CHECK(mCodecSpecificData);
+    CHECK(mCodecSpecificDataSize >= 5);
+
+    // Patch avcc's lengthSize field to match the number
+    // of bytes we use to indicate the size of a nal unit.
+    uint8_t *ptr = (uint8_t *)mCodecSpecificData;
+    ptr[4] = (ptr[4] & 0xfc) | (mOwner->useNalLengthFour() ? 3 : 1);
+    mOwner->beginBox("avcC");
+    mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+    mOwner->endBox();  // avcC
+}
+
+void MPEG4Writer::Track::writeD263Box() {
+    mOwner->beginBox("d263");
+    mOwner->writeInt32(0);  // vendor
+    mOwner->writeInt8(0);   // decoder version
+    mOwner->writeInt8(10);  // level: 10
+    mOwner->writeInt8(0);   // profile: 0
+    mOwner->endBox();  // d263
+}
+
+// This is useful if the pixel is not square
+void MPEG4Writer::Track::writePaspBox() {
+    mOwner->beginBox("pasp");
+    mOwner->writeInt32(1 << 16);  // hspacing
+    mOwner->writeInt32(1 << 16);  // vspacing
+    mOwner->endBox();  // pasp
+}
+
+void MPEG4Writer::Track::writeSttsBox() {
+    mOwner->beginBox("stts");
+    mOwner->writeInt32(0);  // version=0, flags=0
+    mOwner->writeInt32(mNumSttsTableEntries);
 
     // Compensate for small start time difference from different media tracks
     int64_t trackStartTimeOffsetUs = 0;
+    int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
+    if (mStartTimestampUs != moovStartTimeUs) {
+        CHECK(mStartTimestampUs > moovStartTimeUs);
+        trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
+    }
+    int64_t prevTimestampUs = trackStartTimeOffsetUs;
+    for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
+        it != mSttsTableEntries.end(); ++it) {
+        mOwner->writeInt32(it->sampleCount);
 
-    mOwner->beginBox("trak");
+        // Make sure that we are calculating the sample duration the exactly
+        // same way as we made decision on how to create stts entries.
+        int64_t currTimestampUs = prevTimestampUs + it->sampleDurationUs;
+        int32_t dur = ((currTimestampUs * mTimeScale + 500000LL) / 1000000LL -
+            (prevTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+        prevTimestampUs += (it->sampleCount * it->sampleDurationUs);
 
-      mOwner->beginBox("tkhd");
-        // Flags = 7 to indicate that the track is enabled, and
-        // part of the presentation
-        mOwner->writeInt32(0x07);          // version=0, flags=7
-        mOwner->writeInt32(now);           // creation time
-        mOwner->writeInt32(now);           // modification time
-        mOwner->writeInt32(trackID);
-        mOwner->writeInt32(0);             // reserved
-        int32_t tkhdDuration =
-            (trakDurationUs * mvhdTimeScale + 5E5) / 1E6;
-        mOwner->writeInt32(tkhdDuration);  // in mvhd timescale
-        mOwner->writeInt32(0);             // reserved
-        mOwner->writeInt32(0);             // reserved
-        mOwner->writeInt16(0);             // layer
-        mOwner->writeInt16(0);             // alternate group
-        mOwner->writeInt16(mIsAudio ? 0x100 : 0);  // volume
-        mOwner->writeInt16(0);             // reserved
+        mOwner->writeInt32(dur);
+    }
+    mOwner->endBox();  // stts
+}
 
-        mOwner->writeCompositionMatrix(mRotation);       // matrix
+void MPEG4Writer::Track::writeStssBox() {
+    mOwner->beginBox("stss");
+    mOwner->writeInt32(0);  // version=0, flags=0
+    mOwner->writeInt32(mNumStssTableEntries);  // number of sync frames
+    for (List<int32_t>::iterator it = mStssTableEntries.begin();
+        it != mStssTableEntries.end(); ++it) {
+        mOwner->writeInt32(*it);
+    }
+    mOwner->endBox();  // stss
+}
 
-        if (mIsAudio) {
-            mOwner->writeInt32(0);
-            mOwner->writeInt32(0);
-        } else {
-            int32_t width, height;
-            bool success = mMeta->findInt32(kKeyWidth, &width);
-            success = success && mMeta->findInt32(kKeyHeight, &height);
-            CHECK(success);
-
-            mOwner->writeInt32(width << 16);   // 32-bit fixed-point value
-            mOwner->writeInt32(height << 16);  // 32-bit fixed-point value
+void MPEG4Writer::Track::writeStszBox() {
+    mOwner->beginBox("stsz");
+    mOwner->writeInt32(0);  // version=0, flags=0
+    if (mSamplesHaveSameSize) {
+        List<size_t>::iterator it = mSampleSizes.begin();
+        mOwner->writeInt32(*it);  // default sample size
+    } else {
+        mOwner->writeInt32(0);
+    }
+    mOwner->writeInt32(mNumSamples);
+    if (!mSamplesHaveSameSize) {
+        for (List<size_t>::iterator it = mSampleSizes.begin();
+            it != mSampleSizes.end(); ++it) {
+            mOwner->writeInt32(*it);
         }
-      mOwner->endBox();  // tkhd
+    }
+    mOwner->endBox();  // stsz
+}
 
-      int64_t moovStartTimeUs = mOwner->getStartTimestampUs();
-      if (mStartTimestampUs != moovStartTimeUs) {
-          CHECK(mStartTimestampUs > moovStartTimeUs);
-          trackStartTimeOffsetUs = mStartTimestampUs - moovStartTimeUs;
-      }
+void MPEG4Writer::Track::writeStscBox() {
+    mOwner->beginBox("stsc");
+    mOwner->writeInt32(0);  // version=0, flags=0
+    mOwner->writeInt32(mNumStscTableEntries);
+    for (List<StscTableEntry>::iterator it = mStscTableEntries.begin();
+        it != mStscTableEntries.end(); ++it) {
+        mOwner->writeInt32(it->firstChunk);
+        mOwner->writeInt32(it->samplesPerChunk);
+        mOwner->writeInt32(it->sampleDescriptionId);
+    }
+    mOwner->endBox();  // stsc
+}
 
-      mOwner->beginBox("mdia");
-
-        mOwner->beginBox("mdhd");
-          mOwner->writeInt32(0);             // version=0, flags=0
-          mOwner->writeInt32(now);           // creation time
-          mOwner->writeInt32(now);           // modification time
-          mOwner->writeInt32(mTimeScale);    // media timescale
-          int32_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6;
-          mOwner->writeInt32(mdhdDuration);  // use media timescale
-          // Language follows the three letter standard ISO-639-2/T
-          // 'e', 'n', 'g' for "English", for instance.
-          // Each character is packed as the difference between its ASCII value and 0x60.
-          // For "English", these are 00101, 01110, 00111.
-          // XXX: Where is the padding bit located: 0x15C7?
-          mOwner->writeInt16(0);             // language code
-          mOwner->writeInt16(0);             // predefined
-        mOwner->endBox();
-
-        mOwner->beginBox("hdlr");
-          mOwner->writeInt32(0);             // version=0, flags=0
-          mOwner->writeInt32(0);             // component type: should be mhlr
-          mOwner->writeFourcc(mIsAudio ? "soun" : "vide");  // component subtype
-          mOwner->writeInt32(0);             // reserved
-          mOwner->writeInt32(0);             // reserved
-          mOwner->writeInt32(0);             // reserved
-          // Removing "r" for the name string just makes the string 4 byte aligned
-          mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle");  // name
-        mOwner->endBox();
-
-        mOwner->beginBox("minf");
-          if (mIsAudio) {
-              mOwner->beginBox("smhd");
-              mOwner->writeInt32(0);           // version=0, flags=0
-              mOwner->writeInt16(0);           // balance
-              mOwner->writeInt16(0);           // reserved
-              mOwner->endBox();
-          } else {
-              mOwner->beginBox("vmhd");
-              mOwner->writeInt32(0x01);        // version=0, flags=1
-              mOwner->writeInt16(0);           // graphics mode
-              mOwner->writeInt16(0);           // opcolor
-              mOwner->writeInt16(0);
-              mOwner->writeInt16(0);
-              mOwner->endBox();
-          }
-
-          mOwner->beginBox("dinf");
-            mOwner->beginBox("dref");
-              mOwner->writeInt32(0);  // version=0, flags=0
-              mOwner->writeInt32(1);  // entry count (either url or urn)
-              // The table index here refers to the sample description index
-              // in the sample table entries.
-              mOwner->beginBox("url ");
-                mOwner->writeInt32(1);  // version=0, flags=1 (self-contained)
-              mOwner->endBox();  // url
-            mOwner->endBox();  // dref
-          mOwner->endBox();  // dinf
-
-        mOwner->beginBox("stbl");
-
-          mOwner->beginBox("stsd");
-            mOwner->writeInt32(0);               // version=0, flags=0
-            mOwner->writeInt32(1);               // entry count
-            if (mIsAudio) {
-                const char *fourcc = NULL;
-                if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
-                    fourcc = "samr";
-                } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
-                    fourcc = "sawb";
-                } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
-                    fourcc = "mp4a";
-                } else {
-                    LOGE("Unknown mime type '%s'.", mime);
-                    CHECK(!"should not be here, unknown mime type.");
-                }
-
-                mOwner->beginBox(fourcc);          // audio format
-                  mOwner->writeInt32(0);           // reserved
-                  mOwner->writeInt16(0);           // reserved
-                  mOwner->writeInt16(0x1);         // data ref index
-                  mOwner->writeInt32(0);           // reserved
-                  mOwner->writeInt32(0);           // reserved
-                  int32_t nChannels;
-                  CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels));
-                  mOwner->writeInt16(nChannels);   // channel count
-                  mOwner->writeInt16(16);          // sample size
-                  mOwner->writeInt16(0);           // predefined
-                  mOwner->writeInt16(0);           // reserved
-
-                  int32_t samplerate;
-                  bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
-                  CHECK(success);
-                  mOwner->writeInt32(samplerate << 16);
-                  if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
-                    mOwner->beginBox("esds");
-                        CHECK(mCodecSpecificData);
-                        CHECK(mCodecSpecificDataSize > 0);
-
-                        // Make sure all sizes encode to a single byte.
-                        CHECK(mCodecSpecificDataSize + 23 < 128);
-
-                        mOwner->writeInt32(0);     // version=0, flags=0
-                        mOwner->writeInt8(0x03);   // ES_DescrTag
-                        mOwner->writeInt8(23 + mCodecSpecificDataSize);
-                        mOwner->writeInt16(0x0000);// ES_ID
-                        mOwner->writeInt8(0x00);
-
-                        mOwner->writeInt8(0x04);   // DecoderConfigDescrTag
-                        mOwner->writeInt8(15 + mCodecSpecificDataSize);
-                        mOwner->writeInt8(0x40);   // objectTypeIndication ISO/IEC 14492-2
-                        mOwner->writeInt8(0x15);   // streamType AudioStream
-
-                        mOwner->writeInt16(0x03);  // XXX
-                        mOwner->writeInt8(0x00);   // buffer size 24-bit
-                        mOwner->writeInt32(96000); // max bit rate
-                        mOwner->writeInt32(96000); // avg bit rate
-
-                        mOwner->writeInt8(0x05);   // DecoderSpecificInfoTag
-                        mOwner->writeInt8(mCodecSpecificDataSize);
-                        mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
-
-                        static const uint8_t kData2[] = {
-                            0x06,  // SLConfigDescriptorTag
-                            0x01,
-                            0x02
-                        };
-                        mOwner->write(kData2, sizeof(kData2));
-
-                    mOwner->endBox();  // esds
-                  } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime) ||
-                             !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
-                    // 3gpp2 Spec AMRSampleEntry fields
-                    mOwner->beginBox("damr");
-                      mOwner->writeCString("   ");  // vendor: 4 bytes
-                      mOwner->writeInt8(0);         // decoder version
-                      mOwner->writeInt16(0x83FF);   // mode set: all enabled
-                      mOwner->writeInt8(0);         // mode change period
-                      mOwner->writeInt8(1);         // frames per sample
-                    mOwner->endBox();
-                  }
-                mOwner->endBox();
-            } else {
-                if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
-                    mOwner->beginBox("mp4v");
-                } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
-                    mOwner->beginBox("s263");
-                } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
-                    mOwner->beginBox("avc1");
-                } else {
-                    LOGE("Unknown mime type '%s'.", mime);
-                    CHECK(!"should not be here, unknown mime type.");
-                }
-
-                  mOwner->writeInt32(0);           // reserved
-                  mOwner->writeInt16(0);           // reserved
-                  mOwner->writeInt16(1);           // data ref index
-                  mOwner->writeInt16(0);           // predefined
-                  mOwner->writeInt16(0);           // reserved
-                  mOwner->writeInt32(0);           // predefined
-                  mOwner->writeInt32(0);           // predefined
-                  mOwner->writeInt32(0);           // predefined
-
-                  int32_t width, height;
-                  bool success = mMeta->findInt32(kKeyWidth, &width);
-                  success = success && mMeta->findInt32(kKeyHeight, &height);
-                  CHECK(success);
-
-                  mOwner->writeInt16(width);
-                  mOwner->writeInt16(height);
-                  mOwner->writeInt32(0x480000);    // horiz resolution
-                  mOwner->writeInt32(0x480000);    // vert resolution
-                  mOwner->writeInt32(0);           // reserved
-                  mOwner->writeInt16(1);           // frame count
-                  mOwner->write("                                ", 32);
-                  mOwner->writeInt16(0x18);        // depth
-                  mOwner->writeInt16(-1);          // predefined
-
-                  CHECK(23 + mCodecSpecificDataSize < 128);
-
-                  if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
-                      CHECK(mCodecSpecificData);
-                      CHECK(mCodecSpecificDataSize > 0);
-                      mOwner->beginBox("esds");
-
-                        mOwner->writeInt32(0);           // version=0, flags=0
-
-                        mOwner->writeInt8(0x03);  // ES_DescrTag
-                        mOwner->writeInt8(23 + mCodecSpecificDataSize);
-                        mOwner->writeInt16(0x0000);  // ES_ID
-                        mOwner->writeInt8(0x1f);
-
-                        mOwner->writeInt8(0x04);  // DecoderConfigDescrTag
-                        mOwner->writeInt8(15 + mCodecSpecificDataSize);
-                        mOwner->writeInt8(0x20);  // objectTypeIndication ISO/IEC 14492-2
-                        mOwner->writeInt8(0x11);  // streamType VisualStream
-
-                        static const uint8_t kData[] = {
-                            0x01, 0x77, 0x00,
-                            0x00, 0x03, 0xe8, 0x00,
-                            0x00, 0x03, 0xe8, 0x00
-                        };
-                        mOwner->write(kData, sizeof(kData));
-
-                        mOwner->writeInt8(0x05);  // DecoderSpecificInfoTag
-
-                        mOwner->writeInt8(mCodecSpecificDataSize);
-                        mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
-
-                        static const uint8_t kData2[] = {
-                            0x06,  // SLConfigDescriptorTag
-                            0x01,
-                            0x02
-                        };
-                        mOwner->write(kData2, sizeof(kData2));
-
-                      mOwner->endBox();  // esds
-                  } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
-                      mOwner->beginBox("d263");
-
-                          mOwner->writeInt32(0);  // vendor
-                          mOwner->writeInt8(0);   // decoder version
-                          mOwner->writeInt8(10);  // level: 10
-                          mOwner->writeInt8(0);   // profile: 0
-
-                      mOwner->endBox();  // d263
-                  } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
-                      CHECK(mCodecSpecificData);
-                      CHECK(mCodecSpecificDataSize >= 5);
-
-                      // Patch avcc's lengthSize field to match the number
-                      // of bytes we use to indicate the size of a nal unit.
-                      uint8_t *ptr = (uint8_t *)mCodecSpecificData;
-                      ptr[4] =
-                          (ptr[4] & 0xfc)
-                            | (mOwner->useNalLengthFour() ? 3 : 1);
-
-                      mOwner->beginBox("avcC");
-                        mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
-                      mOwner->endBox();  // avcC
-                  }
-
-                  mOwner->beginBox("pasp");
-                    // This is useful if the pixel is not square
-                    mOwner->writeInt32(1 << 16);  // hspacing
-                    mOwner->writeInt32(1 << 16);  // vspacing
-                  mOwner->endBox();  // pasp
-                mOwner->endBox();  // mp4v, s263 or avc1
-            }
-          mOwner->endBox();  // stsd
-
-          mOwner->beginBox("stts");
-            mOwner->writeInt32(0);  // version=0, flags=0
-            mOwner->writeInt32(mNumSttsTableEntries);
-            int64_t prevTimestampUs = trackStartTimeOffsetUs;
-            for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
-                 it != mSttsTableEntries.end(); ++it) {
-                mOwner->writeInt32(it->sampleCount);
-
-                // Make sure that we are calculating the sample duration the exactly
-                // same way as we made decision on how to create stts entries.
-                int64_t currTimestampUs = prevTimestampUs + it->sampleDurationUs;
-                int32_t dur = ((currTimestampUs * mTimeScale + 500000LL) / 1000000LL -
-                               (prevTimestampUs * mTimeScale + 500000LL) / 1000000LL);
-                prevTimestampUs += (it->sampleCount * it->sampleDurationUs);
-
-                mOwner->writeInt32(dur);
-            }
-          mOwner->endBox();  // stts
-
-          if (!mIsAudio) {
-            mOwner->beginBox("stss");
-              mOwner->writeInt32(0);  // version=0, flags=0
-              mOwner->writeInt32(mNumStssTableEntries);  // number of sync frames
-              for (List<int32_t>::iterator it = mStssTableEntries.begin();
-                   it != mStssTableEntries.end(); ++it) {
-                  mOwner->writeInt32(*it);
-              }
-            mOwner->endBox();  // stss
-          }
-
-          mOwner->beginBox("stsz");
-            mOwner->writeInt32(0);  // version=0, flags=0
-            if (mSamplesHaveSameSize) {
-                List<size_t>::iterator it = mSampleSizes.begin();
-                mOwner->writeInt32(*it);  // default sample size
-            } else {
-                mOwner->writeInt32(0);
-            }
-            mOwner->writeInt32(mNumSamples);
-            if (!mSamplesHaveSameSize) {
-                for (List<size_t>::iterator it = mSampleSizes.begin();
-                     it != mSampleSizes.end(); ++it) {
-                    mOwner->writeInt32(*it);
-                }
-            }
-          mOwner->endBox();  // stsz
-
-          mOwner->beginBox("stsc");
-            mOwner->writeInt32(0);  // version=0, flags=0
-            mOwner->writeInt32(mNumStscTableEntries);
-            for (List<StscTableEntry>::iterator it = mStscTableEntries.begin();
-                 it != mStscTableEntries.end(); ++it) {
-                mOwner->writeInt32(it->firstChunk);
-                mOwner->writeInt32(it->samplesPerChunk);
-                mOwner->writeInt32(it->sampleDescriptionId);
-            }
-          mOwner->endBox();  // stsc
-          mOwner->beginBox(use32BitOffset? "stco": "co64");
-            mOwner->writeInt32(0);  // version=0, flags=0
-            mOwner->writeInt32(mNumStcoTableEntries);
-            for (List<off64_t>::iterator it = mChunkOffsets.begin();
-                 it != mChunkOffsets.end(); ++it) {
-                if (use32BitOffset) {
-                    mOwner->writeInt32(static_cast<int32_t>(*it));
-                } else {
-                    mOwner->writeInt64((*it));
-                }
-            }
-          mOwner->endBox();  // stco or co64
-
-        mOwner->endBox();  // stbl
-       mOwner->endBox();  // minf
-      mOwner->endBox();  // mdia
-    mOwner->endBox();  // trak
+void MPEG4Writer::Track::writeStcoBox(bool use32BitOffset) {
+    mOwner->beginBox(use32BitOffset? "stco": "co64");
+    mOwner->writeInt32(0);  // version=0, flags=0
+    mOwner->writeInt32(mNumStcoTableEntries);
+    for (List<off64_t>::iterator it = mChunkOffsets.begin();
+        it != mChunkOffsets.end(); ++it) {
+        if (use32BitOffset) {
+            mOwner->writeInt32(static_cast<int32_t>(*it));
+        } else {
+            mOwner->writeInt64((*it));
+        }
+    }
+    mOwner->endBox();  // stco or co64
 }
 
 }  // namespace android
diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp
index 884f3b4..aac94a6 100644
--- a/media/libstagefright/MetaData.cpp
+++ b/media/libstagefright/MetaData.cpp
@@ -83,7 +83,7 @@
     return setData(key, TYPE_RECT, &r, sizeof(r));
 }
 
-bool MetaData::findCString(uint32_t key, const char **value) {
+bool MetaData::findCString(uint32_t key, const char **value) const {
     uint32_t type;
     const void *data;
     size_t size;
@@ -96,7 +96,7 @@
     return true;
 }
 
-bool MetaData::findInt32(uint32_t key, int32_t *value) {
+bool MetaData::findInt32(uint32_t key, int32_t *value) const {
     uint32_t type;
     const void *data;
     size_t size;
@@ -111,7 +111,7 @@
     return true;
 }
 
-bool MetaData::findInt64(uint32_t key, int64_t *value) {
+bool MetaData::findInt64(uint32_t key, int64_t *value) const {
     uint32_t type;
     const void *data;
     size_t size;
@@ -126,7 +126,7 @@
     return true;
 }
 
-bool MetaData::findFloat(uint32_t key, float *value) {
+bool MetaData::findFloat(uint32_t key, float *value) const {
     uint32_t type;
     const void *data;
     size_t size;
@@ -141,7 +141,7 @@
     return true;
 }
 
-bool MetaData::findPointer(uint32_t key, void **value) {
+bool MetaData::findPointer(uint32_t key, void **value) const {
     uint32_t type;
     const void *data;
     size_t size;
@@ -159,7 +159,7 @@
 bool MetaData::findRect(
         uint32_t key,
         int32_t *left, int32_t *top,
-        int32_t *right, int32_t *bottom) {
+        int32_t *right, int32_t *bottom) const {
     uint32_t type;
     const void *data;
     size_t size;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 78d13b2..cd5bdfb 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -53,7 +53,10 @@
 #include <OMX_Audio.h>
 #include <OMX_Component.h>
 
+#if HAVE_SOFTWARE_DECODERS
 #include "include/ThreadedSource.h"
+#endif
+
 #include "include/avc_utils.h"
 
 namespace android {
@@ -65,11 +68,6 @@
     const char *codec;
 };
 
-#define FACTORY_CREATE(name) \
-static sp<MediaSource> Make##name(const sp<MediaSource> &source) { \
-    return new name(source); \
-}
-
 #define FACTORY_CREATE_ENCODER(name) \
 static sp<MediaSource> Make##name(const sp<MediaSource> &source, const sp<MetaData> &meta) { \
     return new name(source, meta); \
@@ -77,21 +75,30 @@
 
 #define FACTORY_REF(name) { #name, Make##name },
 
-FACTORY_CREATE(MP3Decoder)
-FACTORY_CREATE(AMRNBDecoder)
-FACTORY_CREATE(AMRWBDecoder)
-FACTORY_CREATE(AACDecoder)
-FACTORY_CREATE(AVCDecoder)
-FACTORY_CREATE(G711Decoder)
-FACTORY_CREATE(M4vH263Decoder)
-FACTORY_CREATE(VorbisDecoder)
-FACTORY_CREATE(VPXDecoder)
 FACTORY_CREATE_ENCODER(AMRNBEncoder)
 FACTORY_CREATE_ENCODER(AMRWBEncoder)
 FACTORY_CREATE_ENCODER(AACEncoder)
 FACTORY_CREATE_ENCODER(AVCEncoder)
 FACTORY_CREATE_ENCODER(M4vH263Encoder)
 
+#if HAVE_SOFTWARE_DECODERS
+
+#define FACTORY_CREATE(name) \
+static sp<MediaSource> Make##name(const sp<MediaSource> &source) { \
+    return new name(source); \
+}
+
+FACTORY_CREATE(AMRNBDecoder)
+FACTORY_CREATE(AMRWBDecoder)
+FACTORY_CREATE(AACDecoder)
+FACTORY_CREATE(AVCDecoder)
+FACTORY_CREATE(G711Decoder)
+FACTORY_CREATE(MP3Decoder)
+FACTORY_CREATE(M4vH263Decoder)
+FACTORY_CREATE(VorbisDecoder)
+FACTORY_CREATE(VPXDecoder)
+#endif
+
 static sp<MediaSource> InstantiateSoftwareEncoder(
         const char *name, const sp<MediaSource> &source,
         const sp<MetaData> &meta) {
@@ -119,18 +126,19 @@
 
 static sp<MediaSource> InstantiateSoftwareCodec(
         const char *name, const sp<MediaSource> &source) {
+#if HAVE_SOFTWARE_DECODERS
     struct FactoryInfo {
         const char *name;
         sp<MediaSource> (*CreateFunc)(const sp<MediaSource> &);
     };
 
     static const FactoryInfo kFactoryInfo[] = {
-        FACTORY_REF(MP3Decoder)
         FACTORY_REF(AMRNBDecoder)
         FACTORY_REF(AMRWBDecoder)
         FACTORY_REF(AACDecoder)
         FACTORY_REF(AVCDecoder)
         FACTORY_REF(G711Decoder)
+        FACTORY_REF(MP3Decoder)
         FACTORY_REF(M4vH263Decoder)
         FACTORY_REF(VorbisDecoder)
         FACTORY_REF(VPXDecoder)
@@ -145,6 +153,7 @@
             return (*kFactoryInfo[i].CreateFunc)(source);
         }
     }
+#endif
 
     return NULL;
 }
@@ -156,36 +165,47 @@
     { MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },
 //    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.Nvidia.mp3.decoder" },
 //    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },
+    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.google.mp3.decoder" },
     { MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" },
 //    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" },
 //    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amr.decoder" },
+    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.google.amrnb.decoder" },
     { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder" },
 //    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amrwb.decoder" },
     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" },
+    { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.google.amrwb.decoder" },
     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder" },
 //    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.Nvidia.aac.decoder" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
+    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.decoder" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },
+    { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "OMX.google.g711.alaw.decoder" },
     { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" },
+    { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "OMX.google.g711.mlaw.decoder" },
     { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.Nvidia.mp4.decode" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Decoder" },
+    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.google.mpeg4.decoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.Nvidia.h263.decode" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.decoder.h263" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Decoder" },
+    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.google.h263.decoder" },
     { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.Nvidia.h264.decode" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.decoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.SEC.AVC.Decoder" },
+    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.google.avc.decoder" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" },
+    { MEDIA_MIMETYPE_AUDIO_VORBIS, "OMX.google.vorbis.decoder" },
     { MEDIA_MIMETYPE_AUDIO_VORBIS, "VorbisDecoder" },
+    { MEDIA_MIMETYPE_VIDEO_VPX, "OMX.google.vpx.decoder" },
     { MEDIA_MIMETYPE_VIDEO_VPX, "VPXDecoder" },
 };
 
@@ -277,6 +297,10 @@
 }
 
 static bool IsSoftwareCodec(const char *componentName) {
+    if (!strncmp("OMX.google.", componentName, 11)) {
+        return true;
+    }
+
     if (!strncmp("OMX.", componentName, 4)) {
         return false;
     }
@@ -284,26 +308,29 @@
     return true;
 }
 
-// A sort order in which non-OMX components are first,
-// followed by software codecs, and followed by all the others.
+// A sort order in which OMX software codecs are first, followed
+// by other (non-OMX) software codecs, followed by everything else.
 static int CompareSoftwareCodecsFirst(
         const String8 *elem1, const String8 *elem2) {
-    bool isNotOMX1 = strncmp(elem1->string(), "OMX.", 4);
-    bool isNotOMX2 = strncmp(elem2->string(), "OMX.", 4);
-
-    if (isNotOMX1) {
-        if (isNotOMX2) { return 0; }
-        return -1;
-    }
-    if (isNotOMX2) {
-        return 1;
-    }
+    bool isOMX1 = !strncmp(elem1->string(), "OMX.", 4);
+    bool isOMX2 = !strncmp(elem2->string(), "OMX.", 4);
 
     bool isSoftwareCodec1 = IsSoftwareCodec(elem1->string());
     bool isSoftwareCodec2 = IsSoftwareCodec(elem2->string());
 
     if (isSoftwareCodec1) {
-        if (isSoftwareCodec2) { return 0; }
+        if (!isSoftwareCodec2) { return -1; }
+
+        if (isOMX1) {
+            if (isOMX2) { return 0; }
+
+            return -1;
+        } else {
+            if (isOMX2) { return 0; }
+
+            return 1;
+        }
+
         return -1;
     }
 
@@ -622,6 +649,11 @@
                 LOGE("Profile and/or level exceed the decoder's capabilities.");
                 return ERROR_UNSUPPORTED;
             }
+        } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
+            addCodecSpecificData(data, size);
+
+            CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size));
+            addCodecSpecificData(data, size);
         }
     }
 
@@ -631,16 +663,23 @@
     }
     if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mMIME)) {
         setAMRFormat(false /* isWAMR */, bitRate);
-    }
-    if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mMIME)) {
+    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mMIME)) {
         setAMRFormat(true /* isWAMR */, bitRate);
-    }
-    if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mMIME)) {
+    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mMIME)) {
         int32_t numChannels, sampleRate;
         CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
         CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
 
         setAACFormat(numChannels, sampleRate, bitRate);
+    } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_ALAW, mMIME)
+            || !strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_MLAW, mMIME)) {
+        // These are PCM-like formats with a fixed sample rate but
+        // a variable number of channels.
+
+        int32_t numChannels;
+        CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+
+        setG711Format(numChannels);
     }
 
     if (!strncasecmp(mMIME, "video/", 6)) {
@@ -1316,6 +1355,8 @@
         compressionFormat = OMX_VIDEO_CodingMPEG4;
     } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
         compressionFormat = OMX_VIDEO_CodingH263;
+    } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VPX, mime)) {
+        compressionFormat = OMX_VIDEO_CodingVPX;
     } else {
         LOGE("Not a supported video mime type: %s", mime);
         CHECK(!"Should not be here. Not a supported video mime type.");
@@ -1443,7 +1484,8 @@
       mOutputPortSettingsChangedPending(false),
       mLeftOverBuffer(NULL),
       mPaused(false),
-      mNativeWindow(nativeWindow) {
+      mNativeWindow(!strncmp(componentName, "OMX.google.", 11)
+                        ? NULL : nativeWindow) {
     mPortStatus[kPortIndexInput] = ENABLED;
     mPortStatus[kPortIndexOutput] = ENABLED;
 
@@ -2899,6 +2941,23 @@
 
         offset += srcBuffer->range_length();
 
+        if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_VORBIS, mMIME)) {
+            CHECK(!(mQuirks & kSupportsMultipleFramesPerInputBuffer));
+            CHECK_GE(info->mSize, offset + sizeof(int32_t));
+
+            int32_t numPageSamples;
+            if (!srcBuffer->meta_data()->findInt32(
+                        kKeyValidSamples, &numPageSamples)) {
+                numPageSamples = -1;
+            }
+
+            memcpy((uint8_t *)info->mData + offset,
+                   &numPageSamples,
+                   sizeof(numPageSamples));
+
+            offset += sizeof(numPageSamples);
+        }
+
         if (releaseBuffer) {
             srcBuffer->release();
             srcBuffer = NULL;
@@ -3224,6 +3283,11 @@
     }
 }
 
+void OMXCodec::setG711Format(int32_t numChannels) {
+    CHECK(!mIsEncoder);
+    setRawAudioFormat(kPortIndexInput, 8000, numChannels);
+}
+
 void OMXCodec::setImageOutputFormat(
         OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height) {
     CODEC_LOGV("setImageOutputFormat(%ld, %ld)", width, height);
@@ -4013,6 +4077,13 @@
                          numChannels, params.nChannels);
                 }
 
+                if (sampleRate != params.nSamplingRate) {
+                    LOGW("Codec outputs at different sampling rate than "
+                         "what the input stream contains (contains data at "
+                         "%d Hz, codec outputs %d Hz)",
+                         sampleRate, params.nSamplingRate);
+                }
+
                 mOutputFormat->setCString(
                         kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
 
@@ -4025,8 +4096,7 @@
                         (mQuirks & kDecoderLiesAboutNumberOfChannels)
                             ? numChannels : params.nChannels);
 
-                // The codec-reported sampleRate is not reliable...
-                mOutputFormat->setInt32(kKeySampleRate, sampleRate);
+                mOutputFormat->setInt32(kKeySampleRate, params.nSamplingRate);
             } else if (audio_def->eEncoding == OMX_AUDIO_CodingAMR) {
                 OMX_AUDIO_PARAM_AMRTYPE amr;
                 InitOMXParams(&amr);
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 6538a05..1560b8e 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -378,12 +378,19 @@
 
 ssize_t MyVorbisExtractor::readPage(off64_t offset, Page *page) {
     uint8_t header[27];
-    if (mSource->readAt(offset, header, sizeof(header))
+    ssize_t n;
+    if ((n = mSource->readAt(offset, header, sizeof(header)))
             < (ssize_t)sizeof(header)) {
-        LOGV("failed to read %d bytes at offset 0x%016llx",
-             sizeof(header), offset);
+        LOGV("failed to read %d bytes at offset 0x%016llx, got %ld bytes",
+             sizeof(header), offset, n);
 
-        return ERROR_IO;
+        if (n < 0) {
+            return n;
+        } else if (n == 0) {
+            return ERROR_END_OF_STREAM;
+        } else {
+            return ERROR_IO;
+        }
     }
 
     if (memcmp(header, "OggS", 4)) {
@@ -498,8 +505,8 @@
                     packetSize);
 
             if (n < (ssize_t)packetSize) {
-                LOGV("failed to read %d bytes at 0x%016llx",
-                     packetSize, dataOffset);
+                LOGV("failed to read %d bytes at 0x%016llx, got %ld bytes",
+                     packetSize, dataOffset, n);
                 return ERROR_IO;
             }
 
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 76f47f7..bf978d7 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -264,6 +264,8 @@
       mGroup(NULL) {
     CHECK(mMeta->findInt32(kKeySampleRate, &mSampleRate));
     CHECK(mMeta->findInt32(kKeyChannelCount, &mNumChannels));
+
+    mMeta->setInt32(kKeyMaxInputSize, kMaxFrameSize);
 }
 
 WAVSource::~WAVSource() {
diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk
index 69e331f..5ef54fd 100644
--- a/media/libstagefright/codecs/aacdec/Android.mk
+++ b/media/libstagefright/codecs/aacdec/Android.mk
@@ -143,14 +143,39 @@
  	unpack_idx.cpp \
  	window_tables_fxp.cpp \
  	pvmp4setaudioconfig.cpp \
-        AACDecoder.cpp
+        AACDecoder.cpp \
 
 LOCAL_CFLAGS := -DAAC_PLUS -DHQ_SBR -DPARAMETRICSTEREO -DOSCL_IMPORT_REF= -DOSCL_EXPORT_REF= -DOSCL_UNUSED_ARG=
 
-LOCAL_C_INCLUDES := frameworks/base/media/libstagefright/include
+LOCAL_C_INCLUDES := \
+        frameworks/base/media/libstagefright/include \
 
 LOCAL_ARM_MODE := arm
 
 LOCAL_MODULE := libstagefright_aacdec
 
 include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        SoftAAC.cpp
+
+LOCAL_C_INCLUDES := \
+        frameworks/base/media/libstagefright/include \
+        frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
+
+LOCAL_STATIC_LIBRARIES := \
+        libstagefright_aacdec
+
+LOCAL_SHARED_LIBRARIES := \
+        libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_aacdec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.cpp b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
new file mode 100644
index 0000000..7ce6128
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
@@ -0,0 +1,449 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "SoftAAC"
+#include <utils/Log.h>
+
+#include "SoftAAC.h"
+
+#include "pvmp4audiodecoder_api.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+SoftAAC::SoftAAC(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SimpleSoftOMXComponent(name, callbacks, appData, component),
+      mConfig(new tPVMP4AudioDecoderExternal),
+      mDecoderBuf(NULL),
+      mInputBufferCount(0),
+      mUpsamplingFactor(2),
+      mAnchorTimeUs(0),
+      mNumSamplesOutput(0),
+      mSignalledError(false),
+      mOutputPortSettingsChange(NONE) {
+    initPorts();
+    CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftAAC::~SoftAAC() {
+    free(mDecoderBuf);
+    mDecoderBuf = NULL;
+
+    delete mConfig;
+    mConfig = NULL;
+}
+
+void SoftAAC::initPorts() {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+
+    def.nPortIndex = 0;
+    def.eDir = OMX_DirInput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = 8192;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 1;
+
+    def.format.audio.cMIMEType = const_cast<char *>("audio/aac");
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
+
+    addPort(def);
+
+    def.nPortIndex = 1;
+    def.eDir = OMX_DirOutput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = 8192;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 2;
+
+    def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+    addPort(def);
+}
+
+status_t SoftAAC::initDecoder() {
+    memset(mConfig, 0, sizeof(tPVMP4AudioDecoderExternal));
+    mConfig->outputFormat = OUTPUTFORMAT_16PCM_INTERLEAVED;
+    mConfig->aacPlusEnabled = 1;
+
+    // The software decoder doesn't properly support mono output on
+    // AACplus files. Always output stereo.
+    mConfig->desiredChannels = 2;
+
+    UInt32 memRequirements = PVMP4AudioDecoderGetMemRequirements();
+    mDecoderBuf = malloc(memRequirements);
+
+    Int err = PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf);
+    if (err != MP4AUDEC_SUCCESS) {
+        LOGE("Failed to initialize MP4 audio decoder");
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+OMX_ERRORTYPE SoftAAC::internalGetParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamAudioAac:
+        {
+            OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
+                (OMX_AUDIO_PARAM_AACPROFILETYPE *)params;
+
+            if (aacParams->nPortIndex != 0) {
+                return OMX_ErrorUndefined;
+            }
+
+            aacParams->nBitRate = 0;
+            aacParams->nAudioBandWidth = 0;
+            aacParams->nAACtools = 0;
+            aacParams->nAACERtools = 0;
+            aacParams->eAACProfile = OMX_AUDIO_AACObjectMain;
+            aacParams->eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
+            aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo;
+
+            if (!isConfigured()) {
+                aacParams->nChannels = 1;
+                aacParams->nSampleRate = 44100;
+                aacParams->nFrameLength = 0;
+            } else {
+                aacParams->nChannels = mConfig->encodedChannels;
+                aacParams->nSampleRate = mConfig->samplingRate;
+                aacParams->nFrameLength = mConfig->frameLength;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamAudioPcm:
+        {
+            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+            if (pcmParams->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            pcmParams->eNumData = OMX_NumericalDataSigned;
+            pcmParams->eEndian = OMX_EndianBig;
+            pcmParams->bInterleaved = OMX_TRUE;
+            pcmParams->nBitPerSample = 16;
+            pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+            pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+            pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+            if (!isConfigured()) {
+                pcmParams->nChannels = 1;
+                pcmParams->nSamplingRate = 44100;
+            } else {
+                pcmParams->nChannels = mConfig->desiredChannels;
+                pcmParams->nSamplingRate = mConfig->samplingRate;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalGetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftAAC::internalSetParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamStandardComponentRole:
+        {
+            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+                (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+            if (strncmp((const char *)roleParams->cRole,
+                        "audio_decoder.aac",
+                        OMX_MAX_STRINGNAME_SIZE - 1)) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamAudioAac:
+        {
+            const OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
+                (const OMX_AUDIO_PARAM_AACPROFILETYPE *)params;
+
+            if (aacParams->nPortIndex != 0) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalSetParameter(index, params);
+    }
+}
+
+bool SoftAAC::isConfigured() const {
+    return mInputBufferCount > 0;
+}
+
+void SoftAAC::onQueueFilled(OMX_U32 portIndex) {
+    if (mSignalledError || mOutputPortSettingsChange != NONE) {
+        return;
+    }
+
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+
+    if (portIndex == 0 && mInputBufferCount == 0) {
+        ++mInputBufferCount;
+
+        BufferInfo *info = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *header = info->mHeader;
+
+        mConfig->pInputBuffer = header->pBuffer + header->nOffset;
+        mConfig->inputBufferCurrentLength = header->nFilledLen;
+        mConfig->inputBufferMaxLength = 0;
+
+        Int err = PVMP4AudioDecoderConfig(mConfig, mDecoderBuf);
+        if (err != MP4AUDEC_SUCCESS) {
+            mSignalledError = true;
+            notify(OMX_EventError, OMX_ErrorUndefined, err, NULL);
+            return;
+        }
+
+        inQueue.erase(inQueue.begin());
+        info->mOwnedByUs = false;
+        notifyEmptyBufferDone(header);
+
+        notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+        mOutputPortSettingsChange = AWAITING_DISABLED;
+        return;
+    }
+
+    while (!inQueue.empty() && !outQueue.empty()) {
+        BufferInfo *inInfo = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+        BufferInfo *outInfo = *outQueue.begin();
+        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+            inQueue.erase(inQueue.begin());
+            inInfo->mOwnedByUs = false;
+            notifyEmptyBufferDone(inHeader);
+
+            outHeader->nFilledLen = 0;
+            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+            outQueue.erase(outQueue.begin());
+            outInfo->mOwnedByUs = false;
+            notifyFillBufferDone(outHeader);
+            return;
+        }
+
+        if (inHeader->nOffset == 0) {
+            mAnchorTimeUs = inHeader->nTimeStamp;
+            mNumSamplesOutput = 0;
+        }
+
+        mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset;
+        mConfig->inputBufferCurrentLength = inHeader->nFilledLen;
+        mConfig->inputBufferMaxLength = 0;
+        mConfig->inputBufferUsedLength = 0;
+        mConfig->remainderBits = 0;
+
+        mConfig->pOutputBuffer =
+            reinterpret_cast<Int16 *>(outHeader->pBuffer + outHeader->nOffset);
+
+        mConfig->pOutputBuffer_plus = &mConfig->pOutputBuffer[2048];
+        mConfig->repositionFlag = false;
+
+        Int32 prevSamplingRate = mConfig->samplingRate;
+        Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf);
+
+        /*
+         * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+         * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+         * rate system and the sampling rate in the final output is actually
+         * doubled compared with the core AAC decoder sampling rate.
+         *
+         * Explicit signalling is done by explicitly defining SBR audio object
+         * type in the bitstream. Implicit signalling is done by embedding
+         * SBR content in AAC extension payload specific to SBR, and hence
+         * requires an AAC decoder to perform pre-checks on actual audio frames.
+         *
+         * Thus, we could not say for sure whether a stream is
+         * AAC+/eAAC+ until the first data frame is decoded.
+         */
+        if (mInputBufferCount <= 2) {
+            LOGV("audio/extended audio object type: %d + %d",
+                mConfig->audioObjectType, mConfig->extendedAudioObjectType);
+            LOGV("aac+ upsampling factor: %d desired channels: %d",
+                mConfig->aacPlusUpsamplingFactor, mConfig->desiredChannels);
+
+            if (mInputBufferCount == 1) {
+                mUpsamplingFactor = mConfig->aacPlusUpsamplingFactor;
+                // Check on the sampling rate to see whether it is changed.
+                if (mConfig->samplingRate != prevSamplingRate) {
+                    LOGW("Sample rate was %d Hz, but now is %d Hz",
+                            prevSamplingRate, mConfig->samplingRate);
+
+                    // We'll hold onto the input buffer and will decode
+                    // it again once the output port has been reconfigured.
+
+                    notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+                    mOutputPortSettingsChange = AWAITING_DISABLED;
+                    return;
+                }
+            } else {  // mInputBufferCount == 2
+                if (mConfig->extendedAudioObjectType == MP4AUDIO_AAC_LC ||
+                    mConfig->extendedAudioObjectType == MP4AUDIO_LTP) {
+                    if (mUpsamplingFactor == 2) {
+                        // The stream turns out to be not aacPlus mode anyway
+                        LOGW("Disable AAC+/eAAC+ since extended audio object "
+                             "type is %d",
+                             mConfig->extendedAudioObjectType);
+                        mConfig->aacPlusEnabled = 0;
+                    }
+                } else {
+                    if (mUpsamplingFactor == 1) {
+                        // aacPlus mode does not buy us anything, but to cause
+                        // 1. CPU load to increase, and
+                        // 2. a half speed of decoding
+                        LOGW("Disable AAC+/eAAC+ since upsampling factor is 1");
+                        mConfig->aacPlusEnabled = 0;
+                    }
+                }
+            }
+        }
+
+        size_t numOutBytes =
+            mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels;
+
+        if (decoderErr == MP4AUDEC_SUCCESS) {
+            CHECK_LE(mConfig->inputBufferUsedLength, inHeader->nFilledLen);
+
+            inHeader->nFilledLen -= mConfig->inputBufferUsedLength;
+            inHeader->nOffset += mConfig->inputBufferUsedLength;
+        } else {
+            memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes);
+        }
+
+        if (mUpsamplingFactor == 2) {
+            if (mConfig->desiredChannels == 1) {
+                memcpy(&mConfig->pOutputBuffer[1024],
+                       &mConfig->pOutputBuffer[2048],
+                       numOutBytes * 2);
+            }
+            numOutBytes *= 2;
+        }
+
+        outHeader->nFilledLen = numOutBytes;
+        outHeader->nFlags = 0;
+
+        outHeader->nTimeStamp =
+            mAnchorTimeUs
+                + (mNumSamplesOutput * 1000000ll) / mConfig->samplingRate;
+
+        mNumSamplesOutput += mConfig->frameLength * mUpsamplingFactor;
+
+        if (inHeader->nFilledLen == 0) {
+            inInfo->mOwnedByUs = false;
+            inQueue.erase(inQueue.begin());
+            inInfo = NULL;
+            notifyEmptyBufferDone(inHeader);
+            inHeader = NULL;
+        }
+
+        outInfo->mOwnedByUs = false;
+        outQueue.erase(outQueue.begin());
+        outInfo = NULL;
+        notifyFillBufferDone(outHeader);
+        outHeader = NULL;
+
+        ++mInputBufferCount;
+    }
+}
+
+void SoftAAC::onPortFlushCompleted(OMX_U32 portIndex) {
+    if (portIndex == 0) {
+        // Make sure that the next buffer output does not still
+        // depend on fragments from the last one decoded.
+        PVMP4AudioDecoderResetBuffer(mDecoderBuf);
+    }
+}
+
+void SoftAAC::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+    if (portIndex != 1) {
+        return;
+    }
+
+    switch (mOutputPortSettingsChange) {
+        case NONE:
+            break;
+
+        case AWAITING_DISABLED:
+        {
+            CHECK(!enabled);
+            mOutputPortSettingsChange = AWAITING_ENABLED;
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+            CHECK(enabled);
+            mOutputPortSettingsChange = NONE;
+            break;
+        }
+    }
+}
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+    return new android::SoftAAC(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.h b/media/libstagefright/codecs/aacdec/SoftAAC.h
new file mode 100644
index 0000000..963fd27
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/SoftAAC.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#ifndef SOFT_AAC_H_
+
+#define SOFT_AAC_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct tPVMP4AudioDecoderExternal;
+
+namespace android {
+
+struct SoftAAC : public SimpleSoftOMXComponent {
+    SoftAAC(const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+protected:
+    virtual ~SoftAAC();
+
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+    virtual void onPortFlushCompleted(OMX_U32 portIndex);
+    virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+    enum {
+        kNumBuffers = 4
+    };
+
+    tPVMP4AudioDecoderExternal *mConfig;
+    void *mDecoderBuf;
+
+    size_t mInputBufferCount;
+    size_t mUpsamplingFactor;
+    int64_t mAnchorTimeUs;
+    int64_t mNumSamplesOutput;
+
+    bool mSignalledError;
+
+    enum {
+        NONE,
+        AWAITING_DISABLED,
+        AWAITING_ENABLED
+    } mOutputPortSettingsChange;
+
+    void initPorts();
+    status_t initDecoder();
+    bool isConfigured() const;
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftAAC);
+};
+
+}  // namespace android
+
+#endif  // SOFT_AAC_H_
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.mk b/media/libstagefright/codecs/amrnb/dec/Android.mk
index a545762..296cae4 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.mk
+++ b/media/libstagefright/codecs/amrnb/dec/Android.mk
@@ -52,3 +52,33 @@
 LOCAL_MODULE := libstagefright_amrnbdec
 
 include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        SoftAMR.cpp
+
+LOCAL_C_INCLUDES := \
+        frameworks/base/media/libstagefright/include \
+        frameworks/base/include/media/stagefright/openmax \
+        $(LOCAL_PATH)/src \
+        $(LOCAL_PATH)/include \
+        $(LOCAL_PATH)/../common/include \
+        $(LOCAL_PATH)/../common \
+        frameworks/base/media/libstagefright/codecs/amrwb/src \
+
+LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
+
+LOCAL_STATIC_LIBRARIES := \
+        libstagefright_amrnbdec libstagefright_amrwbdec
+
+LOCAL_SHARED_LIBRARIES := \
+        libstagefright_omx libstagefright_foundation libutils \
+        libstagefright_amrnb_common
+
+LOCAL_MODULE := libstagefright_soft_amrdec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
new file mode 100644
index 0000000..c0a588f
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
@@ -0,0 +1,434 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "SoftAMR"
+#include <utils/Log.h>
+
+#include "SoftAMR.h"
+
+#include "gsmamr_dec.h"
+#include "pvamrwbdecoder.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+SoftAMR::SoftAMR(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SimpleSoftOMXComponent(name, callbacks, appData, component),
+      mMode(MODE_NARROW),
+      mState(NULL),
+      mDecoderBuf(NULL),
+      mDecoderCookie(NULL),
+      mInputBufferCount(0),
+      mAnchorTimeUs(0),
+      mNumSamplesOutput(0),
+      mSignalledError(false),
+      mOutputPortSettingsChange(NONE) {
+    if (!strcmp(name, "OMX.google.amrwb.decoder")) {
+        mMode = MODE_WIDE;
+    } else {
+        CHECK(!strcmp(name, "OMX.google.amrnb.decoder"));
+    }
+
+    initPorts();
+    CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftAMR::~SoftAMR() {
+    if (mMode == MODE_NARROW) {
+        GSMDecodeFrameExit(&mState);
+        mState = NULL;
+    } else {
+        free(mDecoderBuf);
+        mDecoderBuf = NULL;
+
+        mState = NULL;
+        mDecoderCookie = NULL;
+    }
+}
+
+void SoftAMR::initPorts() {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+
+    def.nPortIndex = 0;
+    def.eDir = OMX_DirInput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = 8192;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 1;
+
+    def.format.audio.cMIMEType =
+        mMode == MODE_NARROW
+            ? const_cast<char *>("audio/amr")
+            : const_cast<char *>("audio/amrwb");
+
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingAMR;
+
+    addPort(def);
+
+    def.nPortIndex = 1;
+    def.eDir = OMX_DirOutput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+
+    def.nBufferSize =
+        (mMode == MODE_NARROW ? kNumSamplesPerFrameNB : kNumSamplesPerFrameWB)
+            * sizeof(int16_t);
+
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 2;
+
+    def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+    addPort(def);
+}
+
+status_t SoftAMR::initDecoder() {
+    if (mMode == MODE_NARROW) {
+        Word16 err = GSMInitDecode(&mState, (Word8 *)"AMRNBDecoder");
+
+        if (err != 0) {
+            return UNKNOWN_ERROR;
+        }
+    } else {
+        int32_t memReq = pvDecoder_AmrWbMemRequirements();
+        mDecoderBuf = malloc(memReq);
+
+        pvDecoder_AmrWb_Init(&mState, mDecoderBuf, &mDecoderCookie);
+    }
+
+    return OK;
+}
+
+OMX_ERRORTYPE SoftAMR::internalGetParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamAudioAmr:
+        {
+            OMX_AUDIO_PARAM_AMRTYPE *amrParams =
+                (OMX_AUDIO_PARAM_AMRTYPE *)params;
+
+            if (amrParams->nPortIndex != 0) {
+                return OMX_ErrorUndefined;
+            }
+
+            amrParams->nChannels = 1;
+            amrParams->eAMRDTXMode = OMX_AUDIO_AMRDTXModeOff;
+            amrParams->eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatConformance;
+
+            if (!isConfigured()) {
+                amrParams->nBitRate = 0;
+                amrParams->eAMRBandMode = OMX_AUDIO_AMRBandModeUnused;
+            } else {
+                amrParams->nBitRate = 0;
+                amrParams->eAMRBandMode =
+                    mMode == MODE_NARROW
+                        ? OMX_AUDIO_AMRBandModeNB0 : OMX_AUDIO_AMRBandModeWB0;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamAudioPcm:
+        {
+            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+            if (pcmParams->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            pcmParams->nChannels = 1;
+            pcmParams->eNumData = OMX_NumericalDataSigned;
+            pcmParams->eEndian = OMX_EndianBig;
+            pcmParams->bInterleaved = OMX_TRUE;
+            pcmParams->nBitPerSample = 16;
+
+            pcmParams->nSamplingRate =
+                (mMode == MODE_NARROW) ? kSampleRateNB : kSampleRateWB;
+
+            pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+            pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+            pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalGetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftAMR::internalSetParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamStandardComponentRole:
+        {
+            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+                (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+            if (mMode == MODE_NARROW) {
+                if (strncmp((const char *)roleParams->cRole,
+                            "audio_decoder.amrnb",
+                            OMX_MAX_STRINGNAME_SIZE - 1)) {
+                    return OMX_ErrorUndefined;
+                }
+            } else {
+                if (strncmp((const char *)roleParams->cRole,
+                            "audio_decoder.amrwb",
+                            OMX_MAX_STRINGNAME_SIZE - 1)) {
+                    return OMX_ErrorUndefined;
+                }
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamAudioAmr:
+        {
+            const OMX_AUDIO_PARAM_AMRTYPE *aacParams =
+                (const OMX_AUDIO_PARAM_AMRTYPE *)params;
+
+            if (aacParams->nPortIndex != 0) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalSetParameter(index, params);
+    }
+}
+
+bool SoftAMR::isConfigured() const {
+    return mInputBufferCount > 0;
+}
+
+static size_t getFrameSize(unsigned FT) {
+    static const size_t kFrameSizeWB[9] = {
+        132, 177, 253, 285, 317, 365, 397, 461, 477
+    };
+
+    size_t frameSize = kFrameSizeWB[FT];
+
+    // Round up bits to bytes and add 1 for the header byte.
+    frameSize = (frameSize + 7) / 8 + 1;
+
+    return frameSize;
+}
+
+void SoftAMR::onQueueFilled(OMX_U32 portIndex) {
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+
+    if (mSignalledError || mOutputPortSettingsChange != NONE) {
+        return;
+    }
+
+    while (!inQueue.empty() && !outQueue.empty()) {
+        BufferInfo *inInfo = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+        BufferInfo *outInfo = *outQueue.begin();
+        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+            inQueue.erase(inQueue.begin());
+            inInfo->mOwnedByUs = false;
+            notifyEmptyBufferDone(inHeader);
+
+            outHeader->nFilledLen = 0;
+            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+            outQueue.erase(outQueue.begin());
+            outInfo->mOwnedByUs = false;
+            notifyFillBufferDone(outHeader);
+            return;
+        }
+
+        if (inHeader->nOffset == 0) {
+            mAnchorTimeUs = inHeader->nTimeStamp;
+            mNumSamplesOutput = 0;
+        }
+
+        const uint8_t *inputPtr = inHeader->pBuffer + inHeader->nOffset;
+        int32_t numBytesRead;
+
+        if (mMode == MODE_NARROW) {
+            numBytesRead =
+                AMRDecode(mState,
+                  (Frame_Type_3GPP)((inputPtr[0] >> 3) & 0x0f),
+                  (UWord8 *)&inputPtr[1],
+                  reinterpret_cast<int16_t *>(outHeader->pBuffer),
+                  MIME_IETF);
+
+            if (numBytesRead == -1) {
+                LOGE("PV AMR decoder AMRDecode() call failed");
+
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                mSignalledError = true;
+
+                return;
+            }
+
+            ++numBytesRead;  // Include the frame type header byte.
+
+            if (static_cast<size_t>(numBytesRead) > inHeader->nFilledLen) {
+                // This is bad, should never have happened, but did. Abort now.
+
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                mSignalledError = true;
+
+                return;
+            }
+        } else {
+            int16 mode = ((inputPtr[0] >> 3) & 0x0f);
+            size_t frameSize = getFrameSize(mode);
+            CHECK_GE(inHeader->nFilledLen, frameSize);
+
+            int16 frameType;
+            RX_State_wb rx_state;
+            mime_unsorting(
+                    const_cast<uint8_t *>(&inputPtr[1]),
+                    mInputSampleBuffer,
+                    &frameType, &mode, 1, &rx_state);
+
+            int16_t *outPtr = (int16_t *)outHeader->pBuffer;
+
+            int16_t numSamplesOutput;
+            pvDecoder_AmrWb(
+                    mode, mInputSampleBuffer,
+                    outPtr,
+                    &numSamplesOutput,
+                    mDecoderBuf, frameType, mDecoderCookie);
+
+            CHECK_EQ((int)numSamplesOutput, (int)kNumSamplesPerFrameWB);
+
+            for (int i = 0; i < kNumSamplesPerFrameWB; ++i) {
+                /* Delete the 2 LSBs (14-bit output) */
+                outPtr[i] &= 0xfffC;
+            }
+
+            numBytesRead = frameSize;
+        }
+
+        inHeader->nOffset += numBytesRead;
+        inHeader->nFilledLen -= numBytesRead;
+
+        outHeader->nFlags = 0;
+        outHeader->nOffset = 0;
+
+        if (mMode == MODE_NARROW) {
+            outHeader->nFilledLen = kNumSamplesPerFrameNB * sizeof(int16_t);
+
+            outHeader->nTimeStamp =
+                mAnchorTimeUs
+                    + (mNumSamplesOutput * 1000000ll) / kSampleRateNB;
+
+            mNumSamplesOutput += kNumSamplesPerFrameNB;
+        } else {
+            outHeader->nFilledLen = kNumSamplesPerFrameWB * sizeof(int16_t);
+
+            outHeader->nTimeStamp =
+                mAnchorTimeUs
+                    + (mNumSamplesOutput * 1000000ll) / kSampleRateWB;
+
+            mNumSamplesOutput += kNumSamplesPerFrameWB;
+        }
+
+        if (inHeader->nFilledLen == 0) {
+            inInfo->mOwnedByUs = false;
+            inQueue.erase(inQueue.begin());
+            inInfo = NULL;
+            notifyEmptyBufferDone(inHeader);
+            inHeader = NULL;
+        }
+
+        outInfo->mOwnedByUs = false;
+        outQueue.erase(outQueue.begin());
+        outInfo = NULL;
+        notifyFillBufferDone(outHeader);
+        outHeader = NULL;
+
+        ++mInputBufferCount;
+    }
+}
+
+void SoftAMR::onPortFlushCompleted(OMX_U32 portIndex) {
+}
+
+void SoftAMR::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+    if (portIndex != 1) {
+        return;
+    }
+
+    switch (mOutputPortSettingsChange) {
+        case NONE:
+            break;
+
+        case AWAITING_DISABLED:
+        {
+            CHECK(!enabled);
+            mOutputPortSettingsChange = AWAITING_ENABLED;
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+            CHECK(enabled);
+            mOutputPortSettingsChange = NONE;
+            break;
+        }
+    }
+}
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+    return new android::SoftAMR(name, callbacks, appData, component);
+}
+
diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.h b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h
new file mode 100644
index 0000000..9a596e5
--- /dev/null
+++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#ifndef SOFT_AMR_H_
+
+#define SOFT_AMR_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+namespace android {
+
+struct SoftAMR : public SimpleSoftOMXComponent {
+    SoftAMR(const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+protected:
+    virtual ~SoftAMR();
+
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+    virtual void onPortFlushCompleted(OMX_U32 portIndex);
+    virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+    enum {
+        kNumBuffers             = 4,
+        kSampleRateNB           = 8000,
+        kSampleRateWB           = 16000,
+        kNumSamplesPerFrameNB   = 160,
+        kNumSamplesPerFrameWB   = 320,
+    };
+
+    enum {
+        MODE_NARROW,
+        MODE_WIDE
+
+    } mMode;
+
+    void *mState;
+    void *mDecoderBuf;
+    int16_t *mDecoderCookie;
+
+    size_t mInputBufferCount;
+    int64_t mAnchorTimeUs;
+    int64_t mNumSamplesOutput;
+
+    bool mSignalledError;
+
+    enum {
+        NONE,
+        AWAITING_DISABLED,
+        AWAITING_ENABLED
+    } mOutputPortSettingsChange;
+
+    int16_t mInputSampleBuffer[477];
+
+    void initPorts();
+    status_t initDecoder();
+    bool isConfigured() const;
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftAMR);
+};
+
+}  // namespace android
+
+#endif  // SOFT_AMR_H_
+
diff --git a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp b/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp
index 2a21472..5b111ef 100644
--- a/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp
+++ b/media/libstagefright/codecs/amrwb/AMRWBDecoder.cpp
@@ -177,7 +177,7 @@
     CHECK(mInputBuffer->range_length() >= frameSize);
 
     int16 frameType;
-    RX_State rx_state;
+    RX_State_wb rx_state;
     mime_unsorting(
             const_cast<uint8_t *>(&inputPtr[1]),
             mInputSampleBuffer,
diff --git a/media/libstagefright/codecs/amrwb/src/mime_io.cpp b/media/libstagefright/codecs/amrwb/src/mime_io.cpp
index 9ff8816..e1966c6 100644
--- a/media/libstagefright/codecs/amrwb/src/mime_io.cpp
+++ b/media/libstagefright/codecs/amrwb/src/mime_io.cpp
@@ -531,7 +531,7 @@
                     int16 * frame_type,
                     int16 * mode,
                     uint8 quality,
-                    RX_State *st)
+                    RX_State_wb *st)
 {
 
     int16 i;
diff --git a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h
index 433fc92..c40bc10 100644
--- a/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h
+++ b/media/libstagefright/codecs/amrwb/src/pvamrwbdecoder.h
@@ -101,7 +101,7 @@
 {
     int16 prev_ft;
     int16 prev_mode;
-} RX_State;
+} RX_State_wb;
 
     /*----------------------------------------------------------------------------
     ; ENUMERATED TYPEDEF'S
@@ -141,7 +141,7 @@
                         int16 *frame_type,
                         int16 *mode,
                         uint8 q,
-                        RX_State *st);
+                        RX_State_wb *st);
 
 
     /*----------------------------------------------------------------------------
diff --git a/media/libstagefright/codecs/avc/dec/Android.mk b/media/libstagefright/codecs/avc/dec/Android.mk
index 1b00347..afecdc4 100644
--- a/media/libstagefright/codecs/avc/dec/Android.mk
+++ b/media/libstagefright/codecs/avc/dec/Android.mk
@@ -3,25 +3,54 @@
 
 LOCAL_SRC_FILES := \
         AVCDecoder.cpp \
-	src/avcdec_api.cpp \
- 	src/avc_bitstream.cpp \
- 	src/header.cpp \
- 	src/itrans.cpp \
- 	src/pred_inter.cpp \
- 	src/pred_intra.cpp \
- 	src/residual.cpp \
- 	src/slice.cpp \
- 	src/vlc.cpp
+        src/avcdec_api.cpp \
+        src/avc_bitstream.cpp \
+        src/header.cpp \
+        src/itrans.cpp \
+        src/pred_inter.cpp \
+        src/pred_intra.cpp \
+        src/residual.cpp \
+        src/slice.cpp \
+        src/vlc.cpp
 
 LOCAL_MODULE := libstagefright_avcdec
 
 LOCAL_C_INCLUDES := \
-	$(LOCAL_PATH)/src \
- 	$(LOCAL_PATH)/include \
- 	$(LOCAL_PATH)/../common/include \
+        $(LOCAL_PATH)/src \
+        $(LOCAL_PATH)/include \
+        $(LOCAL_PATH)/../common/include \
         $(TOP)/frameworks/base/media/libstagefright/include \
-        $(TOP)/frameworks/base/include/media/stagefright/openmax
+        frameworks/base/include/media/stagefright/openmax \
 
 LOCAL_CFLAGS := -DOSCL_IMPORT_REF= -DOSCL_UNUSED_ARG= -DOSCL_EXPORT_REF=
 
 include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        SoftAVC.cpp
+
+LOCAL_C_INCLUDES := \
+        $(LOCAL_PATH)/src \
+        $(LOCAL_PATH)/include \
+        $(LOCAL_PATH)/../common/include \
+        frameworks/base/media/libstagefright/include \
+        frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_CFLAGS := -DOSCL_IMPORT_REF=
+
+LOCAL_STATIC_LIBRARIES := \
+        libstagefright_avcdec
+
+LOCAL_SHARED_LIBRARIES := \
+        libstagefright_avc_common \
+        libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_avcdec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/media/libstagefright/codecs/avc/dec/SoftAVC.cpp b/media/libstagefright/codecs/avc/dec/SoftAVC.cpp
new file mode 100644
index 0000000..9f141ac
--- /dev/null
+++ b/media/libstagefright/codecs/avc/dec/SoftAVC.cpp
@@ -0,0 +1,690 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "SoftAVC"
+#include <utils/Log.h>
+
+#include "SoftAVC.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include "avcdec_api.h"
+#include "avcdec_int.h"
+
+namespace android {
+
+static const char kStartCode[4] = { 0x00, 0x00, 0x00, 0x01 };
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+static int32_t Malloc(void *userData, int32_t size, int32_t attrs) {
+    return reinterpret_cast<int32_t>(malloc(size));
+}
+
+static void Free(void *userData, int32_t ptr) {
+    free(reinterpret_cast<void *>(ptr));
+}
+
+SoftAVC::SoftAVC(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SimpleSoftOMXComponent(name, callbacks, appData, component),
+      mHandle(new tagAVCHandle),
+      mInputBufferCount(0),
+      mWidth(160),
+      mHeight(120),
+      mCropLeft(0),
+      mCropTop(0),
+      mCropRight(mWidth - 1),
+      mCropBottom(mHeight - 1),
+      mSPSSeen(false),
+      mPPSSeen(false),
+      mCurrentTimeUs(-1),
+      mEOSStatus(INPUT_DATA_AVAILABLE),
+      mOutputPortSettingsChange(NONE) {
+    initPorts();
+    CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftAVC::~SoftAVC() {
+    PVAVCCleanUpDecoder(mHandle);
+
+    delete mHandle;
+    mHandle = NULL;
+}
+
+void SoftAVC::initPorts() {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+
+    def.nPortIndex = 0;
+    def.eDir = OMX_DirInput;
+    def.nBufferCountMin = kNumInputBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = 8192;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainVideo;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 1;
+
+    def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_AVC);
+    def.format.video.pNativeRender = NULL;
+    def.format.video.nFrameWidth = mWidth;
+    def.format.video.nFrameHeight = mHeight;
+    def.format.video.nStride = def.format.video.nFrameWidth;
+    def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+    def.format.video.nBitrate = 0;
+    def.format.video.xFramerate = 0;
+    def.format.video.bFlagErrorConcealment = OMX_FALSE;
+    def.format.video.eCompressionFormat = OMX_VIDEO_CodingAVC;
+    def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
+    def.format.video.pNativeWindow = NULL;
+
+    addPort(def);
+
+    def.nPortIndex = 1;
+    def.eDir = OMX_DirOutput;
+    def.nBufferCountMin = kNumOutputBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainVideo;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 2;
+
+    def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
+    def.format.video.pNativeRender = NULL;
+    def.format.video.nFrameWidth = mWidth;
+    def.format.video.nFrameHeight = mHeight;
+    def.format.video.nStride = def.format.video.nFrameWidth;
+    def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+    def.format.video.nBitrate = 0;
+    def.format.video.xFramerate = 0;
+    def.format.video.bFlagErrorConcealment = OMX_FALSE;
+    def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+    def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
+    def.format.video.pNativeWindow = NULL;
+
+    def.nBufferSize =
+        (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2;
+
+    addPort(def);
+}
+
+status_t SoftAVC::initDecoder() {
+    memset(mHandle, 0, sizeof(tagAVCHandle));
+    mHandle->AVCObject = NULL;
+    mHandle->userData = this;
+    mHandle->CBAVC_DPBAlloc = ActivateSPSWrapper;
+    mHandle->CBAVC_FrameBind = BindFrameWrapper;
+    mHandle->CBAVC_FrameUnbind = UnbindFrame;
+    mHandle->CBAVC_Malloc = Malloc;
+    mHandle->CBAVC_Free = Free;
+
+    return OK;
+}
+
+OMX_ERRORTYPE SoftAVC::internalGetParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamVideoPortFormat:
+        {
+            OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+                (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (formatParams->nIndex != 0) {
+                return OMX_ErrorNoMore;
+            }
+
+            if (formatParams->nPortIndex == 0) {
+                formatParams->eCompressionFormat = OMX_VIDEO_CodingAVC;
+                formatParams->eColorFormat = OMX_COLOR_FormatUnused;
+                formatParams->xFramerate = 0;
+            } else {
+                CHECK_EQ(formatParams->nPortIndex, 1u);
+
+                formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
+                formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
+                formatParams->xFramerate = 0;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalGetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftAVC::internalSetParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamStandardComponentRole:
+        {
+            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+                (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+            if (strncmp((const char *)roleParams->cRole,
+                        "video_decoder.avc",
+                        OMX_MAX_STRINGNAME_SIZE - 1)) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamVideoPortFormat:
+        {
+            OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+                (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (formatParams->nIndex != 0) {
+                return OMX_ErrorNoMore;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalSetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftAVC::getConfig(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexConfigCommonOutputCrop:
+        {
+            OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params;
+
+            if (rectParams->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            rectParams->nLeft = mCropLeft;
+            rectParams->nTop = mCropTop;
+            rectParams->nWidth = mCropRight - mCropLeft + 1;
+            rectParams->nHeight = mCropBottom - mCropTop + 1;
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return OMX_ErrorUnsupportedIndex;
+    }
+}
+
+static void findNALFragment(
+        const OMX_BUFFERHEADERTYPE *inHeader,
+        const uint8_t **fragPtr, size_t *fragSize) {
+    const uint8_t *data = inHeader->pBuffer + inHeader->nOffset;
+
+    size_t size = inHeader->nFilledLen;
+
+    CHECK(size >= 4);
+    CHECK(!memcmp(kStartCode, data, 4));
+
+    size_t offset = 4;
+    while (offset + 3 < size && memcmp(kStartCode, &data[offset], 4)) {
+        ++offset;
+    }
+
+    *fragPtr = &data[4];
+    if (offset + 3 >= size) {
+        *fragSize = size - 4;
+    } else {
+        *fragSize = offset - 4;
+    }
+}
+
+void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
+    if (mOutputPortSettingsChange != NONE) {
+        return;
+    }
+
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+
+    if (mEOSStatus == OUTPUT_FRAMES_FLUSHED) {
+        return;
+    }
+
+    while ((mEOSStatus != INPUT_DATA_AVAILABLE || !inQueue.empty())
+            && outQueue.size() == kNumOutputBuffers) {
+        if (mEOSStatus == INPUT_EOS_SEEN) {
+            OMX_BUFFERHEADERTYPE *outHeader;
+            if (drainOutputBuffer(&outHeader)) {
+                List<BufferInfo *>::iterator it = outQueue.begin();
+                while ((*it)->mHeader != outHeader) {
+                    ++it;
+                }
+
+                BufferInfo *outInfo = *it;
+                outInfo->mOwnedByUs = false;
+                outQueue.erase(it);
+                outInfo = NULL;
+
+                notifyFillBufferDone(outHeader);
+                outHeader = NULL;
+                return;
+            }
+
+            BufferInfo *outInfo = *outQueue.begin();
+            outHeader = outInfo->mHeader;
+
+            outHeader->nOffset = 0;
+            outHeader->nFilledLen = 0;
+            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+            outHeader->nTimeStamp = 0;
+
+            outQueue.erase(outQueue.begin());
+            outInfo->mOwnedByUs = false;
+            notifyFillBufferDone(outHeader);
+
+            mEOSStatus = OUTPUT_FRAMES_FLUSHED;
+            return;
+        }
+
+        BufferInfo *inInfo = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+            inQueue.erase(inQueue.begin());
+            inInfo->mOwnedByUs = false;
+            notifyEmptyBufferDone(inHeader);
+
+            mEOSStatus = INPUT_EOS_SEEN;
+            continue;
+        }
+
+        mCurrentTimeUs = inHeader->nTimeStamp;
+
+        const uint8_t *fragPtr;
+        size_t fragSize;
+        findNALFragment(inHeader, &fragPtr, &fragSize);
+
+        bool releaseFragment;
+        OMX_BUFFERHEADERTYPE *outHeader;
+        status_t err = decodeFragment(
+                fragPtr, fragSize,
+                &releaseFragment, &outHeader);
+
+        if (releaseFragment) {
+            CHECK_GE(inHeader->nFilledLen, fragSize + 4);
+
+            inHeader->nOffset += fragSize + 4;
+            inHeader->nFilledLen -= fragSize + 4;
+
+            if (inHeader->nFilledLen == 0) {
+                inInfo->mOwnedByUs = false;
+                inQueue.erase(inQueue.begin());
+                inInfo = NULL;
+                notifyEmptyBufferDone(inHeader);
+                inHeader = NULL;
+            }
+        }
+
+        if (outHeader != NULL) {
+            List<BufferInfo *>::iterator it = outQueue.begin();
+            while ((*it)->mHeader != outHeader) {
+                ++it;
+            }
+
+            BufferInfo *outInfo = *it;
+            outInfo->mOwnedByUs = false;
+            outQueue.erase(it);
+            outInfo = NULL;
+
+            notifyFillBufferDone(outHeader);
+            outHeader = NULL;
+            return;
+        }
+
+        if (err == INFO_FORMAT_CHANGED) {
+            return;
+        }
+
+        if (err != OK) {
+            notify(OMX_EventError, OMX_ErrorUndefined, err, NULL);
+            return;
+        }
+    }
+}
+
+status_t SoftAVC::decodeFragment(
+        const uint8_t *fragPtr, size_t fragSize,
+        bool *releaseFragment,
+        OMX_BUFFERHEADERTYPE **outHeader) {
+    *releaseFragment = true;
+    *outHeader = NULL;
+
+    int nalType;
+    int nalRefIdc;
+    AVCDec_Status res =
+        PVAVCDecGetNALType(
+                const_cast<uint8_t *>(fragPtr), fragSize,
+                &nalType, &nalRefIdc);
+
+    if (res != AVCDEC_SUCCESS) {
+        LOGV("cannot determine nal type");
+        return ERROR_MALFORMED;
+    }
+
+    if (nalType != AVC_NALTYPE_SPS && nalType != AVC_NALTYPE_PPS
+            && (!mSPSSeen || !mPPSSeen)) {
+        // We haven't seen SPS or PPS yet.
+        return OK;
+    }
+
+    switch (nalType) {
+        case AVC_NALTYPE_SPS:
+        {
+            mSPSSeen = true;
+
+            res = PVAVCDecSeqParamSet(
+                    mHandle, const_cast<uint8_t *>(fragPtr),
+                    fragSize);
+
+            if (res != AVCDEC_SUCCESS) {
+                return ERROR_MALFORMED;
+            }
+
+            AVCDecObject *pDecVid = (AVCDecObject *)mHandle->AVCObject;
+
+            int32_t width =
+                (pDecVid->seqParams[0]->pic_width_in_mbs_minus1 + 1) * 16;
+
+            int32_t height =
+                (pDecVid->seqParams[0]->pic_height_in_map_units_minus1 + 1) * 16;
+
+            int32_t crop_left, crop_right, crop_top, crop_bottom;
+            if (pDecVid->seqParams[0]->frame_cropping_flag)
+            {
+                crop_left = 2 * pDecVid->seqParams[0]->frame_crop_left_offset;
+                crop_right =
+                    width - (2 * pDecVid->seqParams[0]->frame_crop_right_offset + 1);
+
+                if (pDecVid->seqParams[0]->frame_mbs_only_flag)
+                {
+                    crop_top = 2 * pDecVid->seqParams[0]->frame_crop_top_offset;
+                    crop_bottom =
+                        height -
+                        (2 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1);
+                }
+                else
+                {
+                    crop_top = 4 * pDecVid->seqParams[0]->frame_crop_top_offset;
+                    crop_bottom =
+                        height -
+                        (4 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1);
+                }
+            } else {
+                crop_bottom = height - 1;
+                crop_right = width - 1;
+                crop_top = crop_left = 0;
+            }
+
+            status_t err = OK;
+
+            if (mWidth != width || mHeight != height) {
+                mWidth = width;
+                mHeight = height;
+
+                updatePortDefinitions();
+
+                notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+                mOutputPortSettingsChange = AWAITING_DISABLED;
+
+                err = INFO_FORMAT_CHANGED;
+            }
+
+            if (mCropLeft != crop_left
+                    || mCropTop != crop_top
+                    || mCropRight != crop_right
+                    || mCropBottom != crop_bottom) {
+                mCropLeft = crop_left;
+                mCropTop = crop_top;
+                mCropRight = crop_right;
+                mCropBottom = crop_bottom;
+
+                notify(OMX_EventPortSettingsChanged,
+                       1,
+                       OMX_IndexConfigCommonOutputCrop,
+                       NULL);
+            }
+
+            return err;
+        }
+
+        case AVC_NALTYPE_PPS:
+        {
+            mPPSSeen = true;
+
+            res = PVAVCDecPicParamSet(
+                    mHandle, const_cast<uint8_t *>(fragPtr),
+                    fragSize);
+
+            if (res != AVCDEC_SUCCESS) {
+                LOGV("PVAVCDecPicParamSet returned error %d", res);
+                return ERROR_MALFORMED;
+            }
+
+            return OK;
+        }
+
+        case AVC_NALTYPE_SLICE:
+        case AVC_NALTYPE_IDR:
+        {
+            res = PVAVCDecodeSlice(
+                    mHandle, const_cast<uint8_t *>(fragPtr),
+                    fragSize);
+
+            if (res == AVCDEC_PICTURE_OUTPUT_READY) {
+                *releaseFragment = false;
+
+                if (!drainOutputBuffer(outHeader)) {
+                    return UNKNOWN_ERROR;
+                }
+
+                return OK;
+            }
+
+            if (res == AVCDEC_PICTURE_READY || res == AVCDEC_SUCCESS) {
+                return OK;
+            } else {
+                LOGV("PVAVCDecodeSlice returned error %d", res);
+                return ERROR_MALFORMED;
+            }
+        }
+
+        case AVC_NALTYPE_SEI:
+        {
+            res = PVAVCDecSEI(
+                    mHandle, const_cast<uint8_t *>(fragPtr),
+                    fragSize);
+
+            if (res != AVCDEC_SUCCESS) {
+                return ERROR_MALFORMED;
+            }
+
+            return OK;
+        }
+
+        case AVC_NALTYPE_AUD:
+        case AVC_NALTYPE_FILL:
+        case AVC_NALTYPE_EOSEQ:
+        {
+            return OK;
+        }
+
+        default:
+        {
+            LOGE("Should not be here, unknown nalType %d", nalType);
+
+            return ERROR_MALFORMED;
+        }
+    }
+
+    return OK;
+}
+
+bool SoftAVC::drainOutputBuffer(OMX_BUFFERHEADERTYPE **outHeader) {
+    int32_t index;
+    int32_t Release;
+    AVCFrameIO Output;
+    Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL;
+    AVCDec_Status status =
+        PVAVCDecGetOutput(mHandle, &index, &Release, &Output);
+
+    if (status != AVCDEC_SUCCESS) {
+        return false;
+    }
+
+    PortInfo *port = editPortInfo(1);
+    CHECK_GE(index, 0);
+    CHECK_LT((size_t)index, port->mBuffers.size());
+    CHECK(port->mBuffers.editItemAt(index).mOwnedByUs);
+
+    *outHeader = port->mBuffers.editItemAt(index).mHeader;
+    (*outHeader)->nOffset = 0;
+    (*outHeader)->nFilledLen = port->mDef.nBufferSize;
+    (*outHeader)->nFlags = 0;
+
+    return true;
+}
+
+void SoftAVC::onPortFlushCompleted(OMX_U32 portIndex) {
+    if (portIndex == 0) {
+        PVAVCDecReset(mHandle);
+
+        mEOSStatus = INPUT_DATA_AVAILABLE;
+    }
+}
+
+void SoftAVC::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+    if (portIndex != 1) {
+        return;
+    }
+
+    switch (mOutputPortSettingsChange) {
+        case NONE:
+            break;
+
+        case AWAITING_DISABLED:
+        {
+            CHECK(!enabled);
+            mOutputPortSettingsChange = AWAITING_ENABLED;
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+            CHECK(enabled);
+            mOutputPortSettingsChange = NONE;
+            break;
+        }
+    }
+}
+
+void SoftAVC::updatePortDefinitions() {
+    OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef;
+    def->format.video.nFrameWidth = mWidth;
+    def->format.video.nFrameHeight = mHeight;
+    def->format.video.nStride = def->format.video.nFrameWidth;
+    def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+    def = &editPortInfo(1)->mDef;
+    def->format.video.nFrameWidth = mWidth;
+    def->format.video.nFrameHeight = mHeight;
+    def->format.video.nStride = def->format.video.nFrameWidth;
+    def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+    def->nBufferSize =
+        (def->format.video.nFrameWidth
+            * def->format.video.nFrameHeight * 3) / 2;
+}
+
+// static
+int32_t SoftAVC::ActivateSPSWrapper(
+        void *userData, unsigned int sizeInMbs, unsigned int numBuffers) {
+    return static_cast<SoftAVC *>(userData)->activateSPS(sizeInMbs, numBuffers);
+}
+
+// static
+int32_t SoftAVC::BindFrameWrapper(
+        void *userData, int32_t index, uint8_t **yuv) {
+    return static_cast<SoftAVC *>(userData)->bindFrame(index, yuv);
+}
+
+// static
+void SoftAVC::UnbindFrame(void *userData, int32_t index) {
+}
+
+int32_t SoftAVC::activateSPS(
+        unsigned int sizeInMbs, unsigned int numBuffers) {
+    PortInfo *port = editPortInfo(1);
+    CHECK_GE(port->mBuffers.size(), numBuffers);
+    CHECK_GE(port->mDef.nBufferSize, (sizeInMbs << 7) * 3);
+
+    return 1;
+}
+
+int32_t SoftAVC::bindFrame(int32_t index, uint8_t **yuv) {
+    PortInfo *port = editPortInfo(1);
+
+    CHECK_GE(index, 0);
+    CHECK_LT((size_t)index, port->mBuffers.size());
+
+    BufferInfo *outBuffer =
+        &port->mBuffers.editItemAt(index);
+
+    CHECK(outBuffer->mOwnedByUs);
+
+    outBuffer->mHeader->nTimeStamp = mCurrentTimeUs;
+    *yuv = outBuffer->mHeader->pBuffer;
+
+    return 1;
+}
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+    return new android::SoftAVC(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/avc/dec/SoftAVC.h b/media/libstagefright/codecs/avc/dec/SoftAVC.h
new file mode 100644
index 0000000..1594b4d
--- /dev/null
+++ b/media/libstagefright/codecs/avc/dec/SoftAVC.h
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+#ifndef SOFT_AVC_H_
+
+#define SOFT_AVC_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct tagAVCHandle;
+
+namespace android {
+
+struct SoftAVC : public SimpleSoftOMXComponent {
+    SoftAVC(const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+protected:
+    virtual ~SoftAVC();
+
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual OMX_ERRORTYPE getConfig(OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+    virtual void onPortFlushCompleted(OMX_U32 portIndex);
+    virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+    enum {
+        kNumInputBuffers  = 4,
+        kNumOutputBuffers = 18,
+    };
+
+    enum EOSStatus {
+        INPUT_DATA_AVAILABLE,
+        INPUT_EOS_SEEN,
+        OUTPUT_FRAMES_FLUSHED,
+    };
+
+    tagAVCHandle *mHandle;
+
+    size_t mInputBufferCount;
+
+    int32_t mWidth, mHeight;
+    int32_t mCropLeft, mCropTop, mCropRight, mCropBottom;
+
+    bool mSPSSeen, mPPSSeen;
+
+    int64_t mCurrentTimeUs;
+
+    EOSStatus mEOSStatus;
+
+    enum {
+        NONE,
+        AWAITING_DISABLED,
+        AWAITING_ENABLED
+    } mOutputPortSettingsChange;
+
+    void initPorts();
+    status_t initDecoder();
+
+    status_t decodeFragment(
+            const uint8_t *fragPtr, size_t fragSize,
+            bool *releaseFrames,
+            OMX_BUFFERHEADERTYPE **outHeader);
+
+    void updatePortDefinitions();
+    bool drainOutputBuffer(OMX_BUFFERHEADERTYPE **outHeader);
+
+    static int32_t ActivateSPSWrapper(
+            void *userData, unsigned int sizeInMbs, unsigned int numBuffers);
+
+    static int32_t BindFrameWrapper(
+            void *userData, int32_t index, uint8_t **yuv);
+
+    static void UnbindFrame(void *userData, int32_t index);
+
+    int32_t activateSPS(
+            unsigned int sizeInMbs, unsigned int numBuffers);
+
+    int32_t bindFrame(int32_t index, uint8_t **yuv);
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftAVC);
+};
+
+}  // namespace android
+
+#endif  // SOFT_AVC_H_
+
diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk
index cfb9fe4..b6953bc 100644
--- a/media/libstagefright/codecs/g711/dec/Android.mk
+++ b/media/libstagefright/codecs/g711/dec/Android.mk
@@ -10,3 +10,22 @@
 LOCAL_MODULE := libstagefright_g711dec
 
 include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        SoftG711.cpp
+
+LOCAL_C_INCLUDES := \
+        frameworks/base/media/libstagefright/include \
+        frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_SHARED_LIBRARIES := \
+        libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_g711dec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.cpp b/media/libstagefright/codecs/g711/dec/SoftG711.cpp
new file mode 100644
index 0000000..15e2c26
--- /dev/null
+++ b/media/libstagefright/codecs/g711/dec/SoftG711.cpp
@@ -0,0 +1,302 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "SoftG711"
+#include <utils/Log.h>
+
+#include "SoftG711.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+SoftG711::SoftG711(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SimpleSoftOMXComponent(name, callbacks, appData, component),
+      mIsMLaw(true),
+      mNumChannels(1),
+      mSignalledError(false) {
+    if (!strcmp(name, "OMX.google.g711.alaw.decoder")) {
+        mIsMLaw = false;
+    } else {
+        CHECK(!strcmp(name, "OMX.google.g711.mlaw.decoder"));
+    }
+
+    initPorts();
+}
+
+SoftG711::~SoftG711() {
+}
+
+void SoftG711::initPorts() {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+
+    def.nPortIndex = 0;
+    def.eDir = OMX_DirInput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = 8192;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 1;
+
+    def.format.audio.cMIMEType =
+        const_cast<char *>(
+                mIsMLaw
+                    ? MEDIA_MIMETYPE_AUDIO_G711_MLAW
+                    : MEDIA_MIMETYPE_AUDIO_G711_ALAW);
+
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingG711;
+
+    addPort(def);
+
+    def.nPortIndex = 1;
+    def.eDir = OMX_DirOutput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = kMaxNumSamplesPerFrame * sizeof(int16_t);
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 2;
+
+    def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+    addPort(def);
+}
+
+OMX_ERRORTYPE SoftG711::internalGetParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamAudioPcm:
+        {
+            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+            if (pcmParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            pcmParams->eNumData = OMX_NumericalDataSigned;
+            pcmParams->eEndian = OMX_EndianBig;
+            pcmParams->bInterleaved = OMX_TRUE;
+            pcmParams->nBitPerSample = 16;
+            pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+            pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+            pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+            pcmParams->nChannels = mNumChannels;
+            pcmParams->nSamplingRate = 8000;
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalGetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftG711::internalSetParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamAudioPcm:
+        {
+            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+            if (pcmParams->nPortIndex != 0) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (pcmParams->nChannels < 1 || pcmParams->nChannels > 2) {
+                return OMX_ErrorUndefined;
+            }
+
+            mNumChannels = pcmParams->nChannels;
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamStandardComponentRole:
+        {
+            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+                (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+            if (mIsMLaw) {
+                if (strncmp((const char *)roleParams->cRole,
+                            "audio_decoder.g711mlaw",
+                            OMX_MAX_STRINGNAME_SIZE - 1)) {
+                    return OMX_ErrorUndefined;
+                }
+            } else {
+                if (strncmp((const char *)roleParams->cRole,
+                            "audio_decoder.g711alaw",
+                            OMX_MAX_STRINGNAME_SIZE - 1)) {
+                    return OMX_ErrorUndefined;
+                }
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalSetParameter(index, params);
+    }
+}
+
+void SoftG711::onQueueFilled(OMX_U32 portIndex) {
+    if (mSignalledError) {
+        return;
+    }
+
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+
+    while (!inQueue.empty() && !outQueue.empty()) {
+        BufferInfo *inInfo = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+        BufferInfo *outInfo = *outQueue.begin();
+        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+            inQueue.erase(inQueue.begin());
+            inInfo->mOwnedByUs = false;
+            notifyEmptyBufferDone(inHeader);
+
+            outHeader->nFilledLen = 0;
+            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+            outQueue.erase(outQueue.begin());
+            outInfo->mOwnedByUs = false;
+            notifyFillBufferDone(outHeader);
+            return;
+        }
+
+        if (inHeader->nFilledLen > kMaxNumSamplesPerFrame) {
+            LOGE("input buffer too large (%ld).", inHeader->nFilledLen);
+
+            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+            mSignalledError = true;
+        }
+
+        const uint8_t *inputptr = inHeader->pBuffer + inHeader->nOffset;
+
+        if (mIsMLaw) {
+            DecodeMLaw(
+                    reinterpret_cast<int16_t *>(outHeader->pBuffer),
+                    inputptr, inHeader->nFilledLen);
+        } else {
+            DecodeALaw(
+                    reinterpret_cast<int16_t *>(outHeader->pBuffer),
+                    inputptr, inHeader->nFilledLen);
+        }
+
+        outHeader->nTimeStamp = inHeader->nTimeStamp;
+        outHeader->nOffset = 0;
+        outHeader->nFilledLen = inHeader->nFilledLen * sizeof(int16_t);
+        outHeader->nFlags = 0;
+
+        inInfo->mOwnedByUs = false;
+        inQueue.erase(inQueue.begin());
+        inInfo = NULL;
+        notifyEmptyBufferDone(inHeader);
+        inHeader = NULL;
+
+        outInfo->mOwnedByUs = false;
+        outQueue.erase(outQueue.begin());
+        outInfo = NULL;
+        notifyFillBufferDone(outHeader);
+        outHeader = NULL;
+    }
+}
+
+// static
+void SoftG711::DecodeALaw(
+        int16_t *out, const uint8_t *in, size_t inSize) {
+    while (inSize-- > 0) {
+        int32_t x = *in++;
+
+        int32_t ix = x ^ 0x55;
+        ix &= 0x7f;
+
+        int32_t iexp = ix >> 4;
+        int32_t mant = ix & 0x0f;
+
+        if (iexp > 0) {
+            mant += 16;
+        }
+
+        mant = (mant << 4) + 8;
+
+        if (iexp > 1) {
+            mant = mant << (iexp - 1);
+        }
+
+        *out++ = (x > 127) ? mant : -mant;
+    }
+}
+
+// static
+void SoftG711::DecodeMLaw(
+        int16_t *out, const uint8_t *in, size_t inSize) {
+    while (inSize-- > 0) {
+        int32_t x = *in++;
+
+        int32_t mantissa = ~x;
+        int32_t exponent = (mantissa >> 4) & 7;
+        int32_t segment = exponent + 1;
+        mantissa &= 0x0f;
+
+        int32_t step = 4 << segment;
+
+        int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33;
+
+        *out++ = (x < 0x80) ? -abs : abs;
+    }
+}
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+    return new android::SoftG711(name, callbacks, appData, component);
+}
+
diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.h b/media/libstagefright/codecs/g711/dec/SoftG711.h
new file mode 100644
index 0000000..bff0c68
--- /dev/null
+++ b/media/libstagefright/codecs/g711/dec/SoftG711.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifndef SOFT_G711_H_
+
+#define SOFT_G711_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+namespace android {
+
+struct SoftG711 : public SimpleSoftOMXComponent {
+    SoftG711(const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+protected:
+    virtual ~SoftG711();
+
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+
+private:
+    enum {
+        kNumBuffers = 4,
+        kMaxNumSamplesPerFrame = 16384,
+    };
+
+    bool mIsMLaw;
+    OMX_U32 mNumChannels;
+    bool mSignalledError;
+
+    void initPorts();
+
+    static void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize);
+    static void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize);
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftG711);
+};
+
+}  // namespace android
+
+#endif  // SOFT_G711_H_
+
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.mk b/media/libstagefright/codecs/m4v_h263/dec/Android.mk
index 2d9bcc6..65b642c 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.mk
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.mk
@@ -48,3 +48,29 @@
 LOCAL_CFLAGS := -DOSCL_EXPORT_REF= -DOSCL_IMPORT_REF=
 
 include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        SoftMPEG4.cpp
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/src \
+	$(LOCAL_PATH)/include \
+        frameworks/base/media/libstagefright/include \
+        frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_CFLAGS := -DOSCL_EXPORT_REF= -DOSCL_IMPORT_REF=
+
+LOCAL_STATIC_LIBRARIES := \
+        libstagefright_m4vh263dec
+
+LOCAL_SHARED_LIBRARIES := \
+        libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_mpeg4dec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
new file mode 100644
index 0000000..13e1662
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -0,0 +1,528 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "SoftMPEG4"
+#include <utils/Log.h>
+
+#include "SoftMPEG4.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include "mp4dec_api.h"
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+SoftMPEG4::SoftMPEG4(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SimpleSoftOMXComponent(name, callbacks, appData, component),
+      mMode(MODE_MPEG4),
+      mHandle(new tagvideoDecControls),
+      mInputBufferCount(0),
+      mWidth(352),
+      mHeight(288),
+      mCropLeft(0),
+      mCropTop(0),
+      mCropRight(mWidth - 1),
+      mCropBottom(mHeight - 1),
+      mSignalledError(false),
+      mInitialized(false),
+      mFramesConfigured(false),
+      mNumSamplesOutput(0),
+      mOutputPortSettingsChange(NONE) {
+    if (!strcmp(name, "OMX.google.h263.decoder")) {
+        mMode = MODE_H263;
+    } else {
+        CHECK(!strcmp(name, "OMX.google.mpeg4.decoder"));
+    }
+
+    initPorts();
+    CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftMPEG4::~SoftMPEG4() {
+    if (mInitialized) {
+        PVCleanUpVideoDecoder(mHandle);
+    }
+
+    delete mHandle;
+    mHandle = NULL;
+}
+
+void SoftMPEG4::initPorts() {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+
+    def.nPortIndex = 0;
+    def.eDir = OMX_DirInput;
+    def.nBufferCountMin = kNumInputBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = 8192;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainVideo;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 1;
+
+    def.format.video.cMIMEType =
+        (mMode == MODE_MPEG4)
+            ? const_cast<char *>(MEDIA_MIMETYPE_VIDEO_MPEG4)
+            : const_cast<char *>(MEDIA_MIMETYPE_VIDEO_H263);
+
+    def.format.video.pNativeRender = NULL;
+    def.format.video.nFrameWidth = mWidth;
+    def.format.video.nFrameHeight = mHeight;
+    def.format.video.nStride = def.format.video.nFrameWidth;
+    def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+    def.format.video.nBitrate = 0;
+    def.format.video.xFramerate = 0;
+    def.format.video.bFlagErrorConcealment = OMX_FALSE;
+
+    def.format.video.eCompressionFormat =
+        mMode == MODE_MPEG4 ? OMX_VIDEO_CodingMPEG4 : OMX_VIDEO_CodingH263;
+
+    def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
+    def.format.video.pNativeWindow = NULL;
+
+    addPort(def);
+
+    def.nPortIndex = 1;
+    def.eDir = OMX_DirOutput;
+    def.nBufferCountMin = kNumOutputBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainVideo;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 2;
+
+    def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
+    def.format.video.pNativeRender = NULL;
+    def.format.video.nFrameWidth = mWidth;
+    def.format.video.nFrameHeight = mHeight;
+    def.format.video.nStride = def.format.video.nFrameWidth;
+    def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+    def.format.video.nBitrate = 0;
+    def.format.video.xFramerate = 0;
+    def.format.video.bFlagErrorConcealment = OMX_FALSE;
+    def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+    def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
+    def.format.video.pNativeWindow = NULL;
+
+    def.nBufferSize =
+        (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2;
+
+    addPort(def);
+}
+
+status_t SoftMPEG4::initDecoder() {
+    memset(mHandle, 0, sizeof(tagvideoDecControls));
+    return OK;
+}
+
+OMX_ERRORTYPE SoftMPEG4::internalGetParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamVideoPortFormat:
+        {
+            OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+                (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (formatParams->nIndex != 0) {
+                return OMX_ErrorNoMore;
+            }
+
+            if (formatParams->nPortIndex == 0) {
+                formatParams->eCompressionFormat =
+                    (mMode == MODE_MPEG4)
+                        ? OMX_VIDEO_CodingMPEG4 : OMX_VIDEO_CodingH263;
+
+                formatParams->eColorFormat = OMX_COLOR_FormatUnused;
+                formatParams->xFramerate = 0;
+            } else {
+                CHECK_EQ(formatParams->nPortIndex, 1u);
+
+                formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
+                formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
+                formatParams->xFramerate = 0;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalGetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftMPEG4::internalSetParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamStandardComponentRole:
+        {
+            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+                (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+            if (mMode == MODE_MPEG4) {
+                if (strncmp((const char *)roleParams->cRole,
+                            "video_decoder.mpeg4",
+                            OMX_MAX_STRINGNAME_SIZE - 1)) {
+                    return OMX_ErrorUndefined;
+                }
+            } else {
+                if (strncmp((const char *)roleParams->cRole,
+                            "video_decoder.h263",
+                            OMX_MAX_STRINGNAME_SIZE - 1)) {
+                    return OMX_ErrorUndefined;
+                }
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamVideoPortFormat:
+        {
+            OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+                (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (formatParams->nIndex != 0) {
+                return OMX_ErrorNoMore;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalSetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftMPEG4::getConfig(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexConfigCommonOutputCrop:
+        {
+            OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params;
+
+            if (rectParams->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            rectParams->nLeft = mCropLeft;
+            rectParams->nTop = mCropTop;
+            rectParams->nWidth = mCropRight - mCropLeft + 1;
+            rectParams->nHeight = mCropBottom - mCropTop + 1;
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return OMX_ErrorUnsupportedIndex;
+    }
+}
+
+void SoftMPEG4::onQueueFilled(OMX_U32 portIndex) {
+    if (mSignalledError || mOutputPortSettingsChange != NONE) {
+        return;
+    }
+
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+
+    while (!inQueue.empty() && outQueue.size() == kNumOutputBuffers) {
+        BufferInfo *inInfo = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+        PortInfo *port = editPortInfo(1);
+
+        OMX_BUFFERHEADERTYPE *outHeader =
+            port->mBuffers.editItemAt(mNumSamplesOutput & 1).mHeader;
+
+        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+            inQueue.erase(inQueue.begin());
+            inInfo->mOwnedByUs = false;
+            notifyEmptyBufferDone(inHeader);
+
+            ++mInputBufferCount;
+
+            outHeader->nFilledLen = 0;
+            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+            List<BufferInfo *>::iterator it = outQueue.begin();
+            while ((*it)->mHeader != outHeader) {
+                ++it;
+            }
+
+            BufferInfo *outInfo = *it;
+            outInfo->mOwnedByUs = false;
+            outQueue.erase(it);
+            outInfo = NULL;
+
+            notifyFillBufferDone(outHeader);
+            outHeader = NULL;
+            return;
+        }
+
+        uint8_t *bitstream = inHeader->pBuffer + inHeader->nOffset;
+
+        if (!mInitialized) {
+            uint8_t *vol_data[1];
+            int32_t vol_size = 0;
+
+            vol_data[0] = NULL;
+
+            if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+                vol_data[0] = bitstream;
+                vol_size = inHeader->nFilledLen;
+            }
+
+            MP4DecodingMode mode =
+                (mMode == MODE_MPEG4) ? MPEG4_MODE : H263_MODE;
+
+            Bool success = PVInitVideoDecoder(
+                    mHandle, vol_data, &vol_size, 1, mWidth, mHeight, mode);
+
+            if (!success) {
+                LOGW("PVInitVideoDecoder failed. Unsupported content?");
+
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                mSignalledError = true;
+                return;
+            }
+
+            MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle);
+            if (mode != actualMode) {
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                mSignalledError = true;
+                return;
+            }
+
+            PVSetPostProcType((VideoDecControls *) mHandle, 0);
+
+            if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+                inInfo->mOwnedByUs = false;
+                inQueue.erase(inQueue.begin());
+                inInfo = NULL;
+                notifyEmptyBufferDone(inHeader);
+                inHeader = NULL;
+            }
+
+            mInitialized = true;
+
+            if (mode == MPEG4_MODE && portSettingsChanged()) {
+                return;
+            }
+
+            continue;
+        }
+
+        if (!mFramesConfigured) {
+            PortInfo *port = editPortInfo(1);
+            OMX_BUFFERHEADERTYPE *outHeader = port->mBuffers.editItemAt(1).mHeader;
+
+            PVSetReferenceYUV(mHandle, outHeader->pBuffer);
+
+            mFramesConfigured = true;
+        }
+
+        uint32_t timestamp = 0xFFFFFFFF;
+        int32_t bufferSize = inHeader->nFilledLen;
+
+        uint32_t useExtTimestamp = 0;
+        if (PVDecodeVideoFrame(
+                    mHandle, &bitstream, &timestamp, &bufferSize,
+                    &useExtTimestamp,
+                    outHeader->pBuffer) != PV_TRUE) {
+            LOGE("failed to decode video frame.");
+
+            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+            mSignalledError = true;
+            return;
+        }
+
+        if (portSettingsChanged()) {
+            return;
+        }
+
+        outHeader->nTimeStamp = inHeader->nTimeStamp;
+
+        inInfo->mOwnedByUs = false;
+        inQueue.erase(inQueue.begin());
+        inInfo = NULL;
+        notifyEmptyBufferDone(inHeader);
+        inHeader = NULL;
+
+        ++mInputBufferCount;
+
+        outHeader->nOffset = 0;
+        outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
+        outHeader->nFlags = 0;
+
+        List<BufferInfo *>::iterator it = outQueue.begin();
+        while ((*it)->mHeader != outHeader) {
+            ++it;
+        }
+
+        BufferInfo *outInfo = *it;
+        outInfo->mOwnedByUs = false;
+        outQueue.erase(it);
+        outInfo = NULL;
+
+        notifyFillBufferDone(outHeader);
+        outHeader = NULL;
+
+        ++mNumSamplesOutput;
+    }
+}
+
+bool SoftMPEG4::portSettingsChanged() {
+    int32_t disp_width, disp_height;
+    PVGetVideoDimensions(mHandle, &disp_width, &disp_height);
+
+    int32_t buf_width, buf_height;
+    PVGetBufferDimensions(mHandle, &buf_width, &buf_height);
+
+    CHECK_LE(disp_width, buf_width);
+    CHECK_LE(disp_height, buf_height);
+
+    LOGV("disp_width = %d, disp_height = %d, buf_width = %d, buf_height = %d",
+            disp_width, disp_height, buf_width, buf_height);
+
+    if (mCropRight != disp_width - 1
+            || mCropBottom != disp_height - 1) {
+        mCropLeft = 0;
+        mCropTop = 0;
+        mCropRight = disp_width - 1;
+        mCropBottom = disp_height - 1;
+
+        notify(OMX_EventPortSettingsChanged,
+               1,
+               OMX_IndexConfigCommonOutputCrop,
+               NULL);
+    }
+
+    if (buf_width != mWidth || buf_height != mHeight) {
+        mWidth = buf_width;
+        mHeight = buf_height;
+
+        updatePortDefinitions();
+
+        if (mMode == MODE_H263) {
+            PVCleanUpVideoDecoder(mHandle);
+
+            uint8_t *vol_data[1];
+            int32_t vol_size = 0;
+
+            vol_data[0] = NULL;
+            if (!PVInitVideoDecoder(
+                    mHandle, vol_data, &vol_size, 1, mWidth, mHeight,
+                    H263_MODE)) {
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                mSignalledError = true;
+                return true;
+            }
+        }
+
+        mFramesConfigured = false;
+
+        notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+        mOutputPortSettingsChange = AWAITING_DISABLED;
+        return true;
+    }
+
+    return false;
+}
+
+void SoftMPEG4::onPortFlushCompleted(OMX_U32 portIndex) {
+    if (portIndex == 0 && mInitialized) {
+        CHECK_EQ((int)PVResetVideoDecoder(mHandle), (int)PV_TRUE);
+    }
+}
+
+void SoftMPEG4::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+    if (portIndex != 1) {
+        return;
+    }
+
+    switch (mOutputPortSettingsChange) {
+        case NONE:
+            break;
+
+        case AWAITING_DISABLED:
+        {
+            CHECK(!enabled);
+            mOutputPortSettingsChange = AWAITING_ENABLED;
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+            CHECK(enabled);
+            mOutputPortSettingsChange = NONE;
+            break;
+        }
+    }
+}
+
+void SoftMPEG4::updatePortDefinitions() {
+    OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef;
+    def->format.video.nFrameWidth = mWidth;
+    def->format.video.nFrameHeight = mHeight;
+    def->format.video.nStride = def->format.video.nFrameWidth;
+    def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+    def = &editPortInfo(1)->mDef;
+    def->format.video.nFrameWidth = mWidth;
+    def->format.video.nFrameHeight = mHeight;
+    def->format.video.nStride = def->format.video.nFrameWidth;
+    def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+    def->nBufferSize =
+        (((def->format.video.nFrameWidth + 15) & -16)
+            * ((def->format.video.nFrameHeight + 15) & -16) * 3) / 2;
+}
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+    return new android::SoftMPEG4(name, callbacks, appData, component);
+}
+
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h
new file mode 100644
index 0000000..dff08a7
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#ifndef SOFT_MPEG4_H_
+
+#define SOFT_MPEG4_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct tagvideoDecControls;
+
+namespace android {
+
+struct SoftMPEG4 : public SimpleSoftOMXComponent {
+    SoftMPEG4(const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+protected:
+    virtual ~SoftMPEG4();
+
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual OMX_ERRORTYPE getConfig(OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+    virtual void onPortFlushCompleted(OMX_U32 portIndex);
+    virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+    enum {
+        kNumInputBuffers  = 4,
+        kNumOutputBuffers = 2,
+    };
+
+    enum {
+        MODE_MPEG4,
+        MODE_H263,
+
+    } mMode;
+
+    tagvideoDecControls *mHandle;
+
+    size_t mInputBufferCount;
+
+    int32_t mWidth, mHeight;
+    int32_t mCropLeft, mCropTop, mCropRight, mCropBottom;
+
+    bool mSignalledError;
+    bool mInitialized;
+    bool mFramesConfigured;
+
+    int32_t mNumSamplesOutput;
+
+    enum {
+        NONE,
+        AWAITING_DISABLED,
+        AWAITING_ENABLED
+    } mOutputPortSettingsChange;
+
+    void initPorts();
+    status_t initDecoder();
+
+    void updatePortDefinitions();
+    bool portSettingsChanged();
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG4);
+};
+
+}  // namespace android
+
+#endif  // SOFT_MPEG4_H_
+
+
diff --git a/media/libstagefright/codecs/mp3dec/Android.mk b/media/libstagefright/codecs/mp3dec/Android.mk
index 753500e..2d35183 100644
--- a/media/libstagefright/codecs/mp3dec/Android.mk
+++ b/media/libstagefright/codecs/mp3dec/Android.mk
@@ -57,3 +57,26 @@
 
 include $(BUILD_STATIC_LIBRARY)
 
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        SoftMP3.cpp
+
+LOCAL_C_INCLUDES := \
+        frameworks/base/media/libstagefright/include \
+        frameworks/base/include/media/stagefright/openmax \
+        $(LOCAL_PATH)/src \
+        $(LOCAL_PATH)/include
+
+LOCAL_SHARED_LIBRARIES := \
+        libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_STATIC_LIBRARIES := \
+        libstagefright_mp3dec
+
+LOCAL_MODULE := libstagefright_soft_mp3dec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
new file mode 100644
index 0000000..f6770b0
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
@@ -0,0 +1,325 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "SoftMP3"
+#include <utils/Log.h>
+
+#include "SoftMP3.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+
+#include "include/pvmp3decoder_api.h"
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+SoftMP3::SoftMP3(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SimpleSoftOMXComponent(name, callbacks, appData, component),
+      mConfig(new tPVMP3DecoderExternal),
+      mDecoderBuf(NULL),
+      mAnchorTimeUs(0),
+      mNumFramesOutput(0),
+      mNumChannels(2),
+      mSamplingRate(44100),
+      mSignalledError(false),
+      mOutputPortSettingsChange(NONE) {
+    initPorts();
+    initDecoder();
+}
+
+SoftMP3::~SoftMP3() {
+    if (mDecoderBuf != NULL) {
+        free(mDecoderBuf);
+        mDecoderBuf = NULL;
+    }
+
+    delete mConfig;
+    mConfig = NULL;
+}
+
+void SoftMP3::initPorts() {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+
+    def.nPortIndex = 0;
+    def.eDir = OMX_DirInput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = 8192;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 1;
+
+    def.format.audio.cMIMEType =
+        const_cast<char *>(MEDIA_MIMETYPE_AUDIO_MPEG);
+
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingMP3;
+
+    addPort(def);
+
+    def.nPortIndex = 1;
+    def.eDir = OMX_DirOutput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = kOutputBufferSize;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 2;
+
+    def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+    addPort(def);
+}
+
+void SoftMP3::initDecoder() {
+    mConfig->equalizerType = flat;
+    mConfig->crcEnabled = false;
+
+    uint32_t memRequirements = pvmp3_decoderMemRequirements();
+    mDecoderBuf = malloc(memRequirements);
+
+    pvmp3_InitDecoder(mConfig, mDecoderBuf);
+}
+
+OMX_ERRORTYPE SoftMP3::internalGetParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamAudioPcm:
+        {
+            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+            if (pcmParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            pcmParams->eNumData = OMX_NumericalDataSigned;
+            pcmParams->eEndian = OMX_EndianBig;
+            pcmParams->bInterleaved = OMX_TRUE;
+            pcmParams->nBitPerSample = 16;
+            pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+            pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+            pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+            pcmParams->nChannels = mNumChannels;
+            pcmParams->nSamplingRate = mSamplingRate;
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalGetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftMP3::internalSetParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamStandardComponentRole:
+        {
+            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+                (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+            if (strncmp((const char *)roleParams->cRole,
+                        "audio_decoder.mp3",
+                        OMX_MAX_STRINGNAME_SIZE - 1)) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalSetParameter(index, params);
+    }
+}
+
+void SoftMP3::onQueueFilled(OMX_U32 portIndex) {
+    if (mSignalledError || mOutputPortSettingsChange != NONE) {
+        return;
+    }
+
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+
+    while (!inQueue.empty() && !outQueue.empty()) {
+        BufferInfo *inInfo = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+        BufferInfo *outInfo = *outQueue.begin();
+        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+            inQueue.erase(inQueue.begin());
+            inInfo->mOwnedByUs = false;
+            notifyEmptyBufferDone(inHeader);
+
+            outHeader->nFilledLen = 0;
+            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+            outQueue.erase(outQueue.begin());
+            outInfo->mOwnedByUs = false;
+            notifyFillBufferDone(outHeader);
+            return;
+        }
+
+        if (inHeader->nOffset == 0) {
+            mAnchorTimeUs = inHeader->nTimeStamp;
+            mNumFramesOutput = 0;
+        }
+
+        mConfig->pInputBuffer =
+            inHeader->pBuffer + inHeader->nOffset;
+
+        mConfig->inputBufferCurrentLength = inHeader->nFilledLen;
+        mConfig->inputBufferMaxLength = 0;
+        mConfig->inputBufferUsedLength = 0;
+
+        mConfig->outputFrameSize = kOutputBufferSize / sizeof(int16_t);
+
+        mConfig->pOutputBuffer =
+            reinterpret_cast<int16_t *>(outHeader->pBuffer);
+
+        ERROR_CODE decoderErr;
+        if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf))
+                != NO_DECODING_ERROR) {
+            LOGV("mp3 decoder returned error %d", decoderErr);
+
+            if (decoderErr != NO_ENOUGH_MAIN_DATA_ERROR ||
+                    mConfig->outputFrameSize == 0) {
+
+                if (mConfig->outputFrameSize == 0) {
+                    LOGE("Output frame size is 0");
+                }
+
+                notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+                mSignalledError = true;
+                return;
+            }
+
+            // This is recoverable, just ignore the current frame and
+            // play silence instead.
+            memset(outHeader->pBuffer,
+                   0,
+                   mConfig->outputFrameSize * sizeof(int16_t));
+
+            mConfig->inputBufferUsedLength = inHeader->nFilledLen;
+        } else if (mConfig->samplingRate != mSamplingRate
+                || mConfig->num_channels != mNumChannels) {
+            mSamplingRate = mConfig->samplingRate;
+            mNumChannels = mConfig->num_channels;
+
+            notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+            mOutputPortSettingsChange = AWAITING_DISABLED;
+            return;
+        }
+
+        outHeader->nOffset = 0;
+        outHeader->nFilledLen = mConfig->outputFrameSize * sizeof(int16_t);
+
+        outHeader->nTimeStamp =
+            mAnchorTimeUs
+                + (mNumFramesOutput * 1000000ll) / mConfig->samplingRate;
+
+        outHeader->nFlags = 0;
+
+        CHECK_GE(inHeader->nFilledLen, mConfig->inputBufferUsedLength);
+
+        inHeader->nOffset += mConfig->inputBufferUsedLength;
+        inHeader->nFilledLen -= mConfig->inputBufferUsedLength;
+
+        mNumFramesOutput += mConfig->outputFrameSize / mNumChannels;
+
+        if (inHeader->nFilledLen == 0) {
+            inInfo->mOwnedByUs = false;
+            inQueue.erase(inQueue.begin());
+            inInfo = NULL;
+            notifyEmptyBufferDone(inHeader);
+            inHeader = NULL;
+        }
+
+        outInfo->mOwnedByUs = false;
+        outQueue.erase(outQueue.begin());
+        outInfo = NULL;
+        notifyFillBufferDone(outHeader);
+        outHeader = NULL;
+    }
+}
+
+void SoftMP3::onPortFlushCompleted(OMX_U32 portIndex) {
+    if (portIndex == 0) {
+        // Make sure that the next buffer output does not still
+        // depend on fragments from the last one decoded.
+        pvmp3_InitDecoder(mConfig, mDecoderBuf);
+    }
+}
+
+void SoftMP3::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+    if (portIndex != 1) {
+        return;
+    }
+
+    switch (mOutputPortSettingsChange) {
+        case NONE:
+            break;
+
+        case AWAITING_DISABLED:
+        {
+            CHECK(!enabled);
+            mOutputPortSettingsChange = AWAITING_ENABLED;
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+            CHECK(enabled);
+            mOutputPortSettingsChange = NONE;
+            break;
+        }
+    }
+}
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+    return new android::SoftMP3(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h
new file mode 100644
index 0000000..70d0682
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#ifndef SOFT_MP3_H_
+
+#define SOFT_MP3_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct tPVMP3DecoderExternal;
+
+namespace android {
+
+struct SoftMP3 : public SimpleSoftOMXComponent {
+    SoftMP3(const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+protected:
+    virtual ~SoftMP3();
+
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+    virtual void onPortFlushCompleted(OMX_U32 portIndex);
+    virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+    enum {
+        kNumBuffers = 4,
+        kOutputBufferSize = 4608 * 2
+    };
+
+    tPVMP3DecoderExternal *mConfig;
+    void *mDecoderBuf;
+    int64_t mAnchorTimeUs;
+    int64_t mNumFramesOutput;
+
+    int32_t mNumChannels;
+    int32_t mSamplingRate;
+
+    bool mConfigured;
+
+    bool mSignalledError;
+
+    enum {
+        NONE,
+        AWAITING_DISABLED,
+        AWAITING_ENABLED
+    } mOutputPortSettingsChange;
+
+    void initPorts();
+    void initDecoder();
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftMP3);
+};
+
+}  // namespace android
+
+#endif  // SOFT_MP3_H_
+
+
diff --git a/media/libstagefright/codecs/on2/dec/Android.mk b/media/libstagefright/codecs/on2/dec/Android.mk
index b769f0d..1b3088f 100644
--- a/media/libstagefright/codecs/on2/dec/Android.mk
+++ b/media/libstagefright/codecs/on2/dec/Android.mk
@@ -2,15 +2,42 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-        VPXDecoder.cpp
+        VPXDecoder.cpp  \
 
 LOCAL_MODULE := libstagefright_vpxdec
 
 LOCAL_C_INCLUDES := \
         $(TOP)/frameworks/base/media/libstagefright/include \
-        $(TOP)/frameworks/base/include/media/stagefright/openmax \
+        frameworks/base/include/media/stagefright/openmax \
         $(TOP)/external/libvpx \
         $(TOP)/external/libvpx/vpx_codec \
         $(TOP)/external/libvpx/vpx_ports
 
 include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        SoftVPX.cpp
+
+LOCAL_C_INCLUDES := \
+        $(TOP)/external/libvpx \
+        $(TOP)/external/libvpx/vpx_codec \
+        $(TOP)/external/libvpx/vpx_ports \
+        frameworks/base/media/libstagefright/include \
+        frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_STATIC_LIBRARIES := \
+        libstagefright_vpxdec \
+        libvpx
+
+LOCAL_SHARED_LIBRARIES := \
+        libstagefright libstagefright_omx libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_vpxdec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
new file mode 100644
index 0000000..e9ce719
--- /dev/null
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
@@ -0,0 +1,366 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "SoftVPX"
+#include <utils/Log.h>
+
+#include "SoftVPX.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+
+#include "vpx/vpx_decoder.h"
+#include "vpx/vpx_codec.h"
+#include "vpx/vp8dx.h"
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+SoftVPX::SoftVPX(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SimpleSoftOMXComponent(name, callbacks, appData, component),
+      mCtx(NULL),
+      mWidth(320),
+      mHeight(240),
+      mOutputPortSettingsChange(NONE) {
+    initPorts();
+    CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftVPX::~SoftVPX() {
+    vpx_codec_destroy((vpx_codec_ctx_t *)mCtx);
+    delete (vpx_codec_ctx_t *)mCtx;
+    mCtx = NULL;
+}
+
+void SoftVPX::initPorts() {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+
+    def.nPortIndex = 0;
+    def.eDir = OMX_DirInput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = 8192;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainVideo;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 1;
+
+    def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_VPX);
+    def.format.video.pNativeRender = NULL;
+    def.format.video.nFrameWidth = mWidth;
+    def.format.video.nFrameHeight = mHeight;
+    def.format.video.nStride = def.format.video.nFrameWidth;
+    def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+    def.format.video.nBitrate = 0;
+    def.format.video.xFramerate = 0;
+    def.format.video.bFlagErrorConcealment = OMX_FALSE;
+    def.format.video.eCompressionFormat = OMX_VIDEO_CodingVPX;
+    def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
+    def.format.video.pNativeWindow = NULL;
+
+    addPort(def);
+
+    def.nPortIndex = 1;
+    def.eDir = OMX_DirOutput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainVideo;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 2;
+
+    def.format.video.cMIMEType = const_cast<char *>(MEDIA_MIMETYPE_VIDEO_RAW);
+    def.format.video.pNativeRender = NULL;
+    def.format.video.nFrameWidth = mWidth;
+    def.format.video.nFrameHeight = mHeight;
+    def.format.video.nStride = def.format.video.nFrameWidth;
+    def.format.video.nSliceHeight = def.format.video.nFrameHeight;
+    def.format.video.nBitrate = 0;
+    def.format.video.xFramerate = 0;
+    def.format.video.bFlagErrorConcealment = OMX_FALSE;
+    def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+    def.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
+    def.format.video.pNativeWindow = NULL;
+
+    def.nBufferSize =
+        (def.format.video.nFrameWidth * def.format.video.nFrameHeight * 3) / 2;
+
+    addPort(def);
+}
+
+status_t SoftVPX::initDecoder() {
+    mCtx = new vpx_codec_ctx_t;
+    vpx_codec_err_t vpx_err;
+    if ((vpx_err = vpx_codec_dec_init(
+                (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, NULL, 0))) {
+        LOGE("on2 decoder failed to initialize. (%d)", vpx_err);
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+OMX_ERRORTYPE SoftVPX::internalGetParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamVideoPortFormat:
+        {
+            OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+                (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (formatParams->nIndex != 0) {
+                return OMX_ErrorNoMore;
+            }
+
+            if (formatParams->nPortIndex == 0) {
+                formatParams->eCompressionFormat = OMX_VIDEO_CodingVPX;
+                formatParams->eColorFormat = OMX_COLOR_FormatUnused;
+                formatParams->xFramerate = 0;
+            } else {
+                CHECK_EQ(formatParams->nPortIndex, 1u);
+
+                formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
+                formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
+                formatParams->xFramerate = 0;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalGetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftVPX::internalSetParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamStandardComponentRole:
+        {
+            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+                (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+            if (strncmp((const char *)roleParams->cRole,
+                        "video_decoder.vpx",
+                        OMX_MAX_STRINGNAME_SIZE - 1)) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamVideoPortFormat:
+        {
+            OMX_VIDEO_PARAM_PORTFORMATTYPE *formatParams =
+                (OMX_VIDEO_PARAM_PORTFORMATTYPE *)params;
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (formatParams->nIndex != 0) {
+                return OMX_ErrorNoMore;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalSetParameter(index, params);
+    }
+}
+
+void SoftVPX::onQueueFilled(OMX_U32 portIndex) {
+    if (mOutputPortSettingsChange != NONE) {
+        return;
+    }
+
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+
+    while (!inQueue.empty() && !outQueue.empty()) {
+        BufferInfo *inInfo = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+        BufferInfo *outInfo = *outQueue.begin();
+        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+            inQueue.erase(inQueue.begin());
+            inInfo->mOwnedByUs = false;
+            notifyEmptyBufferDone(inHeader);
+
+            outHeader->nFilledLen = 0;
+            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+            outQueue.erase(outQueue.begin());
+            outInfo->mOwnedByUs = false;
+            notifyFillBufferDone(outHeader);
+            return;
+        }
+
+        if (vpx_codec_decode(
+                    (vpx_codec_ctx_t *)mCtx,
+                    inHeader->pBuffer + inHeader->nOffset,
+                    inHeader->nFilledLen,
+                    NULL,
+                    0)) {
+            LOGE("on2 decoder failed to decode frame.");
+
+            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+            return;
+        }
+
+        vpx_codec_iter_t iter = NULL;
+        vpx_image_t *img = vpx_codec_get_frame((vpx_codec_ctx_t *)mCtx, &iter);
+
+        if (img != NULL) {
+            CHECK_EQ(img->fmt, IMG_FMT_I420);
+
+            int32_t width = img->d_w;
+            int32_t height = img->d_h;
+
+            if (width != mWidth || height != mHeight) {
+                mWidth = width;
+                mHeight = height;
+
+                updatePortDefinitions();
+
+                notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+                mOutputPortSettingsChange = AWAITING_DISABLED;
+                return;
+            }
+
+            outHeader->nOffset = 0;
+            outHeader->nFilledLen = (width * height * 3) / 2;
+            outHeader->nFlags = 0;
+            outHeader->nTimeStamp = inHeader->nTimeStamp;
+
+            const uint8_t *srcLine = (const uint8_t *)img->planes[PLANE_Y];
+            uint8_t *dst = outHeader->pBuffer;
+            for (size_t i = 0; i < img->d_h; ++i) {
+                memcpy(dst, srcLine, img->d_w);
+
+                srcLine += img->stride[PLANE_Y];
+                dst += img->d_w;
+            }
+
+            srcLine = (const uint8_t *)img->planes[PLANE_U];
+            for (size_t i = 0; i < img->d_h / 2; ++i) {
+                memcpy(dst, srcLine, img->d_w / 2);
+
+                srcLine += img->stride[PLANE_U];
+                dst += img->d_w / 2;
+            }
+
+            srcLine = (const uint8_t *)img->planes[PLANE_V];
+            for (size_t i = 0; i < img->d_h / 2; ++i) {
+                memcpy(dst, srcLine, img->d_w / 2);
+
+                srcLine += img->stride[PLANE_V];
+                dst += img->d_w / 2;
+            }
+
+            outInfo->mOwnedByUs = false;
+            outQueue.erase(outQueue.begin());
+            outInfo = NULL;
+            notifyFillBufferDone(outHeader);
+            outHeader = NULL;
+        }
+
+        inInfo->mOwnedByUs = false;
+        inQueue.erase(inQueue.begin());
+        inInfo = NULL;
+        notifyEmptyBufferDone(inHeader);
+        inHeader = NULL;
+    }
+}
+
+void SoftVPX::onPortFlushCompleted(OMX_U32 portIndex) {
+}
+
+void SoftVPX::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+    if (portIndex != 1) {
+        return;
+    }
+
+    switch (mOutputPortSettingsChange) {
+        case NONE:
+            break;
+
+        case AWAITING_DISABLED:
+        {
+            CHECK(!enabled);
+            mOutputPortSettingsChange = AWAITING_ENABLED;
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+            CHECK(enabled);
+            mOutputPortSettingsChange = NONE;
+            break;
+        }
+    }
+}
+
+void SoftVPX::updatePortDefinitions() {
+    OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(0)->mDef;
+    def->format.video.nFrameWidth = mWidth;
+    def->format.video.nFrameHeight = mHeight;
+    def->format.video.nStride = def->format.video.nFrameWidth;
+    def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+    def = &editPortInfo(1)->mDef;
+    def->format.video.nFrameWidth = mWidth;
+    def->format.video.nFrameHeight = mHeight;
+    def->format.video.nStride = def->format.video.nFrameWidth;
+    def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+
+    def->nBufferSize =
+        (def->format.video.nFrameWidth
+            * def->format.video.nFrameHeight * 3) / 2;
+}
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+    return new android::SoftVPX(name, callbacks, appData, component);
+}
+
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h
new file mode 100644
index 0000000..3e814a2
--- /dev/null
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef SOFT_VPX_H_
+
+#define SOFT_VPX_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+namespace android {
+
+struct SoftVPX : public SimpleSoftOMXComponent {
+    SoftVPX(const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+protected:
+    virtual ~SoftVPX();
+
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+    virtual void onPortFlushCompleted(OMX_U32 portIndex);
+    virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+    enum {
+        kNumBuffers = 4
+    };
+
+    void *mCtx;
+
+    int32_t mWidth;
+    int32_t mHeight;
+
+    enum {
+        NONE,
+        AWAITING_DISABLED,
+        AWAITING_ENABLED
+    } mOutputPortSettingsChange;
+
+    void initPorts();
+    status_t initDecoder();
+
+    void updatePortDefinitions();
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftVPX);
+};
+
+}  // namespace android
+
+#endif  // SOFT_VPX_H_
diff --git a/media/libstagefright/codecs/vorbis/dec/Android.mk b/media/libstagefright/codecs/vorbis/dec/Android.mk
index 5c768c8..06f0079 100644
--- a/media/libstagefright/codecs/vorbis/dec/Android.mk
+++ b/media/libstagefright/codecs/vorbis/dec/Android.mk
@@ -6,8 +6,33 @@
 
 LOCAL_C_INCLUDES := \
         frameworks/base/media/libstagefright/include \
-        external/tremolo
+        external/tremolo \
 
 LOCAL_MODULE := libstagefright_vorbisdec
 
 include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        SoftVorbis.cpp
+
+LOCAL_C_INCLUDES := \
+        external/tremolo \
+        frameworks/base/media/libstagefright/include \
+        frameworks/base/include/media/stagefright/openmax \
+
+LOCAL_STATIC_LIBRARIES := \
+        libstagefright_vorbisdec
+
+LOCAL_SHARED_LIBRARIES := \
+        libvorbisidec libstagefright libstagefright_omx \
+        libstagefright_foundation libutils
+
+LOCAL_MODULE := libstagefright_soft_vorbisdec
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
new file mode 100644
index 0000000..4091111
--- /dev/null
+++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
@@ -0,0 +1,445 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "SoftVorbis"
+#include <utils/Log.h>
+
+#include "SoftVorbis.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+
+extern "C" {
+    #include <Tremolo/codec_internal.h>
+
+    int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb);
+    int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb);
+    int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb);
+}
+
+namespace android {
+
+template<class T>
+static void InitOMXParams(T *params) {
+    params->nSize = sizeof(T);
+    params->nVersion.s.nVersionMajor = 1;
+    params->nVersion.s.nVersionMinor = 0;
+    params->nVersion.s.nRevision = 0;
+    params->nVersion.s.nStep = 0;
+}
+
+SoftVorbis::SoftVorbis(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SimpleSoftOMXComponent(name, callbacks, appData, component),
+      mInputBufferCount(0),
+      mState(NULL),
+      mVi(NULL),
+      mAnchorTimeUs(0),
+      mNumFramesOutput(0),
+      mNumFramesLeftOnPage(-1),
+      mOutputPortSettingsChange(NONE) {
+    initPorts();
+    CHECK_EQ(initDecoder(), (status_t)OK);
+}
+
+SoftVorbis::~SoftVorbis() {
+    if (mState != NULL) {
+        vorbis_dsp_clear(mState);
+        delete mState;
+        mState = NULL;
+    }
+
+    if (mVi != NULL) {
+        vorbis_info_clear(mVi);
+        delete mVi;
+        mVi = NULL;
+    }
+}
+
+void SoftVorbis::initPorts() {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+
+    def.nPortIndex = 0;
+    def.eDir = OMX_DirInput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = 8192;
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 1;
+
+    def.format.audio.cMIMEType =
+        const_cast<char *>(MEDIA_MIMETYPE_AUDIO_VORBIS);
+
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
+
+    addPort(def);
+
+    def.nPortIndex = 1;
+    def.eDir = OMX_DirOutput;
+    def.nBufferCountMin = kNumBuffers;
+    def.nBufferCountActual = def.nBufferCountMin;
+    def.nBufferSize = kMaxNumSamplesPerBuffer * sizeof(int16_t);
+    def.bEnabled = OMX_TRUE;
+    def.bPopulated = OMX_FALSE;
+    def.eDomain = OMX_PortDomainAudio;
+    def.bBuffersContiguous = OMX_FALSE;
+    def.nBufferAlignment = 2;
+
+    def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
+    def.format.audio.pNativeRender = NULL;
+    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
+    def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;
+
+    addPort(def);
+}
+
+status_t SoftVorbis::initDecoder() {
+    return OK;
+}
+
+OMX_ERRORTYPE SoftVorbis::internalGetParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamAudioVorbis:
+        {
+            OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams =
+                (OMX_AUDIO_PARAM_VORBISTYPE *)params;
+
+            if (vorbisParams->nPortIndex != 0) {
+                return OMX_ErrorUndefined;
+            }
+
+            vorbisParams->nBitRate = 0;
+            vorbisParams->nMinBitRate = 0;
+            vorbisParams->nMaxBitRate = 0;
+            vorbisParams->nAudioBandWidth = 0;
+            vorbisParams->nQuality = 3;
+            vorbisParams->bManaged = OMX_FALSE;
+            vorbisParams->bDownmix = OMX_FALSE;
+
+            if (!isConfigured()) {
+                vorbisParams->nChannels = 1;
+                vorbisParams->nSampleRate = 44100;
+            } else {
+                vorbisParams->nChannels = mVi->channels;
+                vorbisParams->nSampleRate = mVi->rate;
+                vorbisParams->nBitRate = mVi->bitrate_nominal;
+                vorbisParams->nMinBitRate = mVi->bitrate_lower;
+                vorbisParams->nMaxBitRate = mVi->bitrate_upper;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamAudioPcm:
+        {
+            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+                (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+            if (pcmParams->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            pcmParams->eNumData = OMX_NumericalDataSigned;
+            pcmParams->eEndian = OMX_EndianBig;
+            pcmParams->bInterleaved = OMX_TRUE;
+            pcmParams->nBitPerSample = 16;
+            pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
+            pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+            pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+
+            if (!isConfigured()) {
+                pcmParams->nChannels = 1;
+                pcmParams->nSamplingRate = 44100;
+            } else {
+                pcmParams->nChannels = mVi->channels;
+                pcmParams->nSamplingRate = mVi->rate;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalGetParameter(index, params);
+    }
+}
+
+OMX_ERRORTYPE SoftVorbis::internalSetParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamStandardComponentRole:
+        {
+            const OMX_PARAM_COMPONENTROLETYPE *roleParams =
+                (const OMX_PARAM_COMPONENTROLETYPE *)params;
+
+            if (strncmp((const char *)roleParams->cRole,
+                        "audio_decoder.vorbis",
+                        OMX_MAX_STRINGNAME_SIZE - 1)) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        case OMX_IndexParamAudioVorbis:
+        {
+            const OMX_AUDIO_PARAM_VORBISTYPE *vorbisParams =
+                (const OMX_AUDIO_PARAM_VORBISTYPE *)params;
+
+            if (vorbisParams->nPortIndex != 0) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return SimpleSoftOMXComponent::internalSetParameter(index, params);
+    }
+}
+
+bool SoftVorbis::isConfigured() const {
+    return mInputBufferCount >= 2;
+}
+
+static void makeBitReader(
+        const void *data, size_t size,
+        ogg_buffer *buf, ogg_reference *ref, oggpack_buffer *bits) {
+    buf->data = (uint8_t *)data;
+    buf->size = size;
+    buf->refcount = 1;
+    buf->ptr.owner = NULL;
+
+    ref->buffer = buf;
+    ref->begin = 0;
+    ref->length = size;
+    ref->next = NULL;
+
+    oggpack_readinit(bits, ref);
+}
+
+void SoftVorbis::onQueueFilled(OMX_U32 portIndex) {
+    List<BufferInfo *> &inQueue = getPortQueue(0);
+    List<BufferInfo *> &outQueue = getPortQueue(1);
+
+    if (mOutputPortSettingsChange != NONE) {
+        return;
+    }
+
+    if (portIndex == 0 && mInputBufferCount < 2) {
+        BufferInfo *info = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *header = info->mHeader;
+
+        const uint8_t *data = header->pBuffer + header->nOffset;
+        size_t size = header->nFilledLen;
+
+        ogg_buffer buf;
+        ogg_reference ref;
+        oggpack_buffer bits;
+
+        makeBitReader(
+                (const uint8_t *)data + 7, size - 7,
+                &buf, &ref, &bits);
+
+        if (mInputBufferCount == 0) {
+            CHECK(mVi == NULL);
+            mVi = new vorbis_info;
+            vorbis_info_init(mVi);
+
+            CHECK_EQ(0, _vorbis_unpack_info(mVi, &bits));
+        } else {
+            CHECK_EQ(0, _vorbis_unpack_books(mVi, &bits));
+
+            CHECK(mState == NULL);
+            mState = new vorbis_dsp_state;
+            CHECK_EQ(0, vorbis_dsp_init(mState, mVi));
+
+            notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+            mOutputPortSettingsChange = AWAITING_DISABLED;
+        }
+
+        inQueue.erase(inQueue.begin());
+        info->mOwnedByUs = false;
+        notifyEmptyBufferDone(header);
+
+        ++mInputBufferCount;
+
+        return;
+    }
+
+    while (!inQueue.empty() && !outQueue.empty()) {
+        BufferInfo *inInfo = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+        BufferInfo *outInfo = *outQueue.begin();
+        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+        if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
+            inQueue.erase(inQueue.begin());
+            inInfo->mOwnedByUs = false;
+            notifyEmptyBufferDone(inHeader);
+
+            outHeader->nFilledLen = 0;
+            outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+            outQueue.erase(outQueue.begin());
+            outInfo->mOwnedByUs = false;
+            notifyFillBufferDone(outHeader);
+            return;
+        }
+
+        int32_t numPageSamples;
+        CHECK_GE(inHeader->nFilledLen, sizeof(numPageSamples));
+        memcpy(&numPageSamples,
+               inHeader->pBuffer
+                + inHeader->nOffset + inHeader->nFilledLen - 4,
+               sizeof(numPageSamples));
+
+        if (numPageSamples >= 0) {
+            mNumFramesLeftOnPage = numPageSamples;
+        }
+
+        if (inHeader->nOffset == 0) {
+            mAnchorTimeUs = inHeader->nTimeStamp;
+            mNumFramesOutput = 0;
+        }
+
+        inHeader->nFilledLen -= sizeof(numPageSamples);;
+
+        ogg_buffer buf;
+        buf.data = inHeader->pBuffer + inHeader->nOffset;
+        buf.size = inHeader->nFilledLen;
+        buf.refcount = 1;
+        buf.ptr.owner = NULL;
+
+        ogg_reference ref;
+        ref.buffer = &buf;
+        ref.begin = 0;
+        ref.length = buf.size;
+        ref.next = NULL;
+
+        ogg_packet pack;
+        pack.packet = &ref;
+        pack.bytes = ref.length;
+        pack.b_o_s = 0;
+        pack.e_o_s = 0;
+        pack.granulepos = 0;
+        pack.packetno = 0;
+
+        int numFrames = 0;
+
+        int err = vorbis_dsp_synthesis(mState, &pack, 1);
+        if (err != 0) {
+            LOGW("vorbis_dsp_synthesis returned %d", err);
+        } else {
+            numFrames = vorbis_dsp_pcmout(
+                    mState, (int16_t *)outHeader->pBuffer,
+                    kMaxNumSamplesPerBuffer);
+
+            if (numFrames < 0) {
+                LOGE("vorbis_dsp_pcmout returned %d", numFrames);
+                numFrames = 0;
+            }
+        }
+
+        if (mNumFramesLeftOnPage >= 0) {
+            if (numFrames > mNumFramesLeftOnPage) {
+                LOGV("discarding %d frames at end of page",
+                     numFrames - mNumFramesLeftOnPage);
+                numFrames = mNumFramesLeftOnPage;
+            }
+            mNumFramesLeftOnPage -= numFrames;
+        }
+
+        outHeader->nFilledLen = numFrames * sizeof(int16_t) * mVi->channels;
+        outHeader->nOffset = 0;
+        outHeader->nFlags = 0;
+
+        outHeader->nTimeStamp =
+            mAnchorTimeUs
+                + (mNumFramesOutput * 1000000ll) / mVi->rate;
+
+        mNumFramesOutput += numFrames;
+
+        inInfo->mOwnedByUs = false;
+        inQueue.erase(inQueue.begin());
+        inInfo = NULL;
+        notifyEmptyBufferDone(inHeader);
+        inHeader = NULL;
+
+        outInfo->mOwnedByUs = false;
+        outQueue.erase(outQueue.begin());
+        outInfo = NULL;
+        notifyFillBufferDone(outHeader);
+        outHeader = NULL;
+
+        ++mInputBufferCount;
+    }
+}
+
+void SoftVorbis::onPortFlushCompleted(OMX_U32 portIndex) {
+    if (portIndex == 0 && mState != NULL) {
+        // Make sure that the next buffer output does not still
+        // depend on fragments from the last one decoded.
+
+        mNumFramesOutput = 0;
+        vorbis_dsp_restart(mState);
+    }
+}
+
+void SoftVorbis::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
+    if (portIndex != 1) {
+        return;
+    }
+
+    switch (mOutputPortSettingsChange) {
+        case NONE:
+            break;
+
+        case AWAITING_DISABLED:
+        {
+            CHECK(!enabled);
+            mOutputPortSettingsChange = AWAITING_ENABLED;
+            break;
+        }
+
+        default:
+        {
+            CHECK_EQ((int)mOutputPortSettingsChange, (int)AWAITING_ENABLED);
+            CHECK(enabled);
+            mOutputPortSettingsChange = NONE;
+            break;
+        }
+    }
+}
+
+}  // namespace android
+
+android::SoftOMXComponent *createSoftOMXComponent(
+        const char *name, const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
+    return new android::SoftVorbis(name, callbacks, appData, component);
+}
diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h
new file mode 100644
index 0000000..e252f55
--- /dev/null
+++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef SOFT_VORBIS_H_
+
+#define SOFT_VORBIS_H_
+
+#include "SimpleSoftOMXComponent.h"
+
+struct vorbis_dsp_state;
+struct vorbis_info;
+
+namespace android {
+
+struct SoftVorbis : public SimpleSoftOMXComponent {
+    SoftVorbis(const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+protected:
+    virtual ~SoftVorbis();
+
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+    virtual void onPortFlushCompleted(OMX_U32 portIndex);
+    virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+private:
+    enum {
+        kNumBuffers = 4,
+        kMaxNumSamplesPerBuffer = 8192 * 2
+    };
+
+    size_t mInputBufferCount;
+
+    vorbis_dsp_state *mState;
+    vorbis_info *mVi;
+
+    int64_t mAnchorTimeUs;
+    int64_t mNumFramesOutput;
+    int32_t mNumFramesLeftOnPage;
+
+    enum {
+        NONE,
+        AWAITING_DISABLED,
+        AWAITING_ENABLED
+    } mOutputPortSettingsChange;
+
+    void initPorts();
+    status_t initDecoder();
+    bool isConfigured() const;
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftVorbis);
+};
+
+}  // namespace android
+
+#endif  // SOFT_VORBIS_H_
+
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 3b13476..ee861484 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -77,8 +77,6 @@
             GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN
             | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP));
 
-    CHECK_EQ(0, native_window_set_buffer_count(mNativeWindow.get(), 2));
-
     // Width must be multiple of 32???
     CHECK_EQ(0, native_window_set_buffers_geometry(
                 mNativeWindow.get(),
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index fd3ddf7..835d2bb 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -281,6 +281,7 @@
     void ensureCacheIsFetching_l();
 
     status_t startAudioPlayer_l();
+    void postAudioSeekComplete_l();
 
     void shutdownVideoDecoder_l();
     void setNativeWindow_l(const sp<ANativeWindow> &native);
diff --git a/media/libstagefright/include/SimpleSoftOMXComponent.h b/media/libstagefright/include/SimpleSoftOMXComponent.h
new file mode 100644
index 0000000..2a29a7d
--- /dev/null
+++ b/media/libstagefright/include/SimpleSoftOMXComponent.h
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+#ifndef SIMPLE_SOFT_OMX_COMPONENT_H_
+
+#define SIMPLE_SOFT_OMX_COMPONENT_H_
+
+#include "SoftOMXComponent.h"
+
+#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct ALooper;
+
+struct SimpleSoftOMXComponent : public SoftOMXComponent {
+    SimpleSoftOMXComponent(
+            const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+    virtual ~SimpleSoftOMXComponent();
+
+    void onMessageReceived(const sp<AMessage> &msg);
+
+protected:
+    struct BufferInfo {
+        OMX_BUFFERHEADERTYPE *mHeader;
+        bool mOwnedByUs;
+    };
+
+    struct PortInfo {
+        OMX_PARAM_PORTDEFINITIONTYPE mDef;
+        Vector<BufferInfo> mBuffers;
+        List<BufferInfo *> mQueue;
+
+        enum {
+            NONE,
+            DISABLING,
+            ENABLING,
+        } mTransition;
+    };
+
+    void addPort(const OMX_PARAM_PORTDEFINITIONTYPE &def);
+
+    virtual OMX_ERRORTYPE internalGetParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE internalSetParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual void onQueueFilled(OMX_U32 portIndex);
+    List<BufferInfo *> &getPortQueue(OMX_U32 portIndex);
+
+    virtual void onPortFlushCompleted(OMX_U32 portIndex);
+    virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
+
+    PortInfo *editPortInfo(OMX_U32 portIndex);
+
+private:
+    enum {
+        kWhatSendCommand,
+        kWhatEmptyThisBuffer,
+        kWhatFillThisBuffer,
+    };
+
+    Mutex mLock;
+
+    sp<ALooper> mLooper;
+    sp<AHandlerReflector<SimpleSoftOMXComponent> > mHandler;
+
+    OMX_STATETYPE mState;
+    OMX_STATETYPE mTargetState;
+
+    Vector<PortInfo> mPorts;
+
+    bool isSetParameterAllowed(
+            OMX_INDEXTYPE index, const OMX_PTR params) const;
+
+    virtual OMX_ERRORTYPE sendCommand(
+            OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data);
+
+    virtual OMX_ERRORTYPE getParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE setParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual OMX_ERRORTYPE useBuffer(
+            OMX_BUFFERHEADERTYPE **buffer,
+            OMX_U32 portIndex,
+            OMX_PTR appPrivate,
+            OMX_U32 size,
+            OMX_U8 *ptr);
+
+    virtual OMX_ERRORTYPE allocateBuffer(
+            OMX_BUFFERHEADERTYPE **buffer,
+            OMX_U32 portIndex,
+            OMX_PTR appPrivate,
+            OMX_U32 size);
+
+    virtual OMX_ERRORTYPE freeBuffer(
+            OMX_U32 portIndex,
+            OMX_BUFFERHEADERTYPE *buffer);
+
+    virtual OMX_ERRORTYPE emptyThisBuffer(
+            OMX_BUFFERHEADERTYPE *buffer);
+
+    virtual OMX_ERRORTYPE fillThisBuffer(
+            OMX_BUFFERHEADERTYPE *buffer);
+
+    virtual OMX_ERRORTYPE getState(OMX_STATETYPE *state);
+
+    void onSendCommand(OMX_COMMANDTYPE cmd, OMX_U32 param);
+    void onChangeState(OMX_STATETYPE state);
+    void onPortEnable(OMX_U32 portIndex, bool enable);
+    void onPortFlush(OMX_U32 portIndex, bool sendFlushComplete);
+
+    void checkTransitions();
+
+    DISALLOW_EVIL_CONSTRUCTORS(SimpleSoftOMXComponent);
+};
+
+}  // namespace android
+
+#endif  // SIMPLE_SOFT_OMX_COMPONENT_H_
diff --git a/media/libstagefright/include/SoftOMXComponent.h b/media/libstagefright/include/SoftOMXComponent.h
new file mode 100644
index 0000000..053bc22
--- /dev/null
+++ b/media/libstagefright/include/SoftOMXComponent.h
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+#ifndef SOFT_OMX_COMPONENT_H_
+
+#define SOFT_OMX_COMPONENT_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/RefBase.h>
+
+#include <OMX_Component.h>
+
+namespace android {
+
+struct SoftOMXComponent : public RefBase {
+    SoftOMXComponent(
+            const char *name,
+            const OMX_CALLBACKTYPE *callbacks,
+            OMX_PTR appData,
+            OMX_COMPONENTTYPE **component);
+
+    virtual OMX_ERRORTYPE initCheck() const;
+
+    void setLibHandle(void *libHandle);
+    void *libHandle() const;
+
+protected:
+    virtual ~SoftOMXComponent();
+
+    const char *name() const;
+
+    void notify(
+            OMX_EVENTTYPE event,
+            OMX_U32 data1, OMX_U32 data2, OMX_PTR data);
+
+    void notifyEmptyBufferDone(OMX_BUFFERHEADERTYPE *header);
+    void notifyFillBufferDone(OMX_BUFFERHEADERTYPE *header);
+
+    virtual OMX_ERRORTYPE sendCommand(
+            OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data);
+
+    virtual OMX_ERRORTYPE getParameter(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE setParameter(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual OMX_ERRORTYPE getConfig(
+            OMX_INDEXTYPE index, OMX_PTR params);
+
+    virtual OMX_ERRORTYPE setConfig(
+            OMX_INDEXTYPE index, const OMX_PTR params);
+
+    virtual OMX_ERRORTYPE getExtensionIndex(
+            const char *name, OMX_INDEXTYPE *index);
+
+    virtual OMX_ERRORTYPE useBuffer(
+            OMX_BUFFERHEADERTYPE **buffer,
+            OMX_U32 portIndex,
+            OMX_PTR appPrivate,
+            OMX_U32 size,
+            OMX_U8 *ptr);
+
+    virtual OMX_ERRORTYPE allocateBuffer(
+            OMX_BUFFERHEADERTYPE **buffer,
+            OMX_U32 portIndex,
+            OMX_PTR appPrivate,
+            OMX_U32 size);
+
+    virtual OMX_ERRORTYPE freeBuffer(
+            OMX_U32 portIndex,
+            OMX_BUFFERHEADERTYPE *buffer);
+
+    virtual OMX_ERRORTYPE emptyThisBuffer(
+            OMX_BUFFERHEADERTYPE *buffer);
+
+    virtual OMX_ERRORTYPE fillThisBuffer(
+            OMX_BUFFERHEADERTYPE *buffer);
+
+    virtual OMX_ERRORTYPE getState(OMX_STATETYPE *state);
+
+private:
+    AString mName;
+    const OMX_CALLBACKTYPE *mCallbacks;
+    OMX_COMPONENTTYPE *mComponent;
+
+    void *mLibHandle;
+
+    static OMX_ERRORTYPE SendCommandWrapper(
+            OMX_HANDLETYPE component,
+            OMX_COMMANDTYPE cmd,
+            OMX_U32 param,
+            OMX_PTR data);
+
+    static OMX_ERRORTYPE GetParameterWrapper(
+            OMX_HANDLETYPE component,
+            OMX_INDEXTYPE index,
+            OMX_PTR params);
+
+    static OMX_ERRORTYPE SetParameterWrapper(
+            OMX_HANDLETYPE component,
+            OMX_INDEXTYPE index,
+            OMX_PTR params);
+
+    static OMX_ERRORTYPE GetConfigWrapper(
+            OMX_HANDLETYPE component,
+            OMX_INDEXTYPE index,
+            OMX_PTR params);
+
+    static OMX_ERRORTYPE SetConfigWrapper(
+            OMX_HANDLETYPE component,
+            OMX_INDEXTYPE index,
+            OMX_PTR params);
+
+    static OMX_ERRORTYPE GetExtensionIndexWrapper(
+            OMX_HANDLETYPE component,
+            OMX_STRING name,
+            OMX_INDEXTYPE *index);
+
+    static OMX_ERRORTYPE UseBufferWrapper(
+            OMX_HANDLETYPE component,
+            OMX_BUFFERHEADERTYPE **buffer,
+            OMX_U32 portIndex,
+            OMX_PTR appPrivate,
+            OMX_U32 size,
+            OMX_U8 *ptr);
+
+    static OMX_ERRORTYPE AllocateBufferWrapper(
+            OMX_HANDLETYPE component,
+            OMX_BUFFERHEADERTYPE **buffer,
+            OMX_U32 portIndex,
+            OMX_PTR appPrivate,
+            OMX_U32 size);
+
+    static OMX_ERRORTYPE FreeBufferWrapper(
+            OMX_HANDLETYPE component,
+            OMX_U32 portIndex,
+            OMX_BUFFERHEADERTYPE *buffer);
+
+    static OMX_ERRORTYPE EmptyThisBufferWrapper(
+            OMX_HANDLETYPE component,
+            OMX_BUFFERHEADERTYPE *buffer);
+
+    static OMX_ERRORTYPE FillThisBufferWrapper(
+            OMX_HANDLETYPE component,
+            OMX_BUFFERHEADERTYPE *buffer);
+
+    static OMX_ERRORTYPE GetStateWrapper(
+            OMX_HANDLETYPE component,
+            OMX_STATETYPE *state);
+
+    DISALLOW_EVIL_CONSTRUCTORS(SoftOMXComponent);
+};
+
+}  // namespace android
+
+#endif  // SOFT_OMX_COMPONENT_H_
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index 6e069c8..08ad6f3 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -1,41 +1,28 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-ifneq ($(BUILD_WITHOUT_PV),true)
-# Set up the OpenCore variables.
-include external/opencore/Config.mk
-LOCAL_C_INCLUDES := $(PV_INCLUDES)
-LOCAL_CFLAGS := $(PV_CFLAGS_MINUS_VISIBILITY)
-endif
-
 LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
 
 LOCAL_SRC_FILES:=                     \
-	OMX.cpp                       \
+        OMX.cpp                       \
         OMXComponentBase.cpp          \
+        OMXMaster.cpp                 \
         OMXNodeInstance.cpp           \
-        OMXMaster.cpp
+        SimpleSoftOMXComponent.cpp    \
+        SoftOMXComponent.cpp          \
+        SoftOMXPlugin.cpp             \
 
-ifneq ($(BUILD_WITHOUT_PV),true)
-LOCAL_SRC_FILES += \
-        OMXPVCodecsPlugin.cpp
-else
-LOCAL_CFLAGS += -DNO_OPENCORE
-endif
+LOCAL_C_INCLUDES += \
+        frameworks/base/media/libstagefright \
+        $(TOP)/frameworks/base/include/media/stagefright/openmax
 
-LOCAL_C_INCLUDES += $(TOP)/frameworks/base/include/media/stagefright/openmax
-
-LOCAL_SHARED_LIBRARIES :=       \
-        libbinder               \
-        libmedia                \
-        libutils                \
-        libui                   \
-        libcutils               \
-
-ifneq ($(BUILD_WITHOUT_PV),true)
-LOCAL_SHARED_LIBRARIES += \
-        libopencore_common
-endif
+LOCAL_SHARED_LIBRARIES :=               \
+        libbinder                       \
+        libmedia                        \
+        libutils                        \
+        libui                           \
+        libcutils                       \
+        libstagefright_foundation       \
 
 ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
         LOCAL_LDLIBS += -lpthread -ldl
@@ -49,5 +36,6 @@
 
 include $(BUILD_SHARED_LIBRARY)
 
-include $(call all-makefiles-under,$(LOCAL_PATH))
+################################################################################
 
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp
index 56b169a..545e6d4 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXMaster.cpp
@@ -20,23 +20,18 @@
 
 #include "OMXMaster.h"
 
+#include "SoftOMXPlugin.h"
+
 #include <dlfcn.h>
 
 #include <media/stagefright/MediaDebug.h>
 
-#ifndef NO_OPENCORE
-#include "OMXPVCodecsPlugin.h"
-#endif
-
 namespace android {
 
 OMXMaster::OMXMaster()
     : mVendorLibHandle(NULL) {
     addVendorPlugin();
-
-#ifndef NO_OPENCORE
-    addPlugin(new OMXPVCodecsPlugin);
-#endif
+    addPlugin(new SoftOMXPlugin);
 }
 
 OMXMaster::~OMXMaster() {
@@ -49,7 +44,11 @@
 }
 
 void OMXMaster::addVendorPlugin() {
-    mVendorLibHandle = dlopen("libstagefrighthw.so", RTLD_NOW);
+    addPlugin("libstagefrighthw.so");
+}
+
+void OMXMaster::addPlugin(const char *libname) {
+    mVendorLibHandle = dlopen(libname, RTLD_NOW);
 
     if (mVendorLibHandle == NULL) {
         return;
diff --git a/media/libstagefright/omx/OMXMaster.h b/media/libstagefright/omx/OMXMaster.h
index 7ba8d18..feee1f9 100644
--- a/media/libstagefright/omx/OMXMaster.h
+++ b/media/libstagefright/omx/OMXMaster.h
@@ -58,6 +58,7 @@
     void *mVendorLibHandle;
 
     void addVendorPlugin();
+    void addPlugin(const char *libname);
     void addPlugin(OMXPluginBase *plugin);
     void clearPlugins();
 
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index cdce772..8462988 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -234,6 +234,7 @@
     Mutex::Autolock autoLock(mLock);
 
     OMX_ERRORTYPE err = OMX_GetParameter(mHandle, index, params);
+
     return StatusFromOMXError(err);
 }
 
diff --git a/media/libstagefright/omx/OMXPVCodecsPlugin.cpp b/media/libstagefright/omx/OMXPVCodecsPlugin.cpp
deleted file mode 100644
index d1f5be3..0000000
--- a/media/libstagefright/omx/OMXPVCodecsPlugin.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "OMXPVCodecsPlugin.h"
-
-#include "pv_omxcore.h"
-
-#include <media/stagefright/MediaDebug.h>
-
-namespace android {
-
-OMXPVCodecsPlugin::OMXPVCodecsPlugin() {
-    OMX_MasterInit();
-}
-
-OMXPVCodecsPlugin::~OMXPVCodecsPlugin() {
-    OMX_MasterDeinit();
-}
-
-OMX_ERRORTYPE OMXPVCodecsPlugin::makeComponentInstance(
-        const char *name,
-        const OMX_CALLBACKTYPE *callbacks,
-        OMX_PTR appData,
-        OMX_COMPONENTTYPE **component) {
-    return OMX_MasterGetHandle(
-            reinterpret_cast<OMX_HANDLETYPE *>(component),
-            const_cast<char *>(name),
-            appData,
-            const_cast<OMX_CALLBACKTYPE *>(callbacks));
-}
-
-OMX_ERRORTYPE OMXPVCodecsPlugin::destroyComponentInstance(
-        OMX_COMPONENTTYPE *component) {
-    return OMX_MasterFreeHandle(component);
-}
-
-OMX_ERRORTYPE OMXPVCodecsPlugin::enumerateComponents(
-        OMX_STRING name,
-        size_t size,
-        OMX_U32 index) {
-    return OMX_MasterComponentNameEnum(name, size, index);
-}
-
-OMX_ERRORTYPE OMXPVCodecsPlugin::getRolesOfComponent(
-        const char *name,
-        Vector<String8> *roles) {
-    roles->clear();
-
-    OMX_U32 numRoles;
-    OMX_ERRORTYPE err =
-        OMX_MasterGetRolesOfComponent(
-                const_cast<char *>(name),
-                &numRoles,
-                NULL);
-
-    if (err != OMX_ErrorNone) {
-        return err;
-    }
-
-    if (numRoles > 0) {
-        OMX_U8 **array = new OMX_U8 *[numRoles];
-        for (OMX_U32 i = 0; i < numRoles; ++i) {
-            array[i] = new OMX_U8[OMX_MAX_STRINGNAME_SIZE];
-        }
-
-        OMX_U32 numRoles2;
-        err = OMX_MasterGetRolesOfComponent(
-                const_cast<char *>(name), &numRoles2, array);
-
-        CHECK_EQ(err, OMX_ErrorNone);
-        CHECK_EQ(numRoles, numRoles2);
-
-        for (OMX_U32 i = 0; i < numRoles; ++i) {
-            String8 s((const char *)array[i]);
-            roles->push(s);
-
-            delete[] array[i];
-            array[i] = NULL;
-        }
-
-        delete[] array;
-        array = NULL;
-    }
-
-    return OMX_ErrorNone;
-}
-
-}  // namespace android
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
new file mode 100644
index 0000000..179b2a0
--- /dev/null
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -0,0 +1,640 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "SimpleSoftOMXComponent"
+#include <utils/Log.h>
+
+#include "include/SimpleSoftOMXComponent.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+SimpleSoftOMXComponent::SimpleSoftOMXComponent(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : SoftOMXComponent(name, callbacks, appData, component),
+      mLooper(new ALooper),
+      mHandler(new AHandlerReflector<SimpleSoftOMXComponent>(this)),
+      mState(OMX_StateLoaded),
+      mTargetState(OMX_StateLoaded) {
+    mLooper->setName(name);
+    mLooper->registerHandler(mHandler);
+
+    mLooper->start(
+            false, // runOnCallingThread
+            false, // canCallJava
+            PRIORITY_AUDIO);
+}
+
+SimpleSoftOMXComponent::~SimpleSoftOMXComponent() {
+    mLooper->unregisterHandler(mHandler->id());
+    mLooper->stop();
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::sendCommand(
+        OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) {
+    CHECK(data == NULL);
+
+    sp<AMessage> msg = new AMessage(kWhatSendCommand, mHandler->id());
+    msg->setInt32("cmd", cmd);
+    msg->setInt32("param", param);
+    msg->post();
+
+    return OMX_ErrorNone;
+}
+
+bool SimpleSoftOMXComponent::isSetParameterAllowed(
+        OMX_INDEXTYPE index, const OMX_PTR params) const {
+    if (mState == OMX_StateLoaded) {
+        return true;
+    }
+
+    OMX_U32 portIndex;
+
+    switch (index) {
+        case OMX_IndexParamPortDefinition:
+        {
+            portIndex = ((OMX_PARAM_PORTDEFINITIONTYPE *)params)->nPortIndex;
+            break;
+        }
+
+        case OMX_IndexParamAudioPcm:
+        {
+            portIndex = ((OMX_AUDIO_PARAM_PCMMODETYPE *)params)->nPortIndex;
+            break;
+        }
+
+        case OMX_IndexParamAudioAac:
+        {
+            portIndex = ((OMX_AUDIO_PARAM_AACPROFILETYPE *)params)->nPortIndex;
+            break;
+        }
+
+        default:
+            return false;
+    }
+
+    CHECK(portIndex < mPorts.size());
+
+    return !mPorts.itemAt(portIndex).mDef.bEnabled;
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::getParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    Mutex::Autolock autoLock(mLock);
+    return internalGetParameter(index, params);
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::setParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    Mutex::Autolock autoLock(mLock);
+
+    CHECK(isSetParameterAllowed(index, params));
+
+    return internalSetParameter(index, params);
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::internalGetParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamPortDefinition:
+        {
+            OMX_PARAM_PORTDEFINITIONTYPE *defParams =
+                (OMX_PARAM_PORTDEFINITIONTYPE *)params;
+
+            if (defParams->nPortIndex >= mPorts.size()
+                    || defParams->nSize
+                            != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) {
+                return OMX_ErrorUndefined;
+            }
+
+            const PortInfo *port =
+                &mPorts.itemAt(defParams->nPortIndex);
+
+            memcpy(defParams, &port->mDef, sizeof(port->mDef));
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return OMX_ErrorUnsupportedIndex;
+    }
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::internalSetParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    switch (index) {
+        case OMX_IndexParamPortDefinition:
+        {
+            OMX_PARAM_PORTDEFINITIONTYPE *defParams =
+                (OMX_PARAM_PORTDEFINITIONTYPE *)params;
+
+            if (defParams->nPortIndex >= mPorts.size()
+                    || defParams->nSize
+                            != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) {
+                return OMX_ErrorUndefined;
+            }
+
+            PortInfo *port =
+                &mPorts.editItemAt(defParams->nPortIndex);
+
+            if (defParams->nBufferSize != port->mDef.nBufferSize) {
+                CHECK_GE(defParams->nBufferSize, port->mDef.nBufferSize);
+                port->mDef.nBufferSize = defParams->nBufferSize;
+            }
+
+            if (defParams->nBufferCountActual
+                    != port->mDef.nBufferCountActual) {
+                CHECK_GE(defParams->nBufferCountActual,
+                         port->mDef.nBufferCountMin);
+
+                port->mDef.nBufferCountActual = defParams->nBufferCountActual;
+            }
+
+            return OMX_ErrorNone;
+        }
+
+        default:
+            return OMX_ErrorUnsupportedIndex;
+    }
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::useBuffer(
+        OMX_BUFFERHEADERTYPE **header,
+        OMX_U32 portIndex,
+        OMX_PTR appPrivate,
+        OMX_U32 size,
+        OMX_U8 *ptr) {
+    Mutex::Autolock autoLock(mLock);
+    CHECK_LT(portIndex, mPorts.size());
+
+    *header = new OMX_BUFFERHEADERTYPE;
+    (*header)->nSize = sizeof(OMX_BUFFERHEADERTYPE);
+    (*header)->nVersion.s.nVersionMajor = 1;
+    (*header)->nVersion.s.nVersionMinor = 0;
+    (*header)->nVersion.s.nRevision = 0;
+    (*header)->nVersion.s.nStep = 0;
+    (*header)->pBuffer = ptr;
+    (*header)->nAllocLen = size;
+    (*header)->nFilledLen = 0;
+    (*header)->nOffset = 0;
+    (*header)->pAppPrivate = appPrivate;
+    (*header)->pPlatformPrivate = NULL;
+    (*header)->pInputPortPrivate = NULL;
+    (*header)->pOutputPortPrivate = NULL;
+    (*header)->hMarkTargetComponent = NULL;
+    (*header)->pMarkData = NULL;
+    (*header)->nTickCount = 0;
+    (*header)->nTimeStamp = 0;
+    (*header)->nFlags = 0;
+    (*header)->nOutputPortIndex = portIndex;
+    (*header)->nInputPortIndex = portIndex;
+
+    PortInfo *port = &mPorts.editItemAt(portIndex);
+
+    CHECK(mState == OMX_StateLoaded || port->mDef.bEnabled == OMX_FALSE);
+
+    CHECK_LT(port->mBuffers.size(), port->mDef.nBufferCountActual);
+
+    port->mBuffers.push();
+
+    BufferInfo *buffer =
+        &port->mBuffers.editItemAt(port->mBuffers.size() - 1);
+
+    buffer->mHeader = *header;
+    buffer->mOwnedByUs = false;
+
+    if (port->mBuffers.size() == port->mDef.nBufferCountActual) {
+        port->mDef.bPopulated = OMX_TRUE;
+        checkTransitions();
+    }
+
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::allocateBuffer(
+        OMX_BUFFERHEADERTYPE **header,
+        OMX_U32 portIndex,
+        OMX_PTR appPrivate,
+        OMX_U32 size) {
+    OMX_U8 *ptr = new OMX_U8[size];
+
+    OMX_ERRORTYPE err =
+        useBuffer(header, portIndex, appPrivate, size, ptr);
+
+    if (err != OMX_ErrorNone) {
+        delete[] ptr;
+        ptr = NULL;
+
+        return err;
+    }
+
+    CHECK((*header)->pPlatformPrivate == NULL);
+    (*header)->pPlatformPrivate = ptr;
+
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::freeBuffer(
+        OMX_U32 portIndex,
+        OMX_BUFFERHEADERTYPE *header) {
+    Mutex::Autolock autoLock(mLock);
+
+    CHECK_LT(portIndex, mPorts.size());
+
+    PortInfo *port = &mPorts.editItemAt(portIndex);
+
+#if 0 // XXX
+    CHECK((mState == OMX_StateIdle && mTargetState == OMX_StateLoaded)
+            || port->mDef.bEnabled == OMX_FALSE);
+#endif
+
+    bool found = false;
+    for (size_t i = 0; i < port->mBuffers.size(); ++i) {
+        BufferInfo *buffer = &port->mBuffers.editItemAt(i);
+
+        if (buffer->mHeader == header) {
+            CHECK(!buffer->mOwnedByUs);
+
+            if (header->pPlatformPrivate != NULL) {
+                // This buffer's data was allocated by us.
+                CHECK(header->pPlatformPrivate == header->pBuffer);
+
+                delete[] header->pBuffer;
+                header->pBuffer = NULL;
+            }
+
+            delete header;
+            header = NULL;
+
+            port->mBuffers.removeAt(i);
+            port->mDef.bPopulated = OMX_FALSE;
+
+            checkTransitions();
+
+            found = true;
+            break;
+        }
+    }
+
+    CHECK(found);
+
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::emptyThisBuffer(
+        OMX_BUFFERHEADERTYPE *buffer) {
+    sp<AMessage> msg = new AMessage(kWhatEmptyThisBuffer, mHandler->id());
+    msg->setPointer("header", buffer);
+    msg->post();
+
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::fillThisBuffer(
+        OMX_BUFFERHEADERTYPE *buffer) {
+    sp<AMessage> msg = new AMessage(kWhatFillThisBuffer, mHandler->id());
+    msg->setPointer("header", buffer);
+    msg->post();
+
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SimpleSoftOMXComponent::getState(OMX_STATETYPE *state) {
+    Mutex::Autolock autoLock(mLock);
+
+    *state = mState;
+
+    return OMX_ErrorNone;
+}
+
+void SimpleSoftOMXComponent::onMessageReceived(const sp<AMessage> &msg) {
+    Mutex::Autolock autoLock(mLock);
+
+    switch (msg->what()) {
+        case kWhatSendCommand:
+        {
+            int32_t cmd, param;
+            CHECK(msg->findInt32("cmd", &cmd));
+            CHECK(msg->findInt32("param", &param));
+
+            onSendCommand((OMX_COMMANDTYPE)cmd, (OMX_U32)param);
+            break;
+        }
+
+        case kWhatEmptyThisBuffer:
+        case kWhatFillThisBuffer:
+        {
+            OMX_BUFFERHEADERTYPE *header;
+            CHECK(msg->findPointer("header", (void **)&header));
+
+            CHECK(mState == OMX_StateExecuting && mTargetState == mState);
+
+            bool found = false;
+            for (size_t i = 0; i < mPorts.size(); ++i) {
+                PortInfo *port = &mPorts.editItemAt(i);
+
+                for (size_t j = 0; j < port->mBuffers.size(); ++j) {
+                    BufferInfo *buffer = &port->mBuffers.editItemAt(j);
+
+                    if (buffer->mHeader == header) {
+                        CHECK(!buffer->mOwnedByUs);
+
+                        buffer->mOwnedByUs = true;
+
+                        CHECK((msg->what() == kWhatEmptyThisBuffer
+                                    && port->mDef.eDir == OMX_DirInput)
+                                || (port->mDef.eDir == OMX_DirOutput));
+
+                        port->mQueue.push_back(buffer);
+                        onQueueFilled(i);
+
+                        found = true;
+                        break;
+                    }
+                }
+            }
+
+            CHECK(found);
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+void SimpleSoftOMXComponent::onSendCommand(
+        OMX_COMMANDTYPE cmd, OMX_U32 param) {
+    switch (cmd) {
+        case OMX_CommandStateSet:
+        {
+            onChangeState((OMX_STATETYPE)param);
+            break;
+        }
+
+        case OMX_CommandPortEnable:
+        case OMX_CommandPortDisable:
+        {
+            onPortEnable(param, cmd == OMX_CommandPortEnable);
+            break;
+        }
+
+        case OMX_CommandFlush:
+        {
+            onPortFlush(param, true /* sendFlushComplete */);
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+void SimpleSoftOMXComponent::onChangeState(OMX_STATETYPE state) {
+    // We shouldn't be in a state transition already.
+    CHECK_EQ((int)mState, (int)mTargetState);
+
+    switch (mState) {
+        case OMX_StateLoaded:
+            CHECK_EQ((int)state, (int)OMX_StateIdle);
+            break;
+        case OMX_StateIdle:
+            CHECK(state == OMX_StateLoaded || state == OMX_StateExecuting);
+            break;
+        case OMX_StateExecuting:
+        {
+            CHECK_EQ((int)state, (int)OMX_StateIdle);
+
+            for (size_t i = 0; i < mPorts.size(); ++i) {
+                onPortFlush(i, false /* sendFlushComplete */);
+            }
+
+            mState = OMX_StateIdle;
+            notify(OMX_EventCmdComplete, OMX_CommandStateSet, state, NULL);
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+
+    mTargetState = state;
+
+    checkTransitions();
+}
+
+void SimpleSoftOMXComponent::onPortEnable(OMX_U32 portIndex, bool enable) {
+    CHECK_LT(portIndex, mPorts.size());
+
+    PortInfo *port = &mPorts.editItemAt(portIndex);
+    CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE);
+    CHECK(port->mDef.bEnabled == !enable);
+
+    if (!enable) {
+        port->mDef.bEnabled = OMX_FALSE;
+        port->mTransition = PortInfo::DISABLING;
+
+        for (size_t i = 0; i < port->mBuffers.size(); ++i) {
+            BufferInfo *buffer = &port->mBuffers.editItemAt(i);
+
+            if (buffer->mOwnedByUs) {
+                buffer->mOwnedByUs = false;
+
+                if (port->mDef.eDir == OMX_DirInput) {
+                    notifyEmptyBufferDone(buffer->mHeader);
+                } else {
+                    CHECK_EQ(port->mDef.eDir, OMX_DirOutput);
+                    notifyFillBufferDone(buffer->mHeader);
+                }
+            }
+        }
+
+        port->mQueue.clear();
+    } else {
+        port->mTransition = PortInfo::ENABLING;
+    }
+
+    checkTransitions();
+}
+
+void SimpleSoftOMXComponent::onPortFlush(
+        OMX_U32 portIndex, bool sendFlushComplete) {
+    if (portIndex == OMX_ALL) {
+        for (size_t i = 0; i < mPorts.size(); ++i) {
+            onPortFlush(i, sendFlushComplete);
+        }
+
+        if (sendFlushComplete) {
+            notify(OMX_EventCmdComplete, OMX_CommandFlush, OMX_ALL, NULL);
+        }
+
+        return;
+    }
+
+    CHECK_LT(portIndex, mPorts.size());
+
+    PortInfo *port = &mPorts.editItemAt(portIndex);
+    CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE);
+
+    for (size_t i = 0; i < port->mBuffers.size(); ++i) {
+        BufferInfo *buffer = &port->mBuffers.editItemAt(i);
+
+        if (!buffer->mOwnedByUs) {
+            continue;
+        }
+
+        buffer->mHeader->nFilledLen = 0;
+        buffer->mHeader->nOffset = 0;
+        buffer->mHeader->nFlags = 0;
+
+        buffer->mOwnedByUs = false;
+
+        if (port->mDef.eDir == OMX_DirInput) {
+            notifyEmptyBufferDone(buffer->mHeader);
+        } else {
+            CHECK_EQ(port->mDef.eDir, OMX_DirOutput);
+
+            notifyFillBufferDone(buffer->mHeader);
+        }
+    }
+
+    port->mQueue.clear();
+
+    if (sendFlushComplete) {
+        notify(OMX_EventCmdComplete, OMX_CommandFlush, portIndex, NULL);
+
+        onPortFlushCompleted(portIndex);
+    }
+}
+
+void SimpleSoftOMXComponent::checkTransitions() {
+    if (mState != mTargetState) {
+        bool transitionComplete = true;
+
+        if (mState == OMX_StateLoaded) {
+            CHECK_EQ((int)mTargetState, (int)OMX_StateIdle);
+
+            for (size_t i = 0; i < mPorts.size(); ++i) {
+                const PortInfo &port = mPorts.itemAt(i);
+                if (port.mDef.bEnabled == OMX_FALSE) {
+                    continue;
+                }
+
+                if (port.mDef.bPopulated == OMX_FALSE) {
+                    transitionComplete = false;
+                    break;
+                }
+            }
+        } else if (mTargetState == OMX_StateLoaded) {
+            CHECK_EQ((int)mState, (int)OMX_StateIdle);
+
+            for (size_t i = 0; i < mPorts.size(); ++i) {
+                const PortInfo &port = mPorts.itemAt(i);
+                if (port.mDef.bEnabled == OMX_FALSE) {
+                    continue;
+                }
+
+                size_t n = port.mBuffers.size();
+
+                if (n > 0) {
+                    CHECK_LE(n, port.mDef.nBufferCountActual);
+
+                    if (n == port.mDef.nBufferCountActual) {
+                        CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_TRUE);
+                    } else {
+                        CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_FALSE);
+                    }
+
+                    transitionComplete = false;
+                    break;
+                }
+            }
+        }
+
+        if (transitionComplete) {
+            mState = mTargetState;
+
+            notify(OMX_EventCmdComplete, OMX_CommandStateSet, mState, NULL);
+        }
+    }
+
+    for (size_t i = 0; i < mPorts.size(); ++i) {
+        PortInfo *port = &mPorts.editItemAt(i);
+
+        if (port->mTransition == PortInfo::DISABLING) {
+            if (port->mBuffers.empty()) {
+                LOGV("Port %d now disabled.", i);
+
+                port->mTransition = PortInfo::NONE;
+                notify(OMX_EventCmdComplete, OMX_CommandPortDisable, i, NULL);
+
+                onPortEnableCompleted(i, false /* enabled */);
+            }
+        } else if (port->mTransition == PortInfo::ENABLING) {
+            if (port->mDef.bPopulated == OMX_TRUE) {
+                LOGV("Port %d now enabled.", i);
+
+                port->mTransition = PortInfo::NONE;
+                port->mDef.bEnabled = OMX_TRUE;
+                notify(OMX_EventCmdComplete, OMX_CommandPortEnable, i, NULL);
+
+                onPortEnableCompleted(i, true /* enabled */);
+            }
+        }
+    }
+}
+
+void SimpleSoftOMXComponent::addPort(const OMX_PARAM_PORTDEFINITIONTYPE &def) {
+    CHECK_EQ(def.nPortIndex, mPorts.size());
+
+    mPorts.push();
+    PortInfo *info = &mPorts.editItemAt(mPorts.size() - 1);
+    info->mDef = def;
+    info->mTransition = PortInfo::NONE;
+}
+
+void SimpleSoftOMXComponent::onQueueFilled(OMX_U32 portIndex) {
+}
+
+void SimpleSoftOMXComponent::onPortFlushCompleted(OMX_U32 portIndex) {
+}
+
+void SimpleSoftOMXComponent::onPortEnableCompleted(
+        OMX_U32 portIndex, bool enabled) {
+}
+
+List<SimpleSoftOMXComponent::BufferInfo *> &
+SimpleSoftOMXComponent::getPortQueue(OMX_U32 portIndex) {
+    CHECK_LT(portIndex, mPorts.size());
+    return mPorts.editItemAt(portIndex).mQueue;
+}
+
+SimpleSoftOMXComponent::PortInfo *SimpleSoftOMXComponent::editPortInfo(
+        OMX_U32 portIndex) {
+    CHECK_LT(portIndex, mPorts.size());
+    return &mPorts.editItemAt(portIndex);
+}
+
+}  // namespace android
diff --git a/media/libstagefright/omx/SoftOMXComponent.cpp b/media/libstagefright/omx/SoftOMXComponent.cpp
new file mode 100644
index 0000000..b1c34dc
--- /dev/null
+++ b/media/libstagefright/omx/SoftOMXComponent.cpp
@@ -0,0 +1,326 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "SoftOMXComponent"
+#include <utils/Log.h>
+
+#include "include/SoftOMXComponent.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+SoftOMXComponent::SoftOMXComponent(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component)
+    : mName(name),
+      mCallbacks(callbacks),
+      mComponent(new OMX_COMPONENTTYPE),
+      mLibHandle(NULL) {
+    mComponent->nSize = sizeof(*mComponent);
+    mComponent->nVersion.s.nVersionMajor = 1;
+    mComponent->nVersion.s.nVersionMinor = 0;
+    mComponent->nVersion.s.nRevision = 0;
+    mComponent->nVersion.s.nStep = 0;
+    mComponent->pComponentPrivate = this;
+    mComponent->pApplicationPrivate = appData;
+
+    mComponent->GetComponentVersion = NULL;
+    mComponent->SendCommand = SendCommandWrapper;
+    mComponent->GetParameter = GetParameterWrapper;
+    mComponent->SetParameter = SetParameterWrapper;
+    mComponent->GetConfig = GetConfigWrapper;
+    mComponent->SetConfig = SetConfigWrapper;
+    mComponent->GetExtensionIndex = GetExtensionIndexWrapper;
+    mComponent->GetState = GetStateWrapper;
+    mComponent->ComponentTunnelRequest = NULL;
+    mComponent->UseBuffer = UseBufferWrapper;
+    mComponent->AllocateBuffer = AllocateBufferWrapper;
+    mComponent->FreeBuffer = FreeBufferWrapper;
+    mComponent->EmptyThisBuffer = EmptyThisBufferWrapper;
+    mComponent->FillThisBuffer = FillThisBufferWrapper;
+    mComponent->SetCallbacks = NULL;
+    mComponent->ComponentDeInit = NULL;
+    mComponent->UseEGLImage = NULL;
+    mComponent->ComponentRoleEnum = NULL;
+
+    *component = mComponent;
+}
+
+SoftOMXComponent::~SoftOMXComponent() {
+    delete mComponent;
+    mComponent = NULL;
+}
+
+void SoftOMXComponent::setLibHandle(void *libHandle) {
+    CHECK(libHandle != NULL);
+    mLibHandle = libHandle;
+}
+
+void *SoftOMXComponent::libHandle() const {
+    return mLibHandle;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::initCheck() const {
+    return OMX_ErrorNone;
+}
+
+const char *SoftOMXComponent::name() const {
+    return mName.c_str();
+}
+
+void SoftOMXComponent::notify(
+        OMX_EVENTTYPE event,
+        OMX_U32 data1, OMX_U32 data2, OMX_PTR data) {
+    (*mCallbacks->EventHandler)(
+            mComponent,
+            mComponent->pApplicationPrivate,
+            event,
+            data1,
+            data2,
+            data);
+}
+
+void SoftOMXComponent::notifyEmptyBufferDone(OMX_BUFFERHEADERTYPE *header) {
+    (*mCallbacks->EmptyBufferDone)(
+            mComponent, mComponent->pApplicationPrivate, header);
+}
+
+void SoftOMXComponent::notifyFillBufferDone(OMX_BUFFERHEADERTYPE *header) {
+    (*mCallbacks->FillBufferDone)(
+            mComponent, mComponent->pApplicationPrivate, header);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::SendCommandWrapper(
+        OMX_HANDLETYPE component,
+        OMX_COMMANDTYPE cmd,
+        OMX_U32 param,
+        OMX_PTR data) {
+    SoftOMXComponent *me =
+        (SoftOMXComponent *)
+            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+    return me->sendCommand(cmd, param, data);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::GetParameterWrapper(
+        OMX_HANDLETYPE component,
+        OMX_INDEXTYPE index,
+        OMX_PTR params) {
+    SoftOMXComponent *me =
+        (SoftOMXComponent *)
+            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+    return me->getParameter(index, params);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::SetParameterWrapper(
+        OMX_HANDLETYPE component,
+        OMX_INDEXTYPE index,
+        OMX_PTR params) {
+    SoftOMXComponent *me =
+        (SoftOMXComponent *)
+            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+    return me->setParameter(index, params);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::GetConfigWrapper(
+        OMX_HANDLETYPE component,
+        OMX_INDEXTYPE index,
+        OMX_PTR params) {
+    SoftOMXComponent *me =
+        (SoftOMXComponent *)
+            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+    return me->getConfig(index, params);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::SetConfigWrapper(
+        OMX_HANDLETYPE component,
+        OMX_INDEXTYPE index,
+        OMX_PTR params) {
+    SoftOMXComponent *me =
+        (SoftOMXComponent *)
+            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+    return me->setConfig(index, params);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::GetExtensionIndexWrapper(
+        OMX_HANDLETYPE component,
+        OMX_STRING name,
+        OMX_INDEXTYPE *index) {
+    SoftOMXComponent *me =
+        (SoftOMXComponent *)
+            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+    return me->getExtensionIndex(name, index);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::UseBufferWrapper(
+        OMX_HANDLETYPE component,
+        OMX_BUFFERHEADERTYPE **buffer,
+        OMX_U32 portIndex,
+        OMX_PTR appPrivate,
+        OMX_U32 size,
+        OMX_U8 *ptr) {
+    SoftOMXComponent *me =
+        (SoftOMXComponent *)
+            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+    return me->useBuffer(buffer, portIndex, appPrivate, size, ptr);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::AllocateBufferWrapper(
+        OMX_HANDLETYPE component,
+        OMX_BUFFERHEADERTYPE **buffer,
+        OMX_U32 portIndex,
+        OMX_PTR appPrivate,
+        OMX_U32 size) {
+    SoftOMXComponent *me =
+        (SoftOMXComponent *)
+            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+    return me->allocateBuffer(buffer, portIndex, appPrivate, size);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::FreeBufferWrapper(
+        OMX_HANDLETYPE component,
+        OMX_U32 portIndex,
+        OMX_BUFFERHEADERTYPE *buffer) {
+    SoftOMXComponent *me =
+        (SoftOMXComponent *)
+            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+    return me->freeBuffer(portIndex, buffer);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::EmptyThisBufferWrapper(
+        OMX_HANDLETYPE component,
+        OMX_BUFFERHEADERTYPE *buffer) {
+    SoftOMXComponent *me =
+        (SoftOMXComponent *)
+            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+    return me->emptyThisBuffer(buffer);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::FillThisBufferWrapper(
+        OMX_HANDLETYPE component,
+        OMX_BUFFERHEADERTYPE *buffer) {
+    SoftOMXComponent *me =
+        (SoftOMXComponent *)
+            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+    return me->fillThisBuffer(buffer);
+}
+
+// static
+OMX_ERRORTYPE SoftOMXComponent::GetStateWrapper(
+        OMX_HANDLETYPE component,
+        OMX_STATETYPE *state) {
+    SoftOMXComponent *me =
+        (SoftOMXComponent *)
+            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+    return me->getState(state);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+OMX_ERRORTYPE SoftOMXComponent::sendCommand(
+        OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) {
+    return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::getParameter(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::setParameter(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::getConfig(
+        OMX_INDEXTYPE index, OMX_PTR params) {
+    return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::setConfig(
+        OMX_INDEXTYPE index, const OMX_PTR params) {
+    return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::getExtensionIndex(
+        const char *name, OMX_INDEXTYPE *index) {
+    return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::useBuffer(
+        OMX_BUFFERHEADERTYPE **buffer,
+        OMX_U32 portIndex,
+        OMX_PTR appPrivate,
+        OMX_U32 size,
+        OMX_U8 *ptr) {
+    return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::allocateBuffer(
+        OMX_BUFFERHEADERTYPE **buffer,
+        OMX_U32 portIndex,
+        OMX_PTR appPrivate,
+        OMX_U32 size) {
+    return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::freeBuffer(
+        OMX_U32 portIndex,
+        OMX_BUFFERHEADERTYPE *buffer) {
+    return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::emptyThisBuffer(
+        OMX_BUFFERHEADERTYPE *buffer) {
+    return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::fillThisBuffer(
+        OMX_BUFFERHEADERTYPE *buffer) {
+    return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE SoftOMXComponent::getState(OMX_STATETYPE *state) {
+    return OMX_ErrorUndefined;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
new file mode 100644
index 0000000..6bd6624
--- /dev/null
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "SoftOMXPlugin"
+#include <utils/Log.h>
+
+#include "SoftOMXPlugin.h"
+#include "include/SoftOMXComponent.h"
+
+#include <media/stagefright/foundation/AString.h>
+
+#include <dlfcn.h>
+
+namespace android {
+
+static const struct {
+    const char *mName;
+    const char *mLibNameSuffix;
+    const char *mRole;
+
+} kComponents[] = {
+    { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
+    { "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },
+    { "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },
+    { "OMX.google.avc.decoder", "avcdec", "video_decoder.avc" },
+    { "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },
+    { "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },
+    { "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },
+    { "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" },
+    { "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" },
+    { "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" },
+    { "OMX.google.vpx.decoder", "vpxdec", "video_decoder.vpx" },
+};
+
+static const size_t kNumComponents =
+    sizeof(kComponents) / sizeof(kComponents[0]);
+
+SoftOMXPlugin::SoftOMXPlugin() {
+}
+
+OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance(
+        const char *name,
+        const OMX_CALLBACKTYPE *callbacks,
+        OMX_PTR appData,
+        OMX_COMPONENTTYPE **component) {
+    LOGV("makeComponentInstance '%s'", name);
+
+    for (size_t i = 0; i < kNumComponents; ++i) {
+        if (strcmp(name, kComponents[i].mName)) {
+            continue;
+        }
+
+        AString libName = "libstagefright_soft_";
+        libName.append(kComponents[i].mLibNameSuffix);
+        libName.append(".so");
+
+        void *libHandle = dlopen(libName.c_str(), RTLD_NOW);
+
+        if (libHandle == NULL) {
+            LOGE("unable to dlopen %s", libName.c_str());
+
+            return OMX_ErrorComponentNotFound;
+        }
+
+        typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)(
+                const char *, const OMX_CALLBACKTYPE *,
+                OMX_PTR, OMX_COMPONENTTYPE **);
+
+        CreateSoftOMXComponentFunc createSoftOMXComponent =
+            (CreateSoftOMXComponentFunc)dlsym(
+                    libHandle,
+                    "_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE"
+                    "PvPP17OMX_COMPONENTTYPE");
+
+        if (createSoftOMXComponent == NULL) {
+            dlclose(libHandle);
+            libHandle = NULL;
+
+            return OMX_ErrorComponentNotFound;
+        }
+
+        sp<SoftOMXComponent> codec =
+            (*createSoftOMXComponent)(name, callbacks, appData, component);
+
+        if (codec == NULL) {
+            dlclose(libHandle);
+            libHandle = NULL;
+
+            return OMX_ErrorInsufficientResources;
+        }
+
+        OMX_ERRORTYPE err = codec->initCheck();
+        if (err != OMX_ErrorNone) {
+            dlclose(libHandle);
+            libHandle = NULL;
+
+            return err;
+        }
+
+        codec->incStrong(this);
+        codec->setLibHandle(libHandle);
+
+        return OMX_ErrorNone;
+    }
+
+    return OMX_ErrorInvalidComponentName;
+}
+
+OMX_ERRORTYPE SoftOMXPlugin::destroyComponentInstance(
+        OMX_COMPONENTTYPE *component) {
+    SoftOMXComponent *me =
+        (SoftOMXComponent *)
+            ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
+
+    void *libHandle = me->libHandle();
+
+    me->decStrong(this);
+    me = NULL;
+
+    dlclose(libHandle);
+    libHandle = NULL;
+
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftOMXPlugin::enumerateComponents(
+        OMX_STRING name,
+        size_t size,
+        OMX_U32 index) {
+    if (index >= kNumComponents) {
+        return OMX_ErrorNoMore;
+    }
+
+    strcpy(name, kComponents[index].mName);
+
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE SoftOMXPlugin::getRolesOfComponent(
+        const char *name,
+        Vector<String8> *roles) {
+    for (size_t i = 0; i < kNumComponents; ++i) {
+        if (strcmp(name, kComponents[i].mName)) {
+            continue;
+        }
+
+        roles->clear();
+        roles->push(String8(kComponents[i].mRole));
+
+        return OMX_ErrorNone;
+    }
+
+    return OMX_ErrorInvalidComponentName;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/omx/OMXPVCodecsPlugin.h b/media/libstagefright/omx/SoftOMXPlugin.h
similarity index 76%
rename from media/libstagefright/omx/OMXPVCodecsPlugin.h
rename to media/libstagefright/omx/SoftOMXPlugin.h
index c133232..f93c323 100644
--- a/media/libstagefright/omx/OMXPVCodecsPlugin.h
+++ b/media/libstagefright/omx/SoftOMXPlugin.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef OMX_PV_CODECS_PLUGIN_H_
+#ifndef SOFT_OMX_PLUGIN_H_
 
-#define OMX_PV_CODECS_PLUGIN_H_
+#define SOFT_OMX_PLUGIN_H_
 
+#include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/OMXPluginBase.h>
 
 namespace android {
 
-struct OMXPVCodecsPlugin : public OMXPluginBase {
-    OMXPVCodecsPlugin();
-    virtual ~OMXPVCodecsPlugin();
+struct SoftOMXPlugin : public OMXPluginBase {
+    SoftOMXPlugin();
 
     virtual OMX_ERRORTYPE makeComponentInstance(
             const char *name,
@@ -45,10 +45,9 @@
             Vector<String8> *roles);
 
 private:
-    OMXPVCodecsPlugin(const OMXPVCodecsPlugin &);
-    OMXPVCodecsPlugin &operator=(const OMXPVCodecsPlugin &);
+    DISALLOW_EVIL_CONSTRUCTORS(SoftOMXPlugin);
 };
 
 }  // namespace android
 
-#endif  // OMX_PV_CODECS_PLUGIN_H_
+#endif  // SOFT_OMX_PLUGIN_H_
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index 54c0d77..a404f1f 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -29,6 +29,7 @@
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MediaSource.h>
@@ -454,6 +455,7 @@
         { "video_decoder.avc", "video/avc" },
         { "video_decoder.mpeg4", "video/mp4v-es" },
         { "video_decoder.h263", "video/3gpp" },
+        { "video_decoder.vpx", "video/x-vnd.on2.vp8" },
 
         // we appear to use this as a synonym to amrnb.
         { "audio_decoder.amr", "audio/3gpp" },
@@ -461,7 +463,10 @@
         { "audio_decoder.amrnb", "audio/3gpp" },
         { "audio_decoder.amrwb", "audio/amr-wb" },
         { "audio_decoder.aac", "audio/mp4a-latm" },
-        { "audio_decoder.mp3", "audio/mpeg" }
+        { "audio_decoder.mp3", "audio/mpeg" },
+        { "audio_decoder.vorbis", "audio/vorbis" },
+        { "audio_decoder.g711alaw", MEDIA_MIMETYPE_AUDIO_G711_ALAW },
+        { "audio_decoder.g711mlaw", MEDIA_MIMETYPE_AUDIO_G711_MLAW },
     };
 
     for (size_t i = 0; i < sizeof(kRoleToMime) / sizeof(kRoleToMime[0]); ++i) {
@@ -492,7 +497,15 @@
         { "audio/mp4a-latm",
           "file:///sdcard/media_api/video/H264_AAC.3gp" },
         { "audio/mpeg",
-          "file:///sdcard/media_api/music/MP3CBR.mp3" }
+          "file:///sdcard/media_api/music/MP3CBR.mp3" },
+        { "audio/vorbis",
+          "file:///sdcard/media_api/metaDataTestMedias/OGG/"
+          "When You Say Nothing At All.ogg" },
+        { "video/x-vnd.on2.vp8",
+          "file:///sdcard/media_api/webm/big-buck-bunny_trailer.webm" },
+        { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "file:///sdcard/M1F1-Alaw-AFsp.wav" },
+        { MEDIA_MIMETYPE_AUDIO_G711_MLAW,
+          "file:///sdcard/M1F1-mulaw-AFsp.wav" },
     };
 
     for (size_t i = 0; i < sizeof(kMimeToURL) / sizeof(kMimeToURL[0]); ++i) {
@@ -746,6 +759,10 @@
         const IOMX::ComponentInfo &info = *it;
         const char *componentName = info.mName.string();
 
+        if (strncmp(componentName, "OMX.google.", 11)) {
+            continue;
+        }
+
         for (List<String8>::const_iterator role_it = info.mRoles.begin();
              role_it != info.mRoles.end(); ++role_it) {
             const char *componentRole = (*role_it).string();
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..50a8ac8f
--- /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 6632f34..03afc82 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -230,6 +230,7 @@
     boolean mSafeMode;
     WindowState mStatusBar = null;
     boolean mStatusBarCanHide;
+    int mScreenMarginBottom;
     final ArrayList<WindowState> mStatusBarPanels = new ArrayList<WindowState>();
     WindowState mNavigationBar = null;
 
@@ -1070,6 +1071,14 @@
         return STATUS_BAR_LAYER;
     }
 
+    public int getNonDecorDisplayWidth(int fullWidth) {
+        return fullWidth;
+    }
+
+    public int getNonDecorDisplayHeight(int fullHeight) {
+        return fullHeight - mScreenMarginBottom;
+    }
+
     public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs) {
         return attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD;
     }
@@ -1219,6 +1228,8 @@
                 // The Configuration will be stable by now, so we can load this
                 mStatusBarCanHide = mContext.getResources().getBoolean(
                         com.android.internal.R.bool.config_statusBarCanHide);
+                mScreenMarginBottom = mContext.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.screen_margin_bottom);
 
                 break;
             case TYPE_NAVIGATION_BAR:
@@ -2869,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 46de933..5bca7ee 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -177,6 +177,14 @@
     return true;
 }
 
+static void scalePointerCoords(const PointerCoords* inCoords, size_t count, float scaleFactor,
+        PointerCoords* outCoords) {
+   for (size_t i = 0; i < count; i++) {
+       outCoords[i] = inCoords[i];
+       outCoords[i].scale(scaleFactor);
+   }
+}
+
 static void dumpRegion(String8& dump, const SkRegion& region) {
     if (region.isEmpty()) {
         dump.append("<empty>");
@@ -1603,6 +1611,7 @@
     target.flags = targetFlags;
     target.xOffset = - window->frameLeft;
     target.yOffset = - window->frameTop;
+    target.scaleFactor = window->scaleFactor;
     target.pointerIds = pointerIds;
 }
 
@@ -1616,6 +1625,7 @@
         target.xOffset = 0;
         target.yOffset = 0;
         target.pointerIds.clear();
+        target.scaleFactor = 1.0f;
     }
 }
 
@@ -1717,12 +1727,12 @@
         bool resumeWithAppendedMotionSample) {
 #if DEBUG_DISPATCH_CYCLE
     LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, "
-            "xOffset=%f, yOffset=%f, "
+            "xOffset=%f, yOffset=%f, scaleFactor=%f"
             "pointerIds=0x%x, "
             "resumeWithAppendedMotionSample=%s",
             connection->getInputChannelName(), inputTarget->flags,
             inputTarget->xOffset, inputTarget->yOffset,
-            inputTarget->pointerIds.value,
+            inputTarget->scaleFactor, inputTarget->pointerIds.value,
             toString(resumeWithAppendedMotionSample));
 #endif
 
@@ -1803,8 +1813,19 @@
             // consumed the motion event (or if the channel is broken).
             MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
             MotionSample* appendedMotionSample = motionEntry->lastSample;
-            status_t status = connection->inputPublisher.appendMotionSample(
-                    appendedMotionSample->eventTime, appendedMotionSample->pointerCoords);
+            status_t status;
+            if (motionEventDispatchEntry->scaleFactor == 1.0f) {
+                status = connection->inputPublisher.appendMotionSample(
+                        appendedMotionSample->eventTime, appendedMotionSample->pointerCoords);
+            } else {
+                PointerCoords scaledCoords[MAX_POINTERS];
+                for (size_t i = 0; i < motionEntry->pointerCount; i++) {
+                    scaledCoords[i] = appendedMotionSample->pointerCoords[i];
+                    scaledCoords[i].scale(motionEventDispatchEntry->scaleFactor);
+                }
+                status = connection->inputPublisher.appendMotionSample(
+                        appendedMotionSample->eventTime, scaledCoords);
+            }
             if (status == OK) {
 #if DEBUG_BATCHING
                 LOGD("channel '%s' ~ Successfully streamed new motion sample.",
@@ -1867,7 +1888,8 @@
     // This is a new event.
     // Enqueue a new dispatch entry onto the outbound queue for this connection.
     DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
-            inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset);
+            inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
+            inputTarget->scaleFactor);
     if (dispatchEntry->hasForegroundTarget()) {
         incrementPendingForegroundDispatchesLocked(eventEntry);
     }
@@ -1961,14 +1983,26 @@
             firstMotionSample = & motionEntry->firstSample;
         }
 
+        PointerCoords scaledCoords[MAX_POINTERS];
+        const PointerCoords* usingCoords = firstMotionSample->pointerCoords;
+
         // Set the X and Y offset depending on the input source.
-        float xOffset, yOffset;
+        float xOffset, yOffset, scaleFactor;
         if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
-            xOffset = dispatchEntry->xOffset;
-            yOffset = dispatchEntry->yOffset;
+            scaleFactor = dispatchEntry->scaleFactor;
+            xOffset = dispatchEntry->xOffset * scaleFactor;
+            yOffset = dispatchEntry->yOffset * scaleFactor;
+            if (scaleFactor != 1.0f) {
+                for (size_t i = 0; i < motionEntry->pointerCount; i++) {
+                    scaledCoords[i] = firstMotionSample->pointerCoords[i];
+                    scaledCoords[i].scale(scaleFactor);
+                }
+                usingCoords = scaledCoords;
+            }
         } else {
             xOffset = 0.0f;
             yOffset = 0.0f;
+            scaleFactor = 1.0f;
         }
 
         // Update the connection's input state.
@@ -1977,11 +2011,9 @@
         // Publish the motion event and the first motion sample.
         status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId,
                 motionEntry->source, action, flags, motionEntry->edgeFlags, motionEntry->metaState,
-                xOffset, yOffset,
-                motionEntry->xPrecision, motionEntry->yPrecision,
+                xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,
                 motionEntry->downTime, firstMotionSample->eventTime,
-                motionEntry->pointerCount, motionEntry->pointerIds,
-                firstMotionSample->pointerCoords);
+                motionEntry->pointerCount, motionEntry->pointerIds, usingCoords);
 
         if (status) {
             LOGE("channel '%s' ~ Could not publish motion event, "
@@ -1995,8 +2027,16 @@
             // 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, nextMotionSample->pointerCoords);
+                        nextMotionSample->eventTime, usingCoords);
                 if (status == NO_MEMORY) {
 #if DEBUG_DISPATCH_CYCLE
                     LOGD("channel '%s' ~ Shared memory buffer full.  Some motion samples will "
@@ -2234,18 +2274,21 @@
             }
 
             int32_t xOffset, yOffset;
+            float scaleFactor;
             const InputWindow* window = getWindowLocked(connection->inputChannel);
             if (window) {
                 xOffset = -window->frameLeft;
                 yOffset = -window->frameTop;
+                scaleFactor = window->scaleFactor;
             } else {
                 xOffset = 0;
                 yOffset = 0;
+                scaleFactor = 1.0f;
             }
 
             DispatchEntry* cancelationDispatchEntry =
                     mAllocator.obtainDispatchEntry(cancelationEventEntry, // increments ref
-                    0, xOffset, yOffset);
+                    0, xOffset, yOffset, scaleFactor);
             connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry);
 
             mAllocator.releaseEventEntry(cancelationEventEntry);
@@ -3244,7 +3287,7 @@
             const InputWindow& window = mWindows[i];
             dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
                     "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
-                    "frame=[%d,%d][%d,%d], "
+                    "frame=[%d,%d][%d,%d], scale=%f, "
                     "touchableRegion=",
                     i, window.name.string(),
                     toString(window.paused),
@@ -3255,7 +3298,8 @@
                     window.layoutParamsFlags, window.layoutParamsType,
                     window.layer,
                     window.frameLeft, window.frameTop,
-                    window.frameRight, window.frameBottom);
+                    window.frameRight, window.frameBottom,
+                    window.scaleFactor);
             dumpRegion(dump, window.touchableRegion);
             dump.appendFormat(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
                     window.ownerPid, window.ownerUid,
@@ -3803,13 +3847,14 @@
 
 InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry(
         EventEntry* eventEntry,
-        int32_t targetFlags, float xOffset, float yOffset) {
+        int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) {
     DispatchEntry* entry = mDispatchEntryPool.alloc();
     entry->eventEntry = eventEntry;
     eventEntry->refCount += 1;
     entry->targetFlags = targetFlags;
     entry->xOffset = xOffset;
     entry->yOffset = yOffset;
+    entry->scaleFactor = scaleFactor;
     entry->inProgress = false;
     entry->headMotionSample = NULL;
     entry->tailMotionSample = NULL;
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index af0153b..96ece32 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -132,6 +132,10 @@
     // (ignored for KeyEvents)
     float xOffset, yOffset;
 
+    // Scaling factor to apply to MotionEvent as it is delivered.
+    // (ignored for KeyEvents)
+    float scaleFactor;
+
     // The subset of pointer ids to include in motion events dispatched to this input target
     // if FLAG_SPLIT is set.
     BitSet32 pointerIds;
@@ -474,6 +478,7 @@
         int32_t targetFlags;
         float xOffset;
         float yOffset;
+        float scaleFactor;
 
         // True if dispatch has started.
         bool inProgress;
@@ -602,7 +607,7 @@
                 nsecs_t downTime, uint32_t pointerCount,
                 const int32_t* pointerIds, const PointerCoords* pointerCoords);
         DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry,
-                int32_t targetFlags, float xOffset, float yOffset);
+                int32_t targetFlags, float xOffset, float yOffset, float scaleFactor);
         CommandEntry* obtainCommandEntry(Command command);
 
         void releaseInjectionState(InjectionState* injectionState);
diff --git a/services/input/InputWindow.cpp b/services/input/InputWindow.cpp
index b552f6d..ccea9e4 100644
--- a/services/input/InputWindow.cpp
+++ b/services/input/InputWindow.cpp
@@ -29,8 +29,7 @@
 }
 
 bool InputWindow::frameContainsPoint(int32_t x, int32_t y) const {
-    return x >= frameLeft && x <= frameRight
-            && y >= frameTop && y <= frameBottom;
+    return x <= frameRight || y <= frameBottom;
 }
 
 bool InputWindow::isTrustedOverlay() const {
diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h
index 208353d..cde7294 100644
--- a/services/input/InputWindow.h
+++ b/services/input/InputWindow.h
@@ -133,6 +133,7 @@
     int32_t frameTop;
     int32_t frameRight;
     int32_t frameBottom;
+    float scaleFactor;
     SkRegion touchableRegion;
     bool visible;
     bool canReceiveKeys;
@@ -146,6 +147,11 @@
     bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
     bool frameContainsPoint(int32_t x, int32_t y) const;
 
+    /* These use the globalScale to convert a given screen offset to the
+     * corresponding location within the window.
+     */
+    int32_t displayToWindowX(int32_t x) const;
+
     /* Returns true if the window is of a trusted type that is allowed to silently
      * overlay other windows for the purpose of implementing the secure views feature.
      * Trusted overlays, such as IME windows, can partly obscure other windows without causing
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 da34644..586bb6b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -75,6 +75,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Proxy;
@@ -147,7 +148,7 @@
         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
     static final String TAG = "ActivityManager";
     static final boolean DEBUG = false;
-    static final boolean localLOGV = false;
+    static final boolean localLOGV = DEBUG;
     static final boolean DEBUG_SWITCH = localLOGV || false;
     static final boolean DEBUG_TASKS = localLOGV || false;
     static final boolean DEBUG_PAUSE = localLOGV || false;
@@ -163,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;
@@ -544,6 +545,12 @@
     ProcessRecord mHomeProcess;
     
     /**
+     * Packages that the user has asked to have run in screen size
+     * compatibility mode instead of filling the screen.
+     */
+    final HashSet<String> mScreenCompatPackages = new HashSet<String>();
+
+    /**
      * Set of PendingResultRecord objects that are currently active.
      */
     final HashSet mPendingResultRecords = new HashSet();
@@ -2074,6 +2081,74 @@
         }
     }
     
+    CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
+        return new CompatibilityInfo(ai, mConfiguration.screenLayout,
+                mScreenCompatPackages.contains(ai.packageName));
+    }
+
+    public void setPackageScreenCompatMode(String packageName, boolean compatEnabled) {
+        synchronized (this) {
+            ApplicationInfo ai = null;
+            try {
+                ai = AppGlobals.getPackageManager().
+                        getApplicationInfo(packageName, STOCK_PM_FLAGS);
+            } catch (RemoteException e) {
+            }
+            if (ai == null) {
+                Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName);
+                return;
+            }
+            boolean changed = false;
+            if (compatEnabled) {
+                if (!mScreenCompatPackages.contains(packageName)) {
+                    changed = true;
+                    mScreenCompatPackages.add(packageName);
+                }
+            } else {
+                if (mScreenCompatPackages.contains(packageName)) {
+                    changed = true;
+                    mScreenCompatPackages.remove(packageName);
+                }
+            }
+            if (changed) {
+                CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai);
+
+                // Tell all processes that loaded this package about the change.
+                for (int i=mLruProcesses.size()-1; i>=0; i--) {
+                    ProcessRecord app = mLruProcesses.get(i);
+                    if (!app.pkgList.contains(packageName)) {
+                        continue;
+                    }
+                    try {
+                        if (app.thread != null) {
+                            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc "
+                                    + app.processName + " new compat " + ci);
+                            app.thread.updatePackageCompatibilityInfo(packageName, ci);
+                        }
+                    } catch (Exception e) {
+                    }
+                }
+
+                // All activities that came from the packge must be
+                // restarted as if there was a config change.
+                for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
+                    ActivityRecord a = (ActivityRecord)mMainStack.mHistory.get(i);
+                    if (a.info.packageName.equals(packageName)) {
+                        a.forceNewConfig = true;
+                    }
+                }
+
+                ActivityRecord starting = mMainStack.topRunningActivityLocked(null);
+                if (starting != null) {
+                    mMainStack.ensureActivityConfigurationLocked(starting, 0);
+                    // And we need to make sure at this point that all other activities
+                    // are made visible with the correct configuration.
+                    mMainStack.ensureActivitiesVisibleLocked(starting, 0);
+                }
+            }
+        }
+    }
+
     void reportResumedActivityLocked(ActivityRecord r) {
         //Slog.i(TAG, "**** REPORT RESUME: " + r);
         
@@ -3233,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) {
@@ -3576,12 +3651,14 @@
             }
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Binding proc "
                     + processName + " with config " + mConfiguration);
-            thread.bindApplication(processName, app.instrumentationInfo != null
-                    ? app.instrumentationInfo : app.info, providers,
+            ApplicationInfo appInfo = app.instrumentationInfo != null
+                    ? app.instrumentationInfo : app.info;
+            thread.bindApplication(processName, appInfo, providers,
                     app.instrumentationClass, app.instrumentationProfileFile,
                     app.instrumentationArguments, app.instrumentationWatcher, testMode, 
                     isRestrictedBackupMode || !normalMode,
-                    mConfiguration, getCommonServicesLocked(),
+                    mConfiguration, compatibilityInfoForPackageLocked(appInfo),
+                    getCommonServicesLocked(),
                     mCoreSettingsObserver.getCoreSettingsLocked());
             updateLruProcessLocked(app, false, true);
             app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
@@ -3672,7 +3749,9 @@
             if (DEBUG_BACKUP) Slog.v(TAG, "New app is backup target, launching agent for " + app);
             ensurePackageDexOpt(mBackupTarget.appInfo.packageName);
             try {
-                thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode);
+                thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,
+                        compatibilityInfoForPackageLocked(mBackupTarget.appInfo),
+                        mBackupTarget.backupMode);
             } catch (Exception e) {
                 Slog.w(TAG, "Exception scheduling backup agent creation: ");
                 e.printStackTrace();
@@ -7866,6 +7945,10 @@
         pw.println("  mConfiguration: " + mConfiguration);
         if (dumpAll) {
             pw.println("  mConfigWillChange: " + mMainStack.mConfigWillChange);
+            if (mScreenCompatPackages.size() > 0) {
+                pw.print("  mScreenCompatPackages=");
+                pw.println(mScreenCompatPackages);
+            }
         }
         pw.println("  mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown);
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
@@ -9560,7 +9643,8 @@
                 r.stats.startLaunchedLocked();
             }
             ensurePackageDexOpt(r.serviceInfo.packageName);
-            app.thread.scheduleCreateService(r, r.serviceInfo);
+            app.thread.scheduleCreateService(r, r.serviceInfo,
+                    compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));
             r.postNotification();
             created = true;
         } finally {
@@ -10649,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);
@@ -10670,7 +10756,8 @@
             if (proc.thread != null) {
                 if (DEBUG_BACKUP) Slog.v(TAG, "Agent proc already running: " + proc);
                 try {
-                    proc.thread.scheduleCreateBackupAgent(app, backupMode);
+                    proc.thread.scheduleCreateBackupAgent(app,
+                            compatibilityInfoForPackageLocked(app), backupMode);
                 } catch (RemoteException e) {
                     // Will time out on the backup manager side
                 }
@@ -10742,7 +10829,8 @@
             // If the app crashed during backup, 'thread' will be null here
             if (proc.thread != null) {
                 try {
-                    proc.thread.scheduleDestroyBackupAgent(appInfo);
+                    proc.thread.scheduleDestroyBackupAgent(appInfo,
+                            compatibilityInfoForPackageLocked(appInfo));
                 } catch (Exception e) {
                     Slog.e(TAG, "Exception when unbinding backup agent:");
                     e.printStackTrace();
@@ -11589,6 +11677,7 @@
                     + ": " + r);
             ensurePackageDexOpt(r.intent.getComponent().getPackageName());
             app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
+                    compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
                     r.resultCode, r.resultData, r.resultExtras, r.ordered);
             if (DEBUG_BROADCAST)  Slog.v(TAG,
                     "Process cur broadcast " + r + " DELIVERED for app " + app);
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 2703481..cc9e78e 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -109,6 +109,7 @@
     boolean hasBeenLaunched;// has this activity ever been launched?
     boolean frozenBeforeDestroy;// has been frozen but not yet destroyed.
     boolean immersive;      // immersive mode (don't interrupt if possible)
+    boolean forceNewConfig; // force re-create with new config next time
 
     String stringName;      // for caching of toString().
     
@@ -174,7 +175,8 @@
                 pw.print(" immersive="); pw.print(immersive);
                 pw.print(" launchMode="); pw.println(launchMode);
         pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy);
-                pw.print(" thumbnailNeeded="); pw.println(thumbnailNeeded);
+                pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded);
+                pw.print(" forceNewConfig="); pw.println(forceNewConfig);
         pw.print(prefix); pw.print("thumbHolder="); pw.println(thumbHolder);
         if (launchTime != 0 || startTime != 0) {
             pw.print(prefix); pw.print("launchTime=");
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 9558895..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;
@@ -540,9 +540,11 @@
             }
             mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
             r.sleeping = false;
+            r.forceNewConfig = false;
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
                     System.identityHashCode(r),
-                    r.info, r.icicle, results, newIntents, !andResume,
+                    r.info, mService.compatibilityInfoForPackageLocked(r.info.applicationInfo),
+                    r.icicle, results, newIntents, !andResume,
                     mService.isNextTransitionForward());
             
             if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
@@ -3856,7 +3858,7 @@
         // Short circuit: if the two configurations are the exact same
         // object (the common case), then there is nothing to do.
         Configuration newConfig = mService.mConfiguration;
-        if (r.configuration == newConfig) {
+        if (r.configuration == newConfig && !r.forceNewConfig) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                     "Configuration unchanged in " + r);
             return true;
@@ -3881,6 +3883,7 @@
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                     "Configuration doesn't matter not running " + r);
             r.stopFreezingScreenLocked(false);
+            r.forceNewConfig = false;
             return true;
         }
         
@@ -3892,10 +3895,11 @@
                     + Integer.toHexString(r.info.configChanges)
                     + ", newConfig=" + newConfig);
         }
-        if ((changes&(~r.info.configChanges)) != 0) {
+        if ((changes&(~r.info.configChanges)) != 0 || r.forceNewConfig) {
             // Aha, the activity isn't handling the change, so DIE DIE DIE.
             r.configChangeFlags |= changes;
             r.startFreezingScreenLocked(r.app, globalChanges);
+            r.forceNewConfig = false;
             if (r.app == null || r.app.thread == null) {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                         "Switch is destroying non-running " + r);
@@ -3966,6 +3970,7 @@
         
         try {
             if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r);
+            r.forceNewConfig = false;
             r.app.thread.scheduleRelaunchActivity(r, results, newIntents,
                     changes, !andResume, mService.mConfiguration);
             // Note: don't need to call pauseIfSleepingLocked() here, because
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index e39c239..5be35ee 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -24,6 +24,7 @@
 import android.app.IInstrumentationWatcher;
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
+import android.content.res.CompatibilityInfo;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.SystemClock;
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/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 6e1093f..de439ca 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -2855,8 +2855,8 @@
     }
 
     private File getDataPathForPackage(String packageName, int userId) {
-        return new File(mUserAppDataDir.getAbsolutePath() + File.separator
-                    + userId + File.separator + packageName);
+        return new File(mUserAppDataDir.getAbsolutePath() + File.separator + userId
+                + File.separator + packageName);
     }
 
     private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
@@ -3319,36 +3319,42 @@
              *        only for non-system apps and system app upgrades.
              */
             if (pkg.applicationInfo.nativeLibraryDir != null) {
-                final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
-                final String dataPathString = dataPath.getPath();
+                try {
+                    final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
+                    final String dataPathString = dataPath.getCanonicalFile().getPath();
 
-                if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
-                    /*
-                     * Upgrading from a previous version of the OS sometimes
-                     * leaves native libraries in the /data/data/<app>/lib
-                     * directory for system apps even when they shouldn't be.
-                     * Recent changes in the JNI library search path
-                     * necessitates we remove those to match previous behavior.
-                     */
-                    if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) {
-                        Log.i(TAG, "removed obsolete native libraries for system package " + path);
+                    if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
+                        /*
+                         * Upgrading from a previous version of the OS sometimes
+                         * leaves native libraries in the /data/data/<app>/lib
+                         * directory for system apps even when they shouldn't be.
+                         * Recent changes in the JNI library search path
+                         * necessitates we remove those to match previous behavior.
+                         */
+                        if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) {
+                            Log.i(TAG, "removed obsolete native libraries for system package "
+                                    + path);
+                        }
+                    } else if (nativeLibraryDir.getCanonicalFile().getParent()
+                            .equals(dataPathString)) {
+                        /*
+                         * If this is an internal application or our
+                         * nativeLibraryPath points to our data directory, unpack
+                         * the libraries. The native library path pointing to the
+                         * data directory for an application in an ASEC container
+                         * can happen for older apps that existed before an OTA to
+                         * Gingerbread.
+                         */
+                        Slog.i(TAG, "Unpacking native libraries for " + path);
+                        mInstaller.unlinkNativeLibraryDirectory(dataPathString);
+                        NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir);
+                    } else {
+                        Slog.i(TAG, "Linking native library dir for " + path);
+                        mInstaller.linkNativeLibraryDirectory(dataPathString,
+                                pkg.applicationInfo.nativeLibraryDir);
                     }
-                } else if (nativeLibraryDir.getParent().equals(dataPathString)) {
-                    /*
-                     * If this is an internal application or our
-                     * nativeLibraryPath points to our data directory, unpack
-                     * the libraries. The native library path pointing to the
-                     * data directory for an application in an ASEC container
-                     * can happen for older apps that existed before an OTA to
-                     * Gingerbread.
-                     */
-                    Slog.i(TAG, "Unpacking native libraries for " + path);
-                    mInstaller.unlinkNativeLibraryDirectory(dataPathString);
-                    NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir);
-                } else {
-                    Slog.i(TAG, "Linking native library dir for " + path);
-                    mInstaller.linkNativeLibraryDirectory(dataPathString,
-                            pkg.applicationInfo.nativeLibraryDir);
+                } catch (IOException ioe) {
+                    Log.e(TAG, "Unable to get canonical file " + ioe.toString());
                 }
             }
             pkg.mScanPath = path;
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index 4f157fe..a3e8be0 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -205,12 +205,21 @@
             inputWindow.ownerPid = child.mSession.mPid;
             inputWindow.ownerUid = child.mSession.mUid;
             
-            final Rect frame = child.mFrame;
+            final Rect frame = child.mScaledFrame;
             inputWindow.frameLeft = frame.left;
             inputWindow.frameTop = frame.top;
             inputWindow.frameRight = frame.right;
             inputWindow.frameBottom = frame.bottom;
 
+            if (child.mGlobalScale != 1) {
+                // If we are scaling the window, input coordinates need
+                // to be inversely scaled to map from what is on screen
+                // to what is actually being touched in the UI.
+                inputWindow.scaleFactor = 1.0f/child.mGlobalScale;
+            } else {
+                inputWindow.scaleFactor = 1;
+            }
+
             child.getTouchableRegion(inputWindow.touchableRegion);
         }
 
diff --git a/services/java/com/android/server/wm/InputWindow.java b/services/java/com/android/server/wm/InputWindow.java
index e3eb473..578120e 100644
--- a/services/java/com/android/server/wm/InputWindow.java
+++ b/services/java/com/android/server/wm/InputWindow.java
@@ -46,6 +46,10 @@
     public int frameRight;
     public int frameBottom;
 
+    // Global scaling factor applied to touch events when they are dispatched
+    // to the window
+    public float scaleFactor;
+
     // Window touchable region.
     public final Region touchableRegion = new Region();
 
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 8b739a4..12a5000 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -19,7 +19,6 @@
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
-import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -589,6 +588,7 @@
 
     // The frame use to limit the size of the app running in compatibility mode.
     Rect mCompatibleScreenFrame = new Rect();
+    float mCompatibleScreenScale;
     // The surface used to fill the outer rim of the app running in compatibility mode.
     Surface mBackgroundFillerSurface = null;
     WindowState mBackgroundFillerTarget = null;
@@ -1760,7 +1760,7 @@
         boolean rawChanged = false;
         float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : 0.5f;
         float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
-        int availw = wallpaperWin.mFrame.right-wallpaperWin.mFrame.left-dw;
+        int availw = wallpaperWin.mScaledFrame.right-wallpaperWin.mScaledFrame.left-dw;
         int offset = availw > 0 ? -(int)(availw*wpx+.5f) : 0;
         changed = wallpaperWin.mXOffset != offset;
         if (changed) {
@@ -2889,14 +2889,14 @@
     }
 
     private boolean applyAnimationLocked(AppWindowToken wtoken,
-            WindowManager.LayoutParams lp, int transit, boolean enter) {
+            WindowManager.LayoutParams lp, int transit, boolean enter, boolean bgFiller) {
         // Only apply an animation if the display isn't frozen.  If it is
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
         // is running.
         if (!mDisplayFrozen && mPolicy.isScreenOn()) {
             Animation a;
-            if (lp != null && (lp.flags & FLAG_COMPATIBLE_WINDOW) != 0) {
+            if (bgFiller) {
                 a = new FadeInOutAnimation(enter);
                 if (DEBUG_ANIM) Slog.v(TAG,
                         "applying FadeInOutAnimation for a window in compatibility mode");
@@ -3682,7 +3682,7 @@
     }
 
     boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp,
-            boolean visible, int transit, boolean performLayout) {
+            boolean visible, int transit, boolean performLayout, boolean bgFiller) {
         boolean delayed = false;
 
         if (wtoken.clientHidden == visible) {
@@ -3704,7 +3704,7 @@
                 if (wtoken.animation == sDummyAnimation) {
                     wtoken.animation = null;
                 }
-                applyAnimationLocked(wtoken, lp, transit, visible);
+                applyAnimationLocked(wtoken, lp, transit, visible, bgFiller);
                 changed = true;
                 if (wtoken.animation != null) {
                     delayed = runningAppAnimation = true;
@@ -3857,7 +3857,8 @@
             }
 
             final long origId = Binder.clearCallingIdentity();
-            setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_UNSET, true);
+            setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_UNSET,
+                    true, false);
             wtoken.updateReportedVisibilityLocked();
             Binder.restoreCallingIdentity(origId);
         }
@@ -3983,7 +3984,8 @@
             WindowToken basewtoken = mTokenMap.remove(token);
             if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) {
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Removing app token: " + wtoken);
-                delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_UNSET, true);
+                delayed = setTokenVisibilityLocked(wtoken, null, false,
+                        WindowManagerPolicy.TRANSIT_UNSET, true, false);
                 wtoken.inPendingTransaction = false;
                 mOpeningApps.remove(wtoken);
                 wtoken.waitingToShow = false;
@@ -4759,8 +4761,8 @@
         synchronized(mWindowMap) {
             long ident = Binder.clearCallingIdentity();
 
-            dw = mCurDisplayWidth;
-            dh = mCurDisplayHeight;
+            dw = mPolicy.getNonDecorDisplayWidth(mCurDisplayWidth);
+            dh = mPolicy.getNonDecorDisplayHeight(mCurDisplayHeight);
 
             int aboveAppLayer = mPolicy.windowTypeToLayerLw(
                     WindowManager.LayoutParams.TYPE_APPLICATION) * TYPE_LAYER_MULTIPLIER
@@ -4808,7 +4810,7 @@
                 
                 // Don't include wallpaper in bounds calculation
                 if (!ws.mIsWallpaper) {
-                    final Rect wf = ws.mFrame;
+                    final Rect wf = ws.mScaledFrame;
                     final Rect cr = ws.mContentInsets;
                     int left = wf.left + cr.left;
                     int top = wf.top + cr.top;
@@ -5526,10 +5528,11 @@
 
         // Override display width and height with what we are computing,
         // to be sure they remain consistent.
-        dm.widthPixels = dw;
-        dm.heightPixels = dh;
+        dm.widthPixels = mPolicy.getNonDecorDisplayWidth(dw);
+        dm.heightPixels = mPolicy.getNonDecorDisplayHeight(dh);
 
-        CompatibilityInfo.updateCompatibleScreenFrame(dm, orientation, mCompatibleScreenFrame);
+        mCompatibleScreenScale = CompatibilityInfo.updateCompatibleScreenFrame(
+                dm, mCompatibleScreenFrame, null);
 
         config.screenWidthDp = (int)(dm.widthPixels / dm.density);
         config.screenHeightDp = (int)(dm.heightPixels / dm.density);
@@ -6680,6 +6683,9 @@
         final int dw = mCurDisplayWidth;
         final int dh = mCurDisplayHeight;
 
+        final int innerDw = mPolicy.getNonDecorDisplayWidth(dw);
+        final int innerDh = mPolicy.getNonDecorDisplayHeight(dh);
+
         final int N = mWindows.size();
         int i;
 
@@ -6732,7 +6738,9 @@
                         //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
                         win.mContentChanged = false;
                     }
+                    win.prelayout();
                     mPolicy.layoutWindowLw(win, win.mAttrs, null);
+                    win.evalNeedsBackgroundFiller(innerDw, innerDh);
                     win.mLayoutSeq = seq;
                     if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame="
                             + win.mFrame + " mContainingFrame="
@@ -6767,7 +6775,9 @@
                         //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
                         win.mContentChanged = false;
                     }
+                    win.prelayout();
                     mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow);
+                    win.evalNeedsBackgroundFiller(innerDw, innerDh);
                     win.mLayoutSeq = seq;
                     if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame="
                             + win.mFrame + " mContainingFrame="
@@ -6798,6 +6808,9 @@
         final int dw = mCurDisplayWidth;
         final int dh = mCurDisplayHeight;
 
+        final int innerDw = mPolicy.getNonDecorDisplayWidth(dw);
+        final int innerDh = mPolicy.getNonDecorDisplayHeight(dh);
+
         int i;
 
         if (mFocusMayChange) {
@@ -6897,13 +6910,15 @@
                 boolean tokensAnimating = false;
                 final int NAT = mAppTokens.size();
                 for (i=0; i<NAT; i++) {
-                    if (mAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) {
+                    if (mAppTokens.get(i).stepAnimationLocked(currentTime,
+                            innerDw, innerDh)) {
                         tokensAnimating = true;
                     }
                 }
                 final int NEAT = mExitingAppTokens.size();
                 for (i=0; i<NEAT; i++) {
-                    if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) {
+                    if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime,
+                            innerDw, innerDh)) {
                         tokensAnimating = true;
                     }
                 }
@@ -6956,8 +6971,8 @@
 
                         final boolean wasAnimating = w.mAnimating;
 
-                        int animDw = dw;
-                        int animDh = dh;
+                        int animDw = innerDw;
+                        int animDh = innerDh;
 
                         // If the window has moved due to its containing
                         // content frame changing, then we'd like to animate
@@ -7214,6 +7229,7 @@
                         LayoutParams animLp = null;
                         int bestAnimLayer = -1;
                         boolean fullscreenAnim = false;
+                        boolean needBgFiller = false;
 
                         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                                 "New wallpaper target=" + mWallpaperTarget
@@ -7253,9 +7269,10 @@
                                 if (ws != null) {
                                     // If this is a compatibility mode
                                     // window, we will always use its anim.
-                                    if ((ws.mAttrs.flags&FLAG_COMPATIBLE_WINDOW) != 0) {
+                                    if (ws.mNeedsBackgroundFiller) {
                                         animLp = ws.mAttrs;
                                         bestAnimLayer = Integer.MAX_VALUE;
+                                        needBgFiller = true;
                                     } else if (!fullscreenAnim || ws.mLayer > bestAnimLayer) {
                                         animLp = ws.mAttrs;
                                         bestAnimLayer = ws.mLayer;
@@ -7320,7 +7337,8 @@
                             wtoken.reportedVisible = false;
                             wtoken.inPendingTransaction = false;
                             wtoken.animation = null;
-                            setTokenVisibilityLocked(wtoken, animLp, true, transit, false);
+                            setTokenVisibilityLocked(wtoken, animLp, true,
+                                    transit, false, needBgFiller);
                             wtoken.updateReportedVisibilityLocked();
                             wtoken.waitingToShow = false;
                             wtoken.showAllWindowsLocked();
@@ -7332,7 +7350,8 @@
                                     "Now closing app" + wtoken);
                             wtoken.inPendingTransaction = false;
                             wtoken.animation = null;
-                            setTokenVisibilityLocked(wtoken, animLp, false, transit, false);
+                            setTokenVisibilityLocked(wtoken, animLp, false,
+                                    transit, false, needBgFiller);
                             wtoken.updateReportedVisibilityLocked();
                             wtoken.waitingToHide = false;
                             // Force the allDrawn flag, because we want to start
@@ -7835,12 +7854,14 @@
                     }
 
                     boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn();
-                    if (opaqueDrawn && w.isFullscreen(dw, dh)) {
+                    if (opaqueDrawn && w.isFullscreen(innerDw, innerDh)) {
                         // This window completely covers everything behind it,
                         // so we want to leave all of them as unblurred (for
                         // performance reasons).
                         obscured = true;
-                    } else if (w.needsBackgroundFiller(dw, dh) && (canBeSeen || w.isAnimating())) {
+                    } else if (w.mNeedsBackgroundFiller && w.mHasDrawn
+                            && w.mViewVisibility == View.VISIBLE
+                            && (canBeSeen || w.isAnimating())) {
                         // This window is in compatibility mode, and needs background filler.
                         obscured = true;
                         mBackgroundFillerTarget = w;
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index c05186a..2014e9d 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -72,6 +72,7 @@
     final boolean mIsImWindow;
     final boolean mIsWallpaper;
     final boolean mIsFloatingLayer;
+    final boolean mEnforceSizeCompat;
     int mViewVisibility;
     boolean mPolicyVisibility = true;
     boolean mPolicyVisibilityAfterAnim = true;
@@ -91,6 +92,7 @@
     int mLastLayer;
     boolean mHaveFrame;
     boolean mObscured;
+    boolean mNeedsBackgroundFiller;
     boolean mTurnOnScreen;
 
     int mLayoutSeq = -1;
@@ -154,6 +156,7 @@
 
     // Current transformation being applied.
     boolean mHaveMatrix;
+    float mGlobalScale=1;
     float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
     float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
     float mHScale=1, mVScale=1;
@@ -163,6 +166,7 @@
     // "Real" frame that the application sees.
     final Rect mFrame = new Rect();
     final Rect mLastFrame = new Rect();
+    final Rect mScaledFrame = new Rect();
 
     final Rect mContainingFrame = new Rect();
     final Rect mDisplayFrame = new Rect();
@@ -273,6 +277,7 @@
         mViewVisibility = viewVisibility;
         DeathRecipient deathRecipient = new DeathRecipient();
         mAlpha = a.alpha;
+        mEnforceSizeCompat = (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0;
         if (WindowManagerService.localLOGV) Slog.v(
             WindowManagerService.TAG, "Window " + this + " client=" + c.asBinder()
             + " token=" + token + " (" + mAttrs.token + ")");
@@ -368,7 +373,7 @@
         final Rect display = mDisplayFrame;
         display.set(df);
 
-        if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) {
+        if (mEnforceSizeCompat) {
             container.intersect(mService.mCompatibleScreenFrame);
             if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) {
                 display.intersect(mService.mCompatibleScreenFrame);
@@ -416,6 +421,28 @@
         // Now make sure the window fits in the overall display.
         Gravity.applyDisplay(mAttrs.gravity, df, frame);
 
+        int adjRight=0, adjBottom=0;
+
+        if (mEnforceSizeCompat) {
+            // Adjust window offsets by the scaling factor.
+            int xoff = (int)((frame.left-mService.mCompatibleScreenFrame.left)*mGlobalScale)
+                    - (frame.left-mService.mCompatibleScreenFrame.left);
+            int yoff = (int)((frame.top-mService.mCompatibleScreenFrame.top)*mGlobalScale)
+                    - (frame.top-mService.mCompatibleScreenFrame.top);
+            frame.offset(xoff, yoff);
+
+            // We are temporarily going to apply the compatibility scale
+            // to the window so that we can correctly associate it with the
+            // content and visible frame.
+            adjRight = frame.right - frame.left;
+            adjRight = (int)((adjRight)*mGlobalScale + .5f) - adjRight;
+            adjBottom = frame.bottom - frame.top;
+            adjBottom = (int)((adjBottom)*mGlobalScale + .5f) - adjBottom;
+            frame.right += adjRight;
+            frame.bottom += adjBottom;
+        }
+        mScaledFrame.set(frame);
+
         // Make sure the content and visible frames are inside of the
         // final window frame.
         if (content.left < frame.left) content.left = frame.left;
@@ -439,6 +466,22 @@
         visibleInsets.right = frame.right-visible.right;
         visibleInsets.bottom = frame.bottom-visible.bottom;
 
+        if (mEnforceSizeCompat) {
+            // Scale the computed insets back to the window's compatibility
+            // coordinate space, and put frame back to correct size.
+            final float invScale = 1.0f/mGlobalScale;
+            contentInsets.left = (int)(contentInsets.left*invScale);
+            contentInsets.top = (int)(contentInsets.top*invScale);
+            contentInsets.right = (int)(contentInsets.right*invScale);
+            contentInsets.bottom = (int)(contentInsets.bottom*invScale);
+            visibleInsets.left = (int)(visibleInsets.left*invScale);
+            visibleInsets.top = (int)(visibleInsets.top*invScale);
+            visibleInsets.right = (int)(visibleInsets.right*invScale);
+            visibleInsets.bottom = (int)(visibleInsets.bottom*invScale);
+            frame.right -= adjRight;
+            frame.bottom -= adjBottom;
+        }
+
         if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) {
             mService.updateWallpaperOffsetLocked(this, mService.mDisplay.getRealWidth(),
                     mService.mDisplay.getRealHeight(), false);
@@ -819,9 +862,10 @@
                 if (!mLocalAnimating) {
                     if (WindowManagerService.DEBUG_ANIM) Slog.v(
                         WindowManagerService.TAG, "Starting animation in " + this +
-                        " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() +
+                        " @ " + currentTime + ": ww=" + mScaledFrame.width() +
+                        " wh=" + mScaledFrame.height() +
                         " dw=" + dw + " dh=" + dh + " scale=" + mService.mWindowAnimationScale);
-                    mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh);
+                    mAnimation.initialize(mScaledFrame.width(), mScaledFrame.height(), dw, dh);
                     mAnimation.setStartTime(currentTime);
                     mLocalAnimating = true;
                     mAnimating = true;
@@ -988,6 +1032,14 @@
         return true;
     }
 
+    void prelayout() {
+        if (mEnforceSizeCompat) {
+            mGlobalScale = mService.mCompatibleScreenScale;
+        } else {
+            mGlobalScale = 1;
+        }
+    }
+
     void computeShownFrameLocked() {
         final boolean selfTransformation = mHasLocalTransformation;
         Transformation attachedTransformation =
@@ -1031,6 +1083,7 @@
 
             // Compute the desired transformation.
             tmpMatrix.setTranslate(0, 0);
+            tmpMatrix.postScale(mGlobalScale, mGlobalScale);
             if (selfTransformation) {
                 tmpMatrix.postConcat(mTransformation.getMatrix());
             }
@@ -1105,10 +1158,10 @@
         }
         mShownAlpha = mAlpha;
         mHaveMatrix = false;
-        mDsDx = 1;
+        mDsDx = mGlobalScale;
         mDtDx = 0;
         mDsDy = 0;
-        mDtDy = 1;
+        mDtDy = mGlobalScale;
     }
 
     /**
@@ -1281,12 +1334,14 @@
                 && mService.mPolicy.isScreenOn();
     }
 
-    boolean needsBackgroundFiller(int screenWidth, int screenHeight) {
-        return
+    void evalNeedsBackgroundFiller(int screenWidth, int screenHeight) {
+        mNeedsBackgroundFiller =
              // only if the application is requesting compatible window
-             (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0 &&
+             mEnforceSizeCompat &&
              // only if it's visible
              mHasDrawn && mViewVisibility == View.VISIBLE &&
+             // not needed if the compat window is actually full screen
+             !isFullscreenIgnoringCompat(screenWidth, screenHeight) &&
              // and only if the application fills the compatible screen
              mFrame.left <= mService.mCompatibleScreenFrame.left &&
              mFrame.top <= mService.mCompatibleScreenFrame.top &&
@@ -1295,8 +1350,19 @@
     }
 
     boolean isFullscreen(int screenWidth, int screenHeight) {
-        return mFrame.left <= 0 && mFrame.top <= 0 &&
-                mFrame.right >= screenWidth && mFrame.bottom >= screenHeight;
+        if (mEnforceSizeCompat) {
+            return mFrame.left <= mService.mCompatibleScreenFrame.left &&
+                    mFrame.top <= mService.mCompatibleScreenFrame.top &&
+                    mFrame.right >= mService.mCompatibleScreenFrame.right &&
+                    mFrame.bottom >= mService.mCompatibleScreenFrame.bottom;
+        } else {
+            return isFullscreenIgnoringCompat(screenWidth, screenHeight);
+        }
+    }
+
+    boolean isFullscreenIgnoringCompat(int screenWidth, int screenHeight) {
+        return mScaledFrame.left <= 0 && mScaledFrame.top <= 0 &&
+                mScaledFrame.right >= screenWidth && mScaledFrame.bottom >= screenHeight;
     }
 
     void removeLocked() {
@@ -1426,30 +1492,38 @@
         return true;
     }
 
+    private static void applyScaledInsets(Region outRegion, Rect frame, Rect inset, float scale) {
+        if (scale != 1) {
+            outRegion.set(frame.left + (int)(inset.left*scale),
+                    frame.top + (int)(inset.top*scale),
+                    frame.right - (int)(inset.right*scale),
+                    frame.bottom - (int)(inset.bottom*scale));
+        } else {
+            outRegion.set(
+                    frame.left + inset.left, frame.top + inset.top,
+                    frame.right - inset.right, frame.bottom - inset.bottom);
+        }
+    }
+
     public void getTouchableRegion(Region outRegion) {
-        final Rect frame = mFrame;
+        final Rect frame = mScaledFrame;
         switch (mTouchableInsets) {
             default:
             case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
                 outRegion.set(frame);
                 break;
-            case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: {
-                final Rect inset = mGivenContentInsets;
-                outRegion.set(
-                        frame.left + inset.left, frame.top + inset.top,
-                        frame.right - inset.right, frame.bottom - inset.bottom);
+            case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT:
+                applyScaledInsets(outRegion, frame, mGivenContentInsets, mGlobalScale);
                 break;
-            }
-            case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: {
-                final Rect inset = mGivenVisibleInsets;
-                outRegion.set(
-                        frame.left + inset.left, frame.top + inset.top,
-                        frame.right - inset.right, frame.bottom - inset.bottom);
+            case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE:
+                applyScaledInsets(outRegion, frame, mGivenVisibleInsets, mGlobalScale);
                 break;
-            }
             case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: {
                 final Region givenTouchableRegion = mGivenTouchableRegion;
                 outRegion.set(givenTouchableRegion);
+                if (mGlobalScale != 1) {
+                    outRegion.scale(mGlobalScale);
+                }
                 outRegion.translate(frame.left, frame.top);
                 break;
             }
@@ -1512,7 +1586,8 @@
         }
         pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth);
                 pw.print(" h="); pw.print(mRequestedHeight);
-                pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
+                pw.print(" mLayoutSeq="); pw.print(mLayoutSeq);
+                pw.print(" mNeedsBackgroundFiller="); pw.println(mNeedsBackgroundFiller);
         if (mXOffset != 0 || mYOffset != 0) {
             pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset);
                     pw.print(" y="); pw.println(mYOffset);
@@ -1533,6 +1608,7 @@
                 pw.println();
         pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
                 pw.print(" last="); mLastFrame.printShortString(pw);
+                pw.print(" scaled="); mScaledFrame.printShortString(pw);
                 pw.println();
         pw.print(prefix); pw.print("mContainingFrame=");
                 mContainingFrame.printShortString(pw);
@@ -1568,8 +1644,9 @@
                     pw.print(" mAlpha="); pw.print(mAlpha);
                     pw.print(" mLastAlpha="); pw.println(mLastAlpha);
         }
-        if (mHaveMatrix) {
-            pw.print(prefix); pw.print("mDsDx="); pw.print(mDsDx);
+        if (mHaveMatrix || mGlobalScale != 1) {
+            pw.print(prefix); pw.print("mGlobalScale="); pw.print(mGlobalScale);
+                    pw.print(" mDsDx="); pw.print(mDsDx);
                     pw.print(" mDtDx="); pw.print(mDtDx);
                     pw.print(" mDsDy="); pw.print(mDsDy);
                     pw.print(" mDtDy="); pw.println(mDtDy);
diff --git a/services/jni/com_android_server_InputWindow.cpp b/services/jni/com_android_server_InputWindow.cpp
index 99f625c..012ce21 100644
--- a/services/jni/com_android_server_InputWindow.cpp
+++ b/services/jni/com_android_server_InputWindow.cpp
@@ -38,6 +38,7 @@
     jfieldID frameTop;
     jfieldID frameRight;
     jfieldID frameBottom;
+    jfieldID scaleFactor;
     jfieldID touchableRegion;
     jfieldID visible;
     jfieldID canReceiveKeys;
@@ -100,6 +101,8 @@
             gInputWindowClassInfo.frameRight);
     outInputWindow->frameBottom = env->GetIntField(inputWindowObj,
             gInputWindowClassInfo.frameBottom);
+    outInputWindow->scaleFactor = env->GetFloatField(inputWindowObj,
+            gInputWindowClassInfo.scaleFactor);
 
     jobject regionObj = env->GetObjectField(inputWindowObj,
             gInputWindowClassInfo.touchableRegion);
@@ -174,6 +177,9 @@
     GET_FIELD_ID(gInputWindowClassInfo.frameBottom, clazz,
             "frameBottom", "I");
 
+    GET_FIELD_ID(gInputWindowClassInfo.scaleFactor, clazz,
+            "scaleFactor", "F");
+
     GET_FIELD_ID(gInputWindowClassInfo.touchableRegion, clazz,
             "touchableRegion", "Landroid/graphics/Region;");
 
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 1767dd9..a88825b 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -556,10 +556,7 @@
      * @hide
      */
     public int getLteDbm() {
-        log("TODO: teach getLteDbm to compute dBm properly");
-        int level = -1;
-        if (DBG) log("getLteDbm=" + level);
-        return level;
+        return mLteRsrp;
     }
 
     /**
@@ -568,22 +565,33 @@
      * @hide
      */
     public int getLteLevel() {
-        log("TODO: teach getLteLevel to compute Level properly");
-        int level = SIGNAL_STRENGTH_MODERATE;
-        if (DBG) log("getLteLevel=" + level);
-        return level;
+        int levelLteRsrp = 0;
+
+        if (mLteRsrp == -1) levelLteRsrp = 0;
+        else if (mLteRsrp >= -85) levelLteRsrp = SIGNAL_STRENGTH_GREAT;
+        else if (mLteRsrp >= -95) levelLteRsrp = SIGNAL_STRENGTH_GOOD;
+        else if (mLteRsrp >= -105) levelLteRsrp = SIGNAL_STRENGTH_MODERATE;
+        else if (mLteRsrp >= -115) levelLteRsrp = SIGNAL_STRENGTH_POOR;
+        else levelLteRsrp = 0;
+
+        if (DBG) log("Lte level: "+levelLteRsrp);
+        return levelLteRsrp;
     }
 
     /**
-     * Get the LTE signal level as an asu value between 0..31, 99 is unknown
+     * Get the LTE signal level as an asu value between 0..97, 99 is unknown
+     * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
      *
      * @hide
      */
     public int getLteAsuLevel() {
-        log("TODO: teach getLteAsuLevel to compute asu Level properly");
-        int level = 4;
-        if (DBG) log("getLteAsuLevel=" + level);
-        return level;
+        int lteAsuLevel = 99;
+        int lteDbm = getLteDbm();
+        if (lteDbm <= -140) lteAsuLevel = 0;
+        else if (lteDbm >= -43) lteAsuLevel = 97;
+        else lteAsuLevel = lteDbm + 140;
+        if (DBG) log("Lte Asu level: "+lteAsuLevel);
+        return lteAsuLevel;
     }
 
     /**
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/ApnContext.java b/telephony/java/com/android/internal/telephony/ApnContext.java
index 010d61d..ce1a3b6 100644
--- a/telephony/java/com/android/internal/telephony/ApnContext.java
+++ b/telephony/java/com/android/internal/telephony/ApnContext.java
@@ -28,14 +28,8 @@
  */
 public class ApnContext {
 
-    public static final int PENDING_ACTION_NONE = 1;
-    public static final int PENDING_ACTION_RECONNECT = 2;
-    public static final int PENDING_ACTION_APN_DISABLE = 3;
-
     public final String LOG_TAG;
 
-    private AtomicInteger mPendingAction;
-
     protected static final boolean DBG = true;
 
     private final String mApnType;
@@ -71,21 +65,12 @@
         mApnType = apnType;
         mState = DataConnectionTracker.State.IDLE;
         setReason(Phone.REASON_DATA_ENABLED);
-        mPendingAction = new AtomicInteger(PENDING_ACTION_NONE);
         mDataEnabled = new AtomicBoolean(false);
         mDependencyMet = new AtomicBoolean(true);
         mWaitingApnsPermanentFailureCountDown = new AtomicInteger(0);
         LOG_TAG = logTag;
     }
 
-    public int getPendingAction() {
-        return mPendingAction.get();
-    }
-
-    public void setPendingAction(int pa) {
-        mPendingAction.set(pa);
-    }
-
     public String getApnType() {
         return mApnType;
     }
diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java
index ec04fa8..ce67390 100644
--- a/telephony/java/com/android/internal/telephony/BaseCommands.java
+++ b/telephony/java/com/android/internal/telephony/BaseCommands.java
@@ -71,6 +71,7 @@
     protected RegistrantList mCdmaPrlChangedRegistrants = new RegistrantList();
     protected RegistrantList mExitEmergencyCallbackModeRegistrants = new RegistrantList();
     protected RegistrantList mRilConnectedRegistrants = new RegistrantList();
+    protected RegistrantList mIccRefreshRegistrants = new RegistrantList();
 
     protected Registrant mSMSRegistrant;
     protected Registrant mNITZTimeRegistrant;
@@ -85,7 +86,6 @@
     protected Registrant mCatCallSetUpRegistrant;
     protected Registrant mIccSmsFullRegistrant;
     protected Registrant mEmergencyCallbackModeRegistrant;
-    protected Registrant mIccRefreshRegistrant;
     protected Registrant mRingRegistrant;
     protected Registrant mRestrictedStateRegistrant;
     protected Registrant mGsmBroadcastSmsRegistrant;
@@ -454,16 +454,23 @@
         mIccSmsFullRegistrant.clear();
     }
 
+    public void registerForIccRefresh(Handler h, int what, Object obj) {
+        Registrant r = new Registrant (h, what, obj);
+        mIccRefreshRegistrants.add(r);
+    }
     public void setOnIccRefresh(Handler h, int what, Object obj) {
-        mIccRefreshRegistrant = new Registrant (h, what, obj);
+        registerForIccRefresh(h, what, obj);
     }
 
     public void setEmergencyCallbackMode(Handler h, int what, Object obj) {
         mEmergencyCallbackModeRegistrant = new Registrant (h, what, obj);
     }
 
-    public void unSetOnIccRefresh(Handler h) {
-        mIccRefreshRegistrant.clear();
+    public void unregisterForIccRefresh(Handler h) {
+        mIccRefreshRegistrants.remove(h);
+    }
+    public void unsetOnIccRefresh(Handler h) {
+        unregisterForIccRefresh(h);
     }
 
     public void setOnCallRing(Handler h, int what, Object obj) {
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 1d574ca..96788ef 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -352,14 +352,16 @@
 
     /**
      * Sets the handler for SIM Refresh notifications.
-     * Unlike the register* methods, there's only one notification handler
      *
      * @param h Handler for notification message.
      * @param what User-defined message code.
      * @param obj User object.
      */
+    void registerForIccRefresh(Handler h, int what, Object obj);
+    void unregisterForIccRefresh(Handler h);
+
     void setOnIccRefresh(Handler h, int what, Object obj);
-    void unSetOnIccRefresh(Handler h);
+    void unsetOnIccRefresh(Handler h);
 
     /**
      * Sets the handler for RING notifications.
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index c4359a9..0188cf1 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -394,6 +394,20 @@
         return mRetryMgr.isRetryForever();
     }
 
+    /**
+     * @return whether the retry config is set successfully or not
+     */
+    public boolean configureRetry(int maxRetryCount, int retryTime, int randomizationTime) {
+        return mRetryMgr.configure(maxRetryCount, retryTime, randomizationTime);
+    }
+
+    /**
+     * @return whether the retry config is set successfully or not
+     */
+    public boolean configureRetry(String configStr) {
+        return mRetryMgr.configure(configStr);
+    }
+
     private AtomicInteger mRefCount = new AtomicInteger(0);
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 244f35e..43f3b95 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -671,8 +671,10 @@
 
     protected LinkProperties getLinkProperties(String apnType) {
         int id = apnTypeToId(apnType);
+
         if (isApnIdEnabled(id)) {
-            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(id);
+            // TODO - remove this cdma-only hack and support multiple DCs.
+            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(0);
             return dcac.getLinkPropertiesSync();
         } else {
             return new LinkProperties();
@@ -682,7 +684,8 @@
     protected LinkCapabilities getLinkCapabilities(String apnType) {
         int id = apnTypeToId(apnType);
         if (isApnIdEnabled(id)) {
-            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(id);
+            // TODO - remove this cdma-only hack and support multiple DCs.
+            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(0);
             return dcac.getLinkCapabilitiesSync();
         } else {
             return new LinkCapabilities();
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/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 490051d..d1c6b29 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -2601,8 +2601,8 @@
             case RIL_UNSOL_SIM_REFRESH:
                 if (RILJ_LOGD) unsljLogRet(response, ret);
 
-                if (mIccRefreshRegistrant != null) {
-                    mIccRefreshRegistrant.notifyRegistrant(
+                if (mIccRefreshRegistrants != null) {
+                    mIccRefreshRegistrants.notifyRegistrants(
                             new AsyncResult (null, ret, null));
                 }
                 break;
diff --git a/telephony/java/com/android/internal/telephony/RetryManager.java b/telephony/java/com/android/internal/telephony/RetryManager.java
index b1049a2..29bd104 100644
--- a/telephony/java/com/android/internal/telephony/RetryManager.java
+++ b/telephony/java/com/android/internal/telephony/RetryManager.java
@@ -308,12 +308,10 @@
     }
 
     /**
-     * Reset network re-registration indicator and clear the data-retry counter
-     * and turns off retrying forever.
+     * Clear the data-retry counter
      */
     public void resetRetryCount() {
         mRetryCount = 0;
-        mRetryForever = false;
         if (DBG) log("resetRetryCount: " + mRetryCount);
     }
 
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 3429099..ee63ede 100755
--- a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
@@ -91,18 +91,19 @@
         p.mCM.registerForRUIMReady(this, EVENT_RUIM_READY, null);
         p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         // NOTE the EVENT_SMS_ON_RUIM is not registered
-        p.mCM.setOnIccRefresh(this, EVENT_RUIM_REFRESH, null);
+        p.mCM.registerForIccRefresh(this, EVENT_RUIM_REFRESH, null);
 
         // Start off by setting empty state
         onRadioOffOrNotAvailable();
 
     }
 
+    @Override
     public void dispose() {
         //Unregister for all events
         phone.mCM.unregisterForRUIMReady(this);
         phone.mCM.unregisterForOffOrNotAvailable( this);
-        phone.mCM.unSetOnIccRefresh(this);
+        phone.mCM.unregisterForIccRefresh(this);
     }
 
     @Override
@@ -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 d3e659a..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);
@@ -457,20 +457,10 @@
         // If already active, return
         if (DBG) log("enableApnType: " + apnType + " mState(" + apnContext.getState() + ")");
 
-        if (apnContext.getState() == State.INITING) {
-            if (DBG) log("enableApnType: return APN_REQUEST_STARTED");
-            return Phone.APN_REQUEST_STARTED;
-        }
-        else if (apnContext.getState() == State.CONNECTED) {
+        if (apnContext.getState() == State.CONNECTED) {
             if (DBG) log("enableApnType: return APN_ALREADY_ACTIVE");
             return Phone.APN_ALREADY_ACTIVE;
         }
-        else if (apnContext.getState() == State.DISCONNECTING) {
-            if (DBG) log("enableApnType: while disconnecting, return APN_REQUEST_STARTED");
-            apnContext.setPendingAction(ApnContext.PENDING_ACTION_RECONNECT);
-            return Phone.APN_REQUEST_STARTED;
-        }
-
         setEnabled(apnTypeToId(apnType), true);
         if (DBG) {
             log("enableApnType: new apn request for type " + apnType +
@@ -504,21 +494,12 @@
         ApnContext apnContext = mApnContexts.get(type);
 
         if (apnContext != null) {
+            setEnabled(apnTypeToId(type), false);
             if (apnContext.getState() != State.IDLE && apnContext.getState() != State.FAILED) {
-                apnContext.setPendingAction(ApnContext.PENDING_ACTION_APN_DISABLE);
-                Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
-                msg.arg1 = 1; // tearDown is true;
-                // TODO - don't set things on apnContext from public functions.
-                // Maybe pass reason as arg2?
-                apnContext.setReason(Phone.REASON_DATA_DISABLED);
-                msg.obj = apnContext;
-                sendMessage(msg);
                 if (DBG) log("diableApnType: return APN_REQUEST_STARTED");
                 return Phone.APN_REQUEST_STARTED;
             } else {
                 if (DBG) log("disableApnType: return APN_ALREADY_INACTIVE");
-                apnContext.setEnabled(false);
-                apnContext.setDataConnection(null);
                 return Phone.APN_ALREADY_INACTIVE;
             }
 
@@ -565,10 +546,6 @@
     }
 
     private boolean isDataAllowed(ApnContext apnContext) {
-        if(apnContext.getState() == State.DISCONNECTING
-                && apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
-            return false;
-        }
         return apnContext.isReady() && isDataAllowed();
     }
 
@@ -605,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()) &&
@@ -616,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();
             }
@@ -958,7 +935,7 @@
         }
 
         if (dc == null) {
-            dc = createDataConnection(apnContext.getApnType());
+            dc = createDataConnection();
         }
 
         if (dc == null) {
@@ -970,6 +947,11 @@
         dc.setActiveApnType(apnContext.getApnType());
         int refCount = dc.incAndGetRefCount();
         if (DBG) log("setupData: init dc and apnContext refCount=" + refCount);
+
+        // configure retry count if no other Apn is using the same connection.
+        if (refCount == 1) {
+            configureRetry(dc, apnContext.getApnType());
+        }
         DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId());
         apnContext.setDataConnectionAc(mDataConnectionAsyncChannels.get(dc.getDataConnectionId()));
         apnContext.setApnSetting(apn);
@@ -1327,7 +1309,7 @@
         if (apnContext.getState() == State.FAILED) {
             if (!apnContext.getDataConnection().isRetryNeeded()) {
                 if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)) {
-                    notifyDataConnection(Phone.REASON_APN_FAILED);
+                    mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
                     return;
                 }
                 if (mReregisterOnReconnectFailure) {
@@ -1528,7 +1510,7 @@
             log("onRadioAvailable: We're on the simulator; assuming data is connected");
         }
 
-        if (mPhone.mSIMRecords.getRecordsLoaded()) {
+        if (mPhone.mIccRecords.getRecordsLoaded()) {
             notifyDataAvailability(null);
         }
 
@@ -1645,7 +1627,7 @@
                         log("onDataSetupComplete: All APN's had permanent failures, stop retrying");
                     }
                     apnContext.setState(State.FAILED);
-                    notifyDataConnection(Phone.REASON_APN_FAILED);
+                    mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
                 } else {
                     if (DBG) log("onDataSetupComplete: Not all permanent failures, retry");
                     startDelayedRetry(cause, apnContext);
@@ -1684,10 +1666,6 @@
         apnContext.setState(State.IDLE);
         apnContext.setApnSetting(null);
 
-        // Check if APN disabled.
-        if (apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
-           apnContext.setPendingAction(ApnContext.PENDING_ACTION_NONE);
-        }
         mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
 
         // if all data connection are gone, check whether Airplane mode request was
@@ -1701,10 +1679,7 @@
 
         // If APN is still enabled, try to bring it back up automatically
         if (apnContext.isReady() && retryAfterDisconnected(apnContext.getReason())) {
-            SystemProperties.set("gsm.defaultpdpcontext.active", "false");
-            if (apnContext.getPendingAction() == ApnContext.PENDING_ACTION_RECONNECT) {
-                apnContext.setPendingAction(ApnContext.PENDING_ACTION_NONE);
-            }
+            SystemProperties.set("gsm.defaultpdpcontext.active", "false");  // TODO - what the heck?  This shoudld go
             // Wait a bit before trying the next APN, so that
             // we're not tying up the RIL command channel.
             // This also helps in any external dependency to turn off the context.
@@ -1782,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);
@@ -1815,33 +1790,12 @@
     }
 
     /** Return the id for a new data connection */
-    private GsmDataConnection createDataConnection(String apnType) {
-        if (DBG) log("createDataConnection(" + apnType + ") E");
+    private GsmDataConnection createDataConnection() {
+        if (DBG) log("createDataConnection E");
+
         RetryManager rm = new RetryManager();
-
-        if (apnType.equals(Phone.APN_TYPE_DEFAULT)) {
-            if (!rm.configure(SystemProperties.get("ro.gsm.data_retry_config"))) {
-                if (!rm.configure(DEFAULT_DATA_RETRY_CONFIG)) {
-                    // Should never happen, log an error and default to a simple linear sequence.
-                    loge("createDataConnection: Could not configure using " +
-                            "DEFAULT_DATA_RETRY_CONFIG=" + DEFAULT_DATA_RETRY_CONFIG);
-                    rm.configure(20, 2000, 1000);
-                }
-            }
-        } else {
-            if (!rm.configure(SystemProperties.get("ro.gsm.2nd_data_retry_config"))) {
-                if (!rm.configure(SECONDARY_DATA_RETRY_CONFIG)) {
-                    // Should never happen, log an error and default to a simple sequence.
-                    loge("createDataConnection: Could note configure using " +
-                            "SECONDARY_DATA_RETRY_CONFIG=" + SECONDARY_DATA_RETRY_CONFIG);
-                    rm.configure("max_retries=3, 333, 333, 333");
-                }
-            }
-        }
-
         int id = mUniqueIdGenerator.getAndIncrement();
         GsmDataConnection conn = GsmDataConnection.makeDataConnection(mPhone, id, rm);
-        conn.resetRetryCount();
         mDataConnections.put(id, conn);
         DataConnectionAc dcac = new DataConnectionAc(conn, LOG_TAG);
         int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
@@ -1852,10 +1806,34 @@
                     " status=" + status);
         }
 
-        if (DBG) log("createDataConnection(" + apnType + ") X id=" + id);
+        if (DBG) log("createDataConnection() X id=" + id);
         return conn;
     }
 
+    private void configureRetry(DataConnection dc, String apnType) {
+        if ((dc == null) || (apnType == null)) return;
+
+        if (apnType.equals(Phone.APN_TYPE_DEFAULT)) {
+            if (!dc.configureRetry(SystemProperties.get("ro.gsm.data_retry_config"))) {
+                if (!dc.configureRetry(DEFAULT_DATA_RETRY_CONFIG)) {
+                    // Should never happen, log an error and default to a simple linear sequence.
+                    loge("createDataConnection: Could not configure using " +
+                            "DEFAULT_DATA_RETRY_CONFIG=" + DEFAULT_DATA_RETRY_CONFIG);
+                    dc.configureRetry(20, 2000, 1000);
+                }
+            }
+        } else {
+            if (!dc.configureRetry(SystemProperties.get("ro.gsm.2nd_data_retry_config"))) {
+                if (!dc.configureRetry(SECONDARY_DATA_RETRY_CONFIG)) {
+                    // Should never happen, log an error and default to a simple sequence.
+                    loge("createDataConnection: Could note configure using " +
+                            "SECONDARY_DATA_RETRY_CONFIG=" + SECONDARY_DATA_RETRY_CONFIG);
+                    dc.configureRetry("max_retries=3, 333, 333, 333");
+                }
+            }
+        }
+    }
+
     private void destroyDataConnections() {
         if(mDataConnections != null) {
             if (DBG) log("destroyDataConnections: clear mDataConnectionList");
@@ -1882,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 8b032ff..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;
 
 
     /**
@@ -184,18 +184,19 @@
         p.mCM.registerForOffOrNotAvailable(
                         this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         p.mCM.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null);
-        p.mCM.setOnIccRefresh(this, EVENT_SIM_REFRESH, null);
+        p.mCM.registerForIccRefresh(this, EVENT_SIM_REFRESH, null);
 
         // Start off by setting empty state
         onRadioOffOrNotAvailable();
 
     }
 
+    @Override
     public void dispose() {
         //Unregister for all events
         phone.mCM.unregisterForSIMReady(this);
         phone.mCM.unregisterForOffOrNotAvailable( this);
-        phone.mCM.unSetOnIccRefresh(this);
+        phone.mCM.unregisterForIccRefresh(this);
     }
 
     protected void finalize() {
@@ -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/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
index 6192a3c..2a40c57 100644
--- a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
+++ b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
@@ -62,7 +62,8 @@
                     | ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
                     | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS
                     | ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
-                app.getResources().setCompatibilityInfo(new CompatibilityInfo(ai));
+                app.getResources().setCompatibilityInfo(new CompatibilityInfo(ai,
+                        getResources().getConfiguration().screenLayout, false));
             }
         } catch (PackageManager.NameNotFoundException e) {
             throw new RuntimeException("ouch", e);
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 8d3fd1d..e3ff1b7 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -125,6 +125,7 @@
     }
 
     private void setDumpTimeout(long timeout) {
+        Log.v(LOGTAG, "setting dump timeout at " + timeout);
         Message msg = mHandler.obtainMessage(MSG_DUMP_TIMEOUT);
         mHandler.sendMessageDelayed(msg, timeout);
     }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
index c3a91ce..f0abb50 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
@@ -150,7 +150,7 @@
             canvas.save();
             canvas.scale(10.0f, 10.0f);
             canvas.drawLine(50.0f, 40.0f, 10.0f, 40.0f, mSmallPaint);
-            canvas.drawLine(10.0f, 50.0f, 50.0f, 50.0f, mSmallPaint);
+            canvas.drawLine(10.0f, 45.0f, 20.0f, 55.0f, mSmallPaint);
             canvas.drawLine(10.0f, 60.0f, 50.0f, 60.0f, mHairLinePaint);
             canvas.restore();
         }
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") {
diff --git a/voip/jni/rtp/Android.mk b/voip/jni/rtp/Android.mk
index 61680a2..47ed658 100644
--- a/voip/jni/rtp/Android.mk
+++ b/voip/jni/rtp/Android.mk
@@ -37,9 +37,9 @@
 	libcutils \
 	libutils \
 	libmedia \
-	libstagefright
+	libstagefright_amrnb_common
 
-LOCAL_STATIC_LIBRARIES := libgsm
+LOCAL_STATIC_LIBRARIES := libgsm libstagefright_amrnbdec libstagefright_amrnbenc
 
 LOCAL_C_INCLUDES += \
 	$(JNI_H_INCLUDE) \