Merge "Telephony: Remove SimCard and RuimCard"
diff --git a/Android.mk b/Android.mk
index 79f6220..5572c50 100644
--- a/Android.mk
+++ b/Android.mk
@@ -127,6 +127,7 @@
 	core/java/android/os/IPermissionController.aidl \
 	core/java/android/os/IPowerManager.aidl \
 	core/java/android/os/IRemoteCallback.aidl \
+	core/java/android/os/IUpdateLock.aidl \
 	core/java/android/os/IVibratorService.aidl \
 	core/java/android/service/wallpaper/IWallpaperConnection.aidl \
 	core/java/android/service/wallpaper/IWallpaperEngine.aidl \
diff --git a/api/current.txt b/api/current.txt
index 8af75db..4a607cb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -593,6 +593,7 @@
     field public static final int layerType = 16843604; // 0x1010354
     field public static final int layout = 16842994; // 0x10100f2
     field public static final int layoutAnimation = 16842988; // 0x10100ec
+    field public static final int layoutDirection = 16843689; // 0x10103a9
     field public static final int layout_above = 16843140; // 0x1010184
     field public static final int layout_alignBaseline = 16843142; // 0x1010186
     field public static final int layout_alignBottom = 16843146; // 0x101018a
@@ -614,10 +615,10 @@
     field public static final int layout_height = 16842997; // 0x10100f5
     field public static final int layout_margin = 16842998; // 0x10100f6
     field public static final int layout_marginBottom = 16843002; // 0x10100fa
-    field public static final int layout_marginEnd = 16843692; // 0x10103ac
+    field public static final int layout_marginEnd = 16843693; // 0x10103ad
     field public static final int layout_marginLeft = 16842999; // 0x10100f7
     field public static final int layout_marginRight = 16843001; // 0x10100f9
-    field public static final int layout_marginStart = 16843691; // 0x10103ab
+    field public static final int layout_marginStart = 16843692; // 0x10103ac
     field public static final int layout_marginTop = 16843000; // 0x10100f8
     field public static final int layout_row = 16843643; // 0x101037b
     field public static final int layout_rowSpan = 16843644; // 0x101037c
@@ -713,10 +714,10 @@
     field public static final int packageNames = 16843649; // 0x1010381
     field public static final int padding = 16842965; // 0x10100d5
     field public static final int paddingBottom = 16842969; // 0x10100d9
-    field public static final int paddingEnd = 16843690; // 0x10103aa
+    field public static final int paddingEnd = 16843691; // 0x10103ab
     field public static final int paddingLeft = 16842966; // 0x10100d6
     field public static final int paddingRight = 16842968; // 0x10100d8
-    field public static final int paddingStart = 16843689; // 0x10103a9
+    field public static final int paddingStart = 16843690; // 0x10103aa
     field public static final int paddingTop = 16842967; // 0x10100d7
     field public static final int panelBackground = 16842846; // 0x101005e
     field public static final int panelColorBackground = 16842849; // 0x1010061
@@ -7392,8 +7393,8 @@
     ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
     method public synchronized void close();
     method public java.lang.String getDatabaseName();
-    method public synchronized android.database.sqlite.SQLiteDatabase getReadableDatabase();
-    method public synchronized android.database.sqlite.SQLiteDatabase getWritableDatabase();
+    method public android.database.sqlite.SQLiteDatabase getReadableDatabase();
+    method public android.database.sqlite.SQLiteDatabase getWritableDatabase();
     method public abstract void onCreate(android.database.sqlite.SQLiteDatabase);
     method public void onDowngrade(android.database.sqlite.SQLiteDatabase, int, int);
     method public void onOpen(android.database.sqlite.SQLiteDatabase);
@@ -7580,6 +7581,7 @@
     method public java.lang.String getOriginalMimeType(android.net.Uri);
     method public int openConvertSession(java.lang.String);
     method public int processDrmInfo(android.drm.DrmInfo);
+    method public void release();
     method public int removeAllRights();
     method public int removeRights(java.lang.String);
     method public int removeRights(android.net.Uri);
@@ -11815,6 +11817,7 @@
     enum_constant public static final android.net.NetworkInfo.DetailedState OBTAINING_IPADDR;
     enum_constant public static final android.net.NetworkInfo.DetailedState SCANNING;
     enum_constant public static final android.net.NetworkInfo.DetailedState SUSPENDED;
+    enum_constant public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK;
   }
 
   public static final class NetworkInfo.State extends java.lang.Enum {
@@ -15343,6 +15346,7 @@
     method public abstract void acquired();
     method public void cleanup(android.os.IBinder, boolean);
     method public void dump();
+    method public void dump(java.io.PrintWriter);
     method public boolean isAcquired();
     method public void release(android.os.IBinder);
     method public abstract void released();
@@ -23102,6 +23106,7 @@
     method public void buildDrawingCache(boolean);
     method public void buildLayer();
     method public boolean callOnClick();
+    method public boolean canResolveLayoutDirection();
     method public boolean canScrollHorizontally(int);
     method public boolean canScrollVertically(int);
     method public void cancelLongPress();
@@ -23187,6 +23192,7 @@
     method public boolean getKeepScreenOn();
     method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
     method public int getLayerType();
+    method public int getLayoutDirection();
     method public android.view.ViewGroup.LayoutParams getLayoutParams();
     method public final int getLeft();
     method protected float getLeftFadingEdgeStrength();
@@ -23216,6 +23222,8 @@
     method public final android.view.ViewParent getParent();
     method public float getPivotX();
     method public float getPivotY();
+    method public int getResolvedLayoutDirection();
+    method public int getResolvedLayoutDirection(android.graphics.drawable.Drawable);
     method public int getResolvedTextDirection();
     method public android.content.res.Resources getResources();
     method public final int getRight();
@@ -23283,7 +23291,9 @@
     method public boolean isHovered();
     method public boolean isInEditMode();
     method public boolean isInTouchMode();
+    method protected static boolean isLayoutDirectionRtl(java.util.Locale);
     method public boolean isLayoutRequested();
+    method public boolean isLayoutRtl();
     method public boolean isLongClickable();
     method public boolean isOpaque();
     method protected boolean isPaddingOffsetRequired();
@@ -23372,6 +23382,7 @@
     method public void requestLayout();
     method public boolean requestRectangleOnScreen(android.graphics.Rect);
     method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
+    method public void resetResolvedLayoutDirection();
     method public void resetResolvedTextDirection();
     method public void resolvePadding();
     method public static int resolveSize(int, int);
@@ -23412,6 +23423,7 @@
     method public void setId(int);
     method public void setKeepScreenOn(boolean);
     method public void setLayerType(int, android.graphics.Paint);
+    method public void setLayoutDirection(int);
     method public void setLayoutParams(android.view.ViewGroup.LayoutParams);
     method public final void setLeft(int);
     method public void setLongClickable(boolean);
@@ -23514,6 +23526,10 @@
     field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
     field public static final int LAYER_TYPE_NONE = 0; // 0x0
     field public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1
+    field public static final int LAYOUT_DIRECTION_INHERIT = -2147483648; // 0x80000000
+    field public static final int LAYOUT_DIRECTION_LOCALE = -1073741824; // 0xc0000000
+    field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
+    field public static final int LAYOUT_DIRECTION_RTL = 1073741824; // 0x40000000
     field public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
     field public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff
     field public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000
@@ -23828,7 +23844,6 @@
     method public void requestDisallowInterceptTouchEvent(boolean);
     method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public void requestTransparentRegion(android.view.View);
-    method protected void resetResolvedLayoutDirection();
     method public void scheduleLayoutAnimation();
     method public void setAddStatesFromChildren(boolean);
     method public void setAlwaysDrawnWithCacheEnabled(boolean);
@@ -23866,6 +23881,7 @@
     ctor public ViewGroup.LayoutParams(android.content.Context, android.util.AttributeSet);
     ctor public ViewGroup.LayoutParams(int, int);
     ctor public ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams);
+    method public void onResolveLayoutDirection(int);
     method protected void setBaseAttributes(android.content.res.TypedArray, int, int);
     field public static final deprecated int FILL_PARENT = -1; // 0xffffffff
     field public static final int MATCH_PARENT = -1; // 0xffffffff
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 1dcba70..bd9eb9a 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -69,6 +69,7 @@
         + "  <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n"
         + "  <TYPE> specifies data type such as:\n"
         + "  b - boolean, s - string, i - integer, l - long, f - float, d - double\n"
+        + "  Note: Omit the value for passing an empty string, e.g column:s:\n"
         + "  Example:\n"
         + "  # Add \"new_setting\" secure setting with value \"new_value\".\n"
         + "  adb shell content insert --uri content://settings/secure --bind name:s:new_setting"
@@ -245,13 +246,17 @@
             if (TextUtils.isEmpty(argument)) {
                 throw new IllegalArgumentException("Binding not well formed: " + argument);
             }
-            String[] binding = argument.split(COLON);
-            if (binding.length != 3) {
+            final int firstColonIndex = argument.indexOf(COLON);
+            if (firstColonIndex < 0) {
                 throw new IllegalArgumentException("Binding not well formed: " + argument);
             }
-            String column = binding[0];
-            String type = binding[1];
-            String value = binding[2];
+            final int secondColonIndex = argument.indexOf(COLON, firstColonIndex + 1);
+            if (secondColonIndex < 0) {
+                throw new IllegalArgumentException("Binding not well formed: " + argument);
+            }
+            String column = argument.substring(0, firstColonIndex);
+            String type = argument.substring(firstColonIndex + 1, secondColonIndex);
+            String value = argument.substring(secondColonIndex + 1);
             if (TYPE_STRING.equals(type)) {
                 values.put(column, value);
             } else if (TYPE_BOOLEAN.equalsIgnoreCase(type)) {
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index ebe28db..13b63dc 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -86,16 +86,18 @@
     dump_file("PAGETYPEINFO", "/proc/pagetypeinfo");
     dump_file("BUDDYINFO", "/proc/buddyinfo");
 
-    if (screenshot_path[0]) {
-        ALOGI("taking screenshot\n");
-        run_command(NULL, 5, SU_PATH, "root", "screenshot", screenshot_path, NULL);
-        ALOGI("wrote screenshot: %s\n", screenshot_path);
-    }
+    print_properties();
 
-    run_command("SYSTEM SETTINGS", 20, SU_PATH, "root", "sqlite3",
-            "/data/data/com.android.providers.settings/databases/settings.db",
-            "pragma user_version; select * from system; select * from secure;", NULL);
-    run_command("SYSTEM LOG", 20, "logcat", "-v", "threadtime", "-d", "*:v", NULL);
+    /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
+    dump_file("LAST KMSG", "/proc/last_kmsg");
+    do_dmesg();
+
+    dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
+    dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
+
+    run_command("PROCESSES", 10, "ps", "-P", NULL);
+    run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL);
+    run_command("LIBRANK", 10, "librank", NULL);
 
     /* show the traces we collected in main(), if that was done */
     if (dump_traces_path != NULL) {
@@ -114,11 +116,6 @@
         dump_file("VM TRACES AT LAST ANR", anr_traces_path);
     }
 
-    // dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
-    run_command("EVENT LOG", 20, "logcat", "-b", "events", "-v", "threadtime", "-d", "*:v", NULL);
-    run_command("RADIO LOG", 20, "logcat", "-b", "radio", "-v", "threadtime", "-d", "*:v", NULL);
-
-    run_command("NETWORK INTERFACES", 10, SU_PATH, "root", "netcfg", NULL);
     dump_file("NETWORK DEV INFO", "/proc/net/dev");
     dump_file("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
     dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
@@ -126,6 +123,23 @@
 
     dump_file("NETWORK ROUTES", "/proc/net/route");
     dump_file("NETWORK ROUTES IPV6", "/proc/net/ipv6_route");
+
+    if (screenshot_path[0]) {
+        ALOGI("taking screenshot\n");
+        run_command(NULL, 5, SU_PATH, "root", "screenshot", screenshot_path, NULL);
+        ALOGI("wrote screenshot: %s\n", screenshot_path);
+    }
+
+    run_command("SYSTEM SETTINGS", 20, SU_PATH, "root", "sqlite3",
+            "/data/data/com.android.providers.settings/databases/settings.db",
+            "pragma user_version; select * from system; select * from secure;", NULL);
+
+    // dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
+    run_command("SYSTEM LOG", 20, "logcat", "-v", "threadtime", "-d", "*:v", NULL);
+    run_command("EVENT LOG", 20, "logcat", "-b", "events", "-v", "threadtime", "-d", "*:v", NULL);
+    run_command("RADIO LOG", 20, "logcat", "-b", "radio", "-v", "threadtime", "-d", "*:v", NULL);
+
+    run_command("NETWORK INTERFACES", 10, SU_PATH, "root", "netcfg", NULL);
     run_command("IP RULES", 10, "ip", "rule", "show", NULL);
     run_command("IP RULES v6", 10, "ip", "-6", "rule", "show", NULL);
     run_command("ROUTE TABLE 60", 10, "ip", "route", "show", "table", "60", NULL);
@@ -172,20 +186,9 @@
         }
     }
 
-    print_properties();
-
-    do_dmesg();
-
-    dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
-    dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
-
     run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
     run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
 
-    run_command("PROCESSES", 10, "ps", "-P", NULL);
-    run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL);
-    run_command("LIBRANK", 10, "librank", NULL);
-
     dump_file("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
     dump_file("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
     dump_file("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
@@ -197,8 +200,6 @@
     dump_file("PACKAGE SETTINGS", "/data/system/packages.xml");
     dump_file("PACKAGE UID ERRORS", "/data/system/uiderrors.txt");
 
-    /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
-    dump_file("LAST KMSG", "/proc/last_kmsg");
     run_command("LAST RADIO LOG", 10, "parse_radio_log", "/proc/last_radio_log", NULL);
     dump_file("LAST PANIC CONSOLE", "/data/dontpanic/apanic_console");
     dump_file("LAST PANIC THREADS", "/data/dontpanic/apanic_threads");
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d4b9a36..f10bf03 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3751,6 +3751,7 @@
     }
 
     final void handleTrimMemory(int level) {
+        WindowManagerImpl.getDefault().trimMemory(level);
         ArrayList<ComponentCallbacks2> callbacks;
 
         synchronized (mPackages) {
@@ -3761,7 +3762,7 @@
         for (int i=0; i<N; i++) {
             callbacks.get(i).onTrimMemory(level);
         }
-        WindowManagerImpl.getDefault().trimMemory(level);
+        WindowManagerImpl.getDefault().terminateEgl();
     }
 
     private void setupGraphicsSupport(LoadedApk info) {
@@ -3986,40 +3987,40 @@
             dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
         }
 
-        // allow disk access during application and provider setup. this could
+        // Allow disk access during application and provider setup. This could
         // block processing ordered broadcasts, but later processing would
-        // probably end up doing the same disk access. restore not guarded with
-        // finally block, since exceptions here will take down the application.
+        // probably end up doing the same disk access.
         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
-
-        // If the app is being launched for full backup or restore, bring it up in
-        // a restricted environment with the base application class.
-        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
-        mInitialApplication = app;
-
-        // don't bring up providers in restricted mode; they may depend on the
-        // app's custom Application class
-        if (!data.restrictedBackupMode) {
-            List<ProviderInfo> providers = data.providers;
-            if (providers != null) {
-                installContentProviders(app, providers);
-                // For process that contains content providers, we want to
-                // ensure that the JIT is enabled "at some point".
-                mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
-            }
-        }
-
         try {
-            mInstrumentation.callApplicationOnCreate(app);
-        } catch (Exception e) {
-            if (!mInstrumentation.onException(app, e)) {
-                throw new RuntimeException(
-                    "Unable to create application " + app.getClass().getName()
-                    + ": " + e.toString(), e);
-            }
-        }
+            // If the app is being launched for full backup or restore, bring it up in
+            // a restricted environment with the base application class.
+            Application app = data.info.makeApplication(data.restrictedBackupMode, null);
+            mInitialApplication = app;
 
-        StrictMode.setThreadPolicy(savedPolicy);
+            // don't bring up providers in restricted mode; they may depend on the
+            // app's custom Application class
+            if (!data.restrictedBackupMode) {
+                List<ProviderInfo> providers = data.providers;
+                if (providers != null) {
+                    installContentProviders(app, providers);
+                    // For process that contains content providers, we want to
+                    // ensure that the JIT is enabled "at some point".
+                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
+                }
+            }
+
+            try {
+                mInstrumentation.callApplicationOnCreate(app);
+            } catch (Exception e) {
+                if (!mInstrumentation.onException(app, e)) {
+                    throw new RuntimeException(
+                        "Unable to create application " + app.getClass().getName()
+                        + ": " + e.toString(), e);
+                }
+            }
+        } finally {
+            StrictMode.setThreadPolicy(savedPolicy);
+        }
     }
 
     /*package*/ final void finishInstrumentation(int resultCode, Bundle results) {
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 96a65da..7a612bc 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1080,7 +1080,8 @@
     }
 
     /**
-     * Notify registered observers that a row was updated.
+     * Notify registered observers that a row was updated and attempt to sync changes
+     * to the network.
      * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
      * By default, CursorAdapter objects will get this notification.
      *
@@ -1098,6 +1099,9 @@
      * Notify registered observers that a row was updated.
      * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
      * By default, CursorAdapter objects will get this notification.
+     * If syncToNetwork is true, this will attempt to schedule a local sync using the sync
+     * adapter that's registered for the authority of the provided uri. No account will be
+     * passed to the sync adapter, so all matching accounts will be synchronized.
      *
      * @param uri The uri of the content that was changed.
      * @param observer The observer that originated the change, may be <code>null</null>.
@@ -1105,6 +1109,7 @@
      * has requested to receive self-change notifications by implementing
      * {@link ContentObserver#deliverSelfNotifications()} to return true.
      * @param syncToNetwork If true, attempt to sync the change to the network.
+     * @see #requestSync(android.accounts.Account, String, android.os.Bundle)
      */
     public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
         try {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a1198de..111f45e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1646,6 +1646,17 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
+     * android.os.IUpdateLock} for managing runtime sequences that
+     * must not be interrupted by headless OTA application or similar.
+     *
+     * @hide
+     * @see #getSystemService
+     * @see android.os.UpdateLock
+     */
+    public static final String UPDATE_LOCK_SERVICE = "updatelock";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a {@link
      * android.net.NetworkManagementService} for handling management of
      * system network services
      *
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index e04b2f7..079f739 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -20,6 +20,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.LocaleUtil;
+import android.view.View;
 
 import java.util.Locale;
 
@@ -280,9 +281,9 @@
     public int compatSmallestScreenWidthDp;
 
     /**
-     * @hide The text layout direction associated to the current Locale
+     * @hide The layout direction associated to the current Locale
      */
-    public int textLayoutDirection;
+    public int layoutDirection;
 
     /**
      * @hide Internal book-keeping.
@@ -310,7 +311,7 @@
         mnc = o.mnc;
         if (o.locale != null) {
             locale = (Locale) o.locale.clone();
-            textLayoutDirection = o.textLayoutDirection;
+            layoutDirection = o.layoutDirection;
         }
         userSetLocale = o.userSetLocale;
         touchscreen = o.touchscreen;
@@ -346,10 +347,10 @@
         } else {
             sb.append(" (no locale)");
         }
-        switch (textLayoutDirection) {
-            case LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE: /* ltr not interesting */ break;
-            case LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE: sb.append(" rtl"); break;
-            default: sb.append(" layoutdir="); sb.append(textLayoutDirection); break;
+        switch (layoutDirection) {
+            case View.LAYOUT_DIRECTION_LTR: /* ltr not interesting */ break;
+            case View.LAYOUT_DIRECTION_RTL: sb.append(" rtl"); break;
+            default: sb.append(" layoutDir="); sb.append(layoutDirection); break;
         }
         if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
             sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp");
@@ -472,7 +473,7 @@
         screenWidthDp = compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
         screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
         smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
-        textLayoutDirection = LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
+        layoutDirection = View.LAYOUT_DIRECTION_LTR;
         seq = 0;
     }
 
@@ -508,7 +509,7 @@
             changed |= ActivityInfo.CONFIG_LOCALE;
             locale = delta.locale != null
                     ? (Locale) delta.locale.clone() : null;
-            textLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale);
+            layoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale);
         }
         if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
         {
@@ -776,7 +777,7 @@
         dest.writeInt(compatScreenWidthDp);
         dest.writeInt(compatScreenHeightDp);
         dest.writeInt(compatSmallestScreenWidthDp);
-        dest.writeInt(textLayoutDirection);
+        dest.writeInt(layoutDirection);
         dest.writeInt(seq);
     }
 
@@ -804,7 +805,7 @@
         compatScreenWidthDp = source.readInt();
         compatScreenHeightDp = source.readInt();
         compatSmallestScreenWidthDp = source.readInt();
-        textLayoutDirection = source.readInt();
+        layoutDirection = source.readInt();
         seq = source.readInt();
     }
     
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index b5cef81..1900301 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -208,6 +208,11 @@
                 mConfiguration.label,
                 SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);
 
+        setSyncMode();
+        setPageSize();
+        setAutoCheckpointInterval();
+        setJournalSizeLimit();
+        setJournalModeFromConfiguration();
         setLocaleFromConfiguration();
     }
 
@@ -231,6 +236,44 @@
         }
     }
 
+    private void setSyncMode() {
+        if (!mConfiguration.isInMemoryDb()) {
+            execute("PRAGMA synchronous=" + SQLiteGlobal.getSyncMode(), null, null);
+        }
+    }
+
+    private void setPageSize() {
+        if (!mConfiguration.isInMemoryDb()) {
+            execute("PRAGMA page_size=" + SQLiteGlobal.getDefaultPageSize(), null, null);
+        }
+    }
+
+    private void setAutoCheckpointInterval() {
+        if (!mConfiguration.isInMemoryDb()) {
+            executeForLong("PRAGMA wal_autocheckpoint=" + SQLiteGlobal.getWALAutoCheckpoint(),
+                    null, null);
+        }
+    }
+
+    private void setJournalSizeLimit() {
+        if (!mConfiguration.isInMemoryDb()) {
+            executeForLong("PRAGMA journal_size_limit=" + SQLiteGlobal.getJournalSizeLimit(),
+                    null, null);
+        }
+    }
+
+    private void setJournalModeFromConfiguration() {
+        if (!mConfiguration.isInMemoryDb()) {
+            String result = executeForString("PRAGMA journal_mode=" + mConfiguration.journalMode,
+                    null, null);
+            if (!result.equalsIgnoreCase(mConfiguration.journalMode)) {
+                Log.e(TAG, "setting journal_mode to " + mConfiguration.journalMode
+                        + " failed for db: " + mConfiguration.label
+                        + " (on pragma set journal_mode, sqlite returned:" + result);
+            }
+        }
+    }
+
     private void setLocaleFromConfiguration() {
         nativeSetLocale(mConnectionPtr, mConfiguration.locale.toString());
     }
@@ -246,7 +289,9 @@
             }
         }
 
-        // Remember whether locale has changed.
+        // Remember what changed.
+        boolean journalModeChanged = !configuration.journalMode.equalsIgnoreCase(
+                mConfiguration.journalMode);
         boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
 
         // Update configuration parameters.
@@ -255,6 +300,11 @@
         // Update prepared statement cache size.
         mPreparedStatementCache.resize(configuration.maxSqlCacheSize);
 
+        // Update journal mode.
+        if (journalModeChanged) {
+            setJournalModeFromConfiguration();
+        }
+
         // Update locale.
         if (localeChanged) {
             setLocaleFromConfiguration();
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 236948e..3562e89 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -92,13 +92,25 @@
             new ArrayList<SQLiteConnection>();
     private SQLiteConnection mAvailablePrimaryConnection;
 
+    // Describes what should happen to an acquired connection when it is returned to the pool.
+    enum AcquiredConnectionStatus {
+        // The connection should be returned to the pool as usual.
+        NORMAL,
+
+        // The connection must be reconfigured before being returned.
+        RECONFIGURE,
+
+        // The connection must be closed and discarded.
+        DISCARD,
+    }
+
     // Weak references to all acquired connections.  The associated value
-    // is a boolean that indicates whether the connection must be reconfigured
-    // before being returned to the available connection list.
+    // indicates whether the connection must be reconfigured before being
+    // returned to the available connection list or discarded.
     // For example, the prepared statement cache size may have changed and
-    // need to be updated.
-    private final WeakHashMap<SQLiteConnection, Boolean> mAcquiredConnections =
-            new WeakHashMap<SQLiteConnection, Boolean>();
+    // need to be updated in preparation for the next client.
+    private final WeakHashMap<SQLiteConnection, AcquiredConnectionStatus> mAcquiredConnections =
+            new WeakHashMap<SQLiteConnection, AcquiredConnectionStatus>();
 
     /**
      * Connection flag: Read-only.
@@ -168,7 +180,7 @@
     private void open() {
         // Open the primary connection.
         // This might throw if the database is corrupt.
-        mAvailablePrimaryConnection = openConnectionLocked(
+        mAvailablePrimaryConnection = openConnectionLocked(mConfiguration,
                 true /*primaryConnection*/); // might throw
 
         // Mark the pool as being open for business.
@@ -209,16 +221,7 @@
 
                 mIsOpen = false;
 
-                final int count = mAvailableNonPrimaryConnections.size();
-                for (int i = 0; i < count; i++) {
-                    closeConnectionAndLogExceptionsLocked(mAvailableNonPrimaryConnections.get(i));
-                }
-                mAvailableNonPrimaryConnections.clear();
-
-                if (mAvailablePrimaryConnection != null) {
-                    closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
-                    mAvailablePrimaryConnection = null;
-                }
+                closeAvailableConnectionsAndLogExceptionsLocked();
 
                 final int pendingCount = mAcquiredConnections.size();
                 if (pendingCount != 0) {
@@ -254,21 +257,27 @@
         synchronized (mLock) {
             throwIfClosedLocked();
 
-            final boolean poolSizeChanged = mConfiguration.maxConnectionPoolSize
-                    != configuration.maxConnectionPoolSize;
-            mConfiguration.updateParametersFrom(configuration);
+            if (mConfiguration.openFlags != configuration.openFlags) {
+                // Try to reopen the primary connection using the new open flags then
+                // close and discard all existing connections.
+                // This might throw if the database is corrupt or cannot be opened in
+                // the new mode in which case existing connections will remain untouched.
+                SQLiteConnection newPrimaryConnection = openConnectionLocked(configuration,
+                        true /*primaryConnection*/); // might throw
 
-            if (poolSizeChanged) {
-                int availableCount = mAvailableNonPrimaryConnections.size();
-                while (availableCount-- > mConfiguration.maxConnectionPoolSize - 1) {
-                    SQLiteConnection connection =
-                            mAvailableNonPrimaryConnections.remove(availableCount);
-                    closeConnectionAndLogExceptionsLocked(connection);
-                }
+                closeAvailableConnectionsAndLogExceptionsLocked();
+                discardAcquiredConnectionsLocked();
+
+                mAvailablePrimaryConnection = newPrimaryConnection;
+                mConfiguration.updateParametersFrom(configuration);
+            } else {
+                // Reconfigure the database connections in place.
+                mConfiguration.updateParametersFrom(configuration);
+
+                closeExcessConnectionsAndLogExceptionsLocked();
+                reconfigureAllConnectionsLocked();
             }
 
-            reconfigureAllConnectionsLocked();
-
             wakeConnectionWaitersLocked();
         }
     }
@@ -310,8 +319,8 @@
      */
     public void releaseConnection(SQLiteConnection connection) {
         synchronized (mLock) {
-            Boolean mustReconfigure = mAcquiredConnections.remove(connection);
-            if (mustReconfigure == null) {
+            AcquiredConnectionStatus status = mAcquiredConnections.remove(connection);
+            if (status == null) {
                 throw new IllegalStateException("Cannot perform this operation "
                         + "because the specified connection was not acquired "
                         + "from this pool or has already been released.");
@@ -320,18 +329,8 @@
             if (!mIsOpen) {
                 closeConnectionAndLogExceptionsLocked(connection);
             } else if (connection.isPrimaryConnection()) {
-                assert mAvailablePrimaryConnection == null;
-                try {
-                    if (mustReconfigure == Boolean.TRUE) {
-                        connection.reconfigure(mConfiguration); // might throw
-                    }
-                } catch (RuntimeException ex) {
-                    Log.e(TAG, "Failed to reconfigure released primary connection, closing it: "
-                            + connection, ex);
-                    closeConnectionAndLogExceptionsLocked(connection);
-                    connection = null;
-                }
-                if (connection != null) {
+                if (recycleConnectionLocked(connection, status)) {
+                    assert mAvailablePrimaryConnection == null;
                     mAvailablePrimaryConnection = connection;
                 }
                 wakeConnectionWaitersLocked();
@@ -339,17 +338,7 @@
                     mConfiguration.maxConnectionPoolSize - 1) {
                 closeConnectionAndLogExceptionsLocked(connection);
             } else {
-                try {
-                    if (mustReconfigure == Boolean.TRUE) {
-                        connection.reconfigure(mConfiguration); // might throw
-                    }
-                } catch (RuntimeException ex) {
-                    Log.e(TAG, "Failed to reconfigure released non-primary connection, "
-                            + "closing it: " + connection, ex);
-                    closeConnectionAndLogExceptionsLocked(connection);
-                    connection = null;
-                }
-                if (connection != null) {
+                if (recycleConnectionLocked(connection, status)) {
                     mAvailableNonPrimaryConnections.add(connection);
                 }
                 wakeConnectionWaitersLocked();
@@ -357,6 +346,25 @@
         }
     }
 
+    // Can't throw.
+    private boolean recycleConnectionLocked(SQLiteConnection connection,
+            AcquiredConnectionStatus status) {
+        if (status == AcquiredConnectionStatus.RECONFIGURE) {
+            try {
+                connection.reconfigure(mConfiguration); // might throw
+            } catch (RuntimeException ex) {
+                Log.e(TAG, "Failed to reconfigure released connection, closing it: "
+                        + connection, ex);
+                status = AcquiredConnectionStatus.DISCARD;
+            }
+        }
+        if (status == AcquiredConnectionStatus.DISCARD) {
+            closeConnectionAndLogExceptionsLocked(connection);
+            return false;
+        }
+        return true;
+    }
+
     /**
      * Returns true if the session should yield the connection due to
      * contention over available database connections.
@@ -407,9 +415,10 @@
     }
 
     // Might throw.
-    private SQLiteConnection openConnectionLocked(boolean primaryConnection) {
+    private SQLiteConnection openConnectionLocked(SQLiteDatabaseConfiguration configuration,
+            boolean primaryConnection) {
         final int connectionId = mNextConnectionId++;
-        return SQLiteConnection.open(this, mConfiguration,
+        return SQLiteConnection.open(this, configuration,
                 connectionId, primaryConnection); // might throw
     }
 
@@ -443,6 +452,30 @@
     }
 
     // Can't throw.
+    private void closeAvailableConnectionsAndLogExceptionsLocked() {
+        final int count = mAvailableNonPrimaryConnections.size();
+        for (int i = 0; i < count; i++) {
+            closeConnectionAndLogExceptionsLocked(mAvailableNonPrimaryConnections.get(i));
+        }
+        mAvailableNonPrimaryConnections.clear();
+
+        if (mAvailablePrimaryConnection != null) {
+            closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
+            mAvailablePrimaryConnection = null;
+        }
+    }
+
+    // Can't throw.
+    private void closeExcessConnectionsAndLogExceptionsLocked() {
+        int availableCount = mAvailableNonPrimaryConnections.size();
+        while (availableCount-- > mConfiguration.maxConnectionPoolSize - 1) {
+            SQLiteConnection connection =
+                    mAvailableNonPrimaryConnections.remove(availableCount);
+            closeConnectionAndLogExceptionsLocked(connection);
+        }
+    }
+
+    // Can't throw.
     private void closeConnectionAndLogExceptionsLocked(SQLiteConnection connection) {
         try {
             connection.close(); // might throw
@@ -453,8 +486,12 @@
     }
 
     // Can't throw.
+    private void discardAcquiredConnectionsLocked() {
+        markAcquiredConnectionsLocked(AcquiredConnectionStatus.DISCARD);
+    }
+
+    // Can't throw.
     private void reconfigureAllConnectionsLocked() {
-        boolean wake = false;
         if (mAvailablePrimaryConnection != null) {
             try {
                 mAvailablePrimaryConnection.reconfigure(mConfiguration); // might throw
@@ -463,7 +500,6 @@
                         + mAvailablePrimaryConnection, ex);
                 closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
                 mAvailablePrimaryConnection = null;
-                wake = true;
             }
         }
 
@@ -478,27 +514,30 @@
                 closeConnectionAndLogExceptionsLocked(connection);
                 mAvailableNonPrimaryConnections.remove(i--);
                 count -= 1;
-                wake = true;
             }
         }
 
+        markAcquiredConnectionsLocked(AcquiredConnectionStatus.RECONFIGURE);
+    }
+
+    // Can't throw.
+    private void markAcquiredConnectionsLocked(AcquiredConnectionStatus status) {
         if (!mAcquiredConnections.isEmpty()) {
             ArrayList<SQLiteConnection> keysToUpdate = new ArrayList<SQLiteConnection>(
                     mAcquiredConnections.size());
-            for (Map.Entry<SQLiteConnection, Boolean> entry : mAcquiredConnections.entrySet()) {
-                if (entry.getValue() != Boolean.TRUE) {
+            for (Map.Entry<SQLiteConnection, AcquiredConnectionStatus> entry
+                    : mAcquiredConnections.entrySet()) {
+                AcquiredConnectionStatus oldStatus = entry.getValue();
+                if (status != oldStatus
+                        && oldStatus != AcquiredConnectionStatus.DISCARD) {
                     keysToUpdate.add(entry.getKey());
                 }
             }
             final int updateCount = keysToUpdate.size();
             for (int i = 0; i < updateCount; i++) {
-                mAcquiredConnections.put(keysToUpdate.get(i), Boolean.TRUE);
+                mAcquiredConnections.put(keysToUpdate.get(i), status);
             }
         }
-
-        if (wake) {
-            wakeConnectionWaitersLocked();
-        }
     }
 
     // Might throw.
@@ -658,8 +697,7 @@
         int activeConnections = 0;
         int idleConnections = 0;
         if (!mAcquiredConnections.isEmpty()) {
-            for (Map.Entry<SQLiteConnection, Boolean> entry : mAcquiredConnections.entrySet()) {
-                final SQLiteConnection connection = entry.getKey();
+            for (SQLiteConnection connection : mAcquiredConnections.keySet()) {
                 String description = connection.describeCurrentOperationUnsafe();
                 if (description != null) {
                     requests.add(description);
@@ -769,7 +807,8 @@
 
         // Uhoh.  No primary connection!  Either this is the first time we asked
         // for it, or maybe it leaked?
-        connection = openConnectionLocked(true /*primaryConnection*/); // might throw
+        connection = openConnectionLocked(mConfiguration,
+                true /*primaryConnection*/); // might throw
         finishAcquireConnectionLocked(connection, connectionFlags); // might throw
         return connection;
     }
@@ -807,7 +846,8 @@
         if (openConnections >= mConfiguration.maxConnectionPoolSize) {
             return null;
         }
-        connection = openConnectionLocked(false /*primaryConnection*/); // might throw
+        connection = openConnectionLocked(mConfiguration,
+                false /*primaryConnection*/); // might throw
         finishAcquireConnectionLocked(connection, connectionFlags); // might throw
         return connection;
     }
@@ -818,7 +858,7 @@
             final boolean readOnly = (connectionFlags & CONNECTION_FLAG_READ_ONLY) != 0;
             connection.setOnlyAllowReadOnlyOperations(readOnly);
 
-            mAcquiredConnections.put(connection, Boolean.FALSE);
+            mAcquiredConnections.put(connection, AcquiredConnectionStatus.NORMAL);
         } catch (RuntimeException ex) {
             Log.e(TAG, "Failed to prepare acquired connection for session, closing it: "
                     + connection +", connectionFlags=" + connectionFlags);
@@ -858,7 +898,7 @@
     private void throwIfClosedLocked() {
         if (!mIsOpen) {
             throw new IllegalStateException("Cannot perform this operation "
-                    + "because the connection pool have been closed.");
+                    + "because the connection pool has been closed.");
         }
     }
 
@@ -922,11 +962,11 @@
 
             printer.println("  Acquired connections:");
             if (!mAcquiredConnections.isEmpty()) {
-                for (Map.Entry<SQLiteConnection, Boolean> entry :
+                for (Map.Entry<SQLiteConnection, AcquiredConnectionStatus> entry :
                         mAcquiredConnections.entrySet()) {
                     final SQLiteConnection connection = entry.getKey();
                     connection.dumpUnsafe(indentedPrinter, verbose);
-                    indentedPrinter.println("  Pending reconfiguration: " + entry.getValue());
+                    indentedPrinter.println("  Status: " + entry.getValue());
                 }
             } else {
                 indentedPrinter.println("<none>");
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 505f83e..515658f 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -677,6 +677,38 @@
         return openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler);
     }
 
+    /**
+     * Reopens the database in read-write mode.
+     * If the database is already read-write, does nothing.
+     *
+     * @throws SQLiteException if the database could not be reopened as requested, in which
+     * case it remains open in read only mode.
+     * @throws IllegalStateException if the database is not open.
+     *
+     * @see #isReadOnly()
+     * @hide
+     */
+    public void reopenReadWrite() {
+        synchronized (mLock) {
+            throwIfNotOpenLocked();
+
+            if (!isReadOnlyLocked()) {
+                return; // nothing to do
+            }
+
+            // Reopen the database in read-write mode.
+            final int oldOpenFlags = mConfigurationLocked.openFlags;
+            mConfigurationLocked.openFlags = (mConfigurationLocked.openFlags & ~OPEN_READ_MASK)
+                    | OPEN_READWRITE;
+            try {
+                mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+            } catch (RuntimeException ex) {
+                mConfigurationLocked.openFlags = oldOpenFlags;
+                throw ex;
+            }
+        }
+    }
+
     private void open() {
         try {
             try {
@@ -685,9 +717,6 @@
                 onCorruption();
                 openInner();
             }
-
-            // Disable WAL if it was previously enabled.
-            setJournalMode("TRUNCATE");
         } catch (SQLiteException ex) {
             Log.e(TAG, "Failed to open database '" + getLabel() + "'.", ex);
             close();
@@ -707,19 +736,6 @@
         }
     }
 
-    private void setJournalMode(String mode) {
-        // Journal mode can be set only for non-memory databases
-        // AND can't be set for readonly databases
-        if (isInMemoryDatabase() || isReadOnly()) {
-            return;
-        }
-        String s = DatabaseUtils.stringForQuery(this, "PRAGMA journal_mode=" + mode, null);
-        if (!s.equalsIgnoreCase(mode)) {
-            Log.e(TAG, "setting journal_mode to " + mode + " failed for db: " + getLabel()
-                    + " (on pragma set journal_mode, sqlite returned:" + s);
-        }
-    }
-
     /**
      * Create a memory backed SQLite database.  Its contents will be destroyed
      * when the database is closed.
@@ -1729,13 +1745,10 @@
             }
 
             mIsWALEnabledLocked = true;
-            mConfigurationLocked.maxConnectionPoolSize = Math.max(2,
-                    Resources.getSystem().getInteger(
-                            com.android.internal.R.integer.db_connection_pool_size));
+            mConfigurationLocked.maxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
+            mConfigurationLocked.journalMode = "WAL";
             mConnectionPoolLocked.reconfigure(mConfigurationLocked);
         }
-
-        setJournalMode("WAL");
         return true;
     }
 
@@ -1753,10 +1766,9 @@
 
             mIsWALEnabledLocked = false;
             mConfigurationLocked.maxConnectionPoolSize = 1;
+            mConfigurationLocked.journalMode = SQLiteGlobal.getDefaultJournalMode();
             mConnectionPoolLocked.reconfigure(mConfigurationLocked);
         }
-
-        setJournalMode("TRUNCATE");
     }
 
     /**
@@ -1902,28 +1914,6 @@
         return true;
     }
 
-    /**
-     * Prevent other threads from using the database's primary connection.
-     *
-     * This method is only used by {@link SQLiteOpenHelper} when transitioning from
-     * a readable to a writable database.  It should not be used in any other way.
-     *
-     * @see #unlockPrimaryConnection()
-     */
-    void lockPrimaryConnection() {
-        getThreadSession().beginTransaction(SQLiteSession.TRANSACTION_MODE_DEFERRED,
-                null, SQLiteConnectionPool.CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY, null);
-    }
-
-    /**
-     * Allow other threads to use the database's primary connection.
-     *
-     * @see #lockPrimaryConnection()
-     */
-    void unlockPrimaryConnection() {
-        getThreadSession().endTransaction(null);
-    }
-
     @Override
     public String toString() {
         return "SQLiteDatabase: " + getPath();
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index bc79ad3..32a1bcb 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -51,17 +51,17 @@
     public final String path;
 
     /**
-     * The flags used to open the database.
-     */
-    public final int openFlags;
-
-    /**
      * The label to use to describe the database when it appears in logs.
      * This is derived from the path but is stripped to remove PII.
      */
     public final String label;
 
     /**
+     * The flags used to open the database.
+     */
+    public int openFlags;
+
+    /**
      * The maximum number of connections to retain in the connection pool.
      * Must be at least 1.
      *
@@ -85,6 +85,13 @@
     public Locale locale;
 
     /**
+     * The database journal mode.
+     *
+     * Default is {@link SQLiteGlobal#getDefaultJournalMode()}.
+     */
+    public String journalMode;
+
+    /**
      * The custom functions to register.
      */
     public final ArrayList<SQLiteCustomFunction> customFunctions =
@@ -103,13 +110,14 @@
         }
 
         this.path = path;
-        this.openFlags = openFlags;
         label = stripPathForLogs(path);
+        this.openFlags = openFlags;
 
         // Set default values for optional parameters.
         maxConnectionPoolSize = 1;
         maxSqlCacheSize = 25;
         locale = Locale.getDefault();
+        journalMode = SQLiteGlobal.getDefaultJournalMode();
     }
 
     /**
@@ -123,7 +131,6 @@
         }
 
         this.path = other.path;
-        this.openFlags = other.openFlags;
         this.label = other.label;
         updateParametersFrom(other);
     }
@@ -138,14 +145,16 @@
         if (other == null) {
             throw new IllegalArgumentException("other must not be null.");
         }
-        if (!path.equals(other.path) || openFlags != other.openFlags) {
+        if (!path.equals(other.path)) {
             throw new IllegalArgumentException("other configuration must refer to "
                     + "the same database.");
         }
 
+        openFlags = other.openFlags;
         maxConnectionPoolSize = other.maxConnectionPoolSize;
         maxSqlCacheSize = other.maxSqlCacheSize;
         locale = other.locale;
+        journalMode = other.journalMode;
         customFunctions.clear();
         customFunctions.addAll(other.customFunctions);
     }
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index 204483d..10ce991 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -33,12 +33,16 @@
 
     /**
      * Controls the printing of informational SQL log messages.
+     *
+     * Enable using "adb shell setprop log.tag.SQLiteLog VERBOSE".
      */
     public static final boolean DEBUG_SQL_LOG =
             Log.isLoggable("SQLiteLog", Log.VERBOSE);
 
     /**
      * Controls the printing of SQL statements as they are executed.
+     *
+     * Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE".
      */
     public static final boolean DEBUG_SQL_STATEMENTS =
             Log.isLoggable("SQLiteStatements", Log.VERBOSE);
@@ -46,6 +50,8 @@
     /**
      * Controls the printing of wall-clock time taken to execute SQL statements
      * as they are executed.
+     *
+     * Enable using "adb shell setprop log.tag.SQLiteTime VERBOSE".
      */
     public static final boolean DEBUG_SQL_TIME =
             Log.isLoggable("SQLiteTime", Log.VERBOSE);
@@ -61,15 +67,17 @@
      *
      * Reads the "db.log.slow_query_threshold" system property, which can be changed
      * by the user at any time.  If the value is zero, then all queries will
-     * be considered slow.  If the value does not exist, then no queries will
+     * be considered slow.  If the value does not exist or is negative, then no queries will
      * be considered slow.
      *
      * This value can be changed dynamically while the system is running.
+     * For example, "adb shell setprop db.log.slow_query_threshold 200" will
+     * log all queries that take 200ms or longer to run.
      * @hide
      */
     public static final boolean shouldLogSlowQuery(long elapsedTimeMillis) {
         int slowQueryMillis = SystemProperties.getInt("db.log.slow_query_threshold", -1);
-        return slowQueryMillis >= 0 && elapsedTimeMillis > slowQueryMillis;
+        return slowQueryMillis >= 0 && elapsedTimeMillis >= slowQueryMillis;
     }
 
     /**
diff --git a/core/java/android/database/sqlite/SQLiteGlobal.java b/core/java/android/database/sqlite/SQLiteGlobal.java
index dbefd63..af0cf45 100644
--- a/core/java/android/database/sqlite/SQLiteGlobal.java
+++ b/core/java/android/database/sqlite/SQLiteGlobal.java
@@ -16,6 +16,7 @@
 
 package android.database.sqlite;
 
+import android.content.res.Resources;
 import android.os.StatFs;
 
 /**
@@ -64,4 +65,44 @@
             return sDefaultPageSize;
         }
     }
+
+    /**
+     * Gets the default journal mode when WAL is not in use.
+     */
+    public static String getDefaultJournalMode() {
+        return Resources.getSystem().getString(
+                com.android.internal.R.string.db_default_journal_mode);
+    }
+
+    /**
+     * Gets the journal size limit in bytes.
+     */
+    public static int getJournalSizeLimit() {
+        return Resources.getSystem().getInteger(
+                com.android.internal.R.integer.db_journal_size_limit);
+    }
+
+    /**
+     * Gets the database synchronization mode.
+     */
+    public static String getSyncMode() {
+        return Resources.getSystem().getString(
+                com.android.internal.R.string.db_sync_mode);
+    }
+
+    /**
+     * Gets the WAL auto-checkpoint integer in database pages.
+     */
+    public static int getWALAutoCheckpoint() {
+        return Math.max(1, Resources.getSystem().getInteger(
+                com.android.internal.R.integer.db_wal_autocheckpoint));
+    }
+
+    /**
+     * Gets the default connection pool size when in WAL mode.
+     */
+    public static int getWALConnectionPoolSize() {
+        return Math.max(2, Resources.getSystem().getInteger(
+                com.android.internal.R.integer.db_connection_pool_size));
+    }
 }
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 46d9369..ffa4663 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -43,13 +43,21 @@
 public abstract class SQLiteOpenHelper {
     private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
 
+    // When true, getReadableDatabase returns a read-only database if it is just being opened.
+    // The database handle is reopened in read/write mode when getWritableDatabase is called.
+    // We leave this behavior disabled in production because it is inefficient and breaks
+    // many applications.  For debugging purposes it can be useful to turn on strict
+    // read-only semantics to catch applications that call getReadableDatabase when they really
+    // wanted getWritableDatabase.
+    private static final boolean DEBUG_STRICT_READONLY = false;
+
     private final Context mContext;
     private final String mName;
     private final CursorFactory mFactory;
     private final int mNewVersion;
 
-    private SQLiteDatabase mDatabase = null;
-    private boolean mIsInitializing = false;
+    private SQLiteDatabase mDatabase;
+    private boolean mIsInitializing;
     private final DatabaseErrorHandler mErrorHandler;
 
     /**
@@ -127,76 +135,9 @@
      * @throws SQLiteException if the database cannot be opened for writing
      * @return a read/write database object valid until {@link #close} is called
      */
-    public synchronized SQLiteDatabase getWritableDatabase() {
-        if (mDatabase != null) {
-            if (!mDatabase.isOpen()) {
-                // darn! the user closed the database by calling mDatabase.close()
-                mDatabase = null;
-            } else if (!mDatabase.isReadOnly()) {
-                return mDatabase;  // The database is already open for business
-            }
-        }
-
-        if (mIsInitializing) {
-            throw new IllegalStateException("getWritableDatabase called recursively");
-        }
-
-        // If we have a read-only database open, someone could be using it
-        // (though they shouldn't), which would cause a lock to be held on
-        // the file, and our attempts to open the database read-write would
-        // fail waiting for the file lock.  To prevent that, we acquire a lock
-        // on the read-only database, which shuts out other users.
-
-        boolean success = false;
-        SQLiteDatabase db = null;
-        if (mDatabase != null) {
-            mDatabase.lockPrimaryConnection();
-        }
-        try {
-            mIsInitializing = true;
-            if (mName == null) {
-                db = SQLiteDatabase.create(null);
-            } else {
-                db = mContext.openOrCreateDatabase(mName, 0, mFactory, mErrorHandler);
-            }
-
-            int version = db.getVersion();
-            if (version != mNewVersion) {
-                db.beginTransaction();
-                try {
-                    if (version == 0) {
-                        onCreate(db);
-                    } else {
-                        if (version > mNewVersion) {
-                            onDowngrade(db, version, mNewVersion);
-                        } else {
-                            onUpgrade(db, version, mNewVersion);
-                        }
-                    }
-                    db.setVersion(mNewVersion);
-                    db.setTransactionSuccessful();
-                } finally {
-                    db.endTransaction();
-                }
-            }
-
-            onOpen(db);
-            success = true;
-            return db;
-        } finally {
-            mIsInitializing = false;
-            if (success) {
-                if (mDatabase != null) {
-                    try { mDatabase.close(); } catch (Exception e) { }
-                    mDatabase.unlockPrimaryConnection();
-                }
-                mDatabase = db;
-            } else {
-                if (mDatabase != null) {
-                    mDatabase.unlockPrimaryConnection();
-                }
-                if (db != null) db.close();
-            }
+    public SQLiteDatabase getWritableDatabase() {
+        synchronized (this) {
+            return getDatabaseLocked(true);
         }
     }
 
@@ -218,45 +159,95 @@
      * @return a database object valid until {@link #getWritableDatabase}
      *     or {@link #close} is called.
      */
-    public synchronized SQLiteDatabase getReadableDatabase() {
+    public SQLiteDatabase getReadableDatabase() {
+        synchronized (this) {
+            return getDatabaseLocked(false);
+        }
+    }
+
+    private SQLiteDatabase getDatabaseLocked(boolean writable) {
         if (mDatabase != null) {
             if (!mDatabase.isOpen()) {
-                // darn! the user closed the database by calling mDatabase.close()
+                // Darn!  The user closed the database by calling mDatabase.close().
                 mDatabase = null;
-            } else {
-                return mDatabase;  // The database is already open for business
+            } else if (!writable || !mDatabase.isReadOnly()) {
+                // The database is already open for business.
+                return mDatabase;
             }
         }
 
         if (mIsInitializing) {
-            throw new IllegalStateException("getReadableDatabase called recursively");
+            throw new IllegalStateException("getDatabase called recursively");
         }
 
-        try {
-            return getWritableDatabase();
-        } catch (SQLiteException e) {
-            if (mName == null) throw e;  // Can't open a temp database read-only!
-            Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e);
-        }
-
-        SQLiteDatabase db = null;
+        SQLiteDatabase db = mDatabase;
         try {
             mIsInitializing = true;
-            String path = mContext.getDatabasePath(mName).getPath();
-            db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY,
-                    mErrorHandler);
-            if (db.getVersion() != mNewVersion) {
-                throw new SQLiteException("Can't upgrade read-only database from version " +
-                        db.getVersion() + " to " + mNewVersion + ": " + path);
+
+            if (db != null) {
+                if (writable && db.isReadOnly()) {
+                    db.reopenReadWrite();
+                }
+            } else if (mName == null) {
+                db = SQLiteDatabase.create(null);
+            } else {
+                try {
+                    if (DEBUG_STRICT_READONLY && !writable) {
+                        final String path = mContext.getDatabasePath(mName).getPath();
+                        db = SQLiteDatabase.openDatabase(path, mFactory,
+                                SQLiteDatabase.OPEN_READONLY, mErrorHandler);
+                    } else {
+                        db = mContext.openOrCreateDatabase(mName, 0, mFactory, mErrorHandler);
+                    }
+                } catch (SQLiteException ex) {
+                    if (writable) {
+                        throw ex;
+                    }
+                    Log.e(TAG, "Couldn't open " + mName
+                            + " for writing (will try read-only):", ex);
+                    final String path = mContext.getDatabasePath(mName).getPath();
+                    db = SQLiteDatabase.openDatabase(path, mFactory,
+                            SQLiteDatabase.OPEN_READONLY, mErrorHandler);
+                }
             }
 
+            final int version = db.getVersion();
+            if (version != mNewVersion) {
+                if (db.isReadOnly()) {
+                    throw new SQLiteException("Can't upgrade read-only database from version " +
+                            db.getVersion() + " to " + mNewVersion + ": " + mName);
+                }
+
+                db.beginTransaction();
+                try {
+                    if (version == 0) {
+                        onCreate(db);
+                    } else {
+                        if (version > mNewVersion) {
+                            onDowngrade(db, version, mNewVersion);
+                        } else {
+                            onUpgrade(db, version, mNewVersion);
+                        }
+                    }
+                    db.setVersion(mNewVersion);
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                }
+            }
             onOpen(db);
-            Log.w(TAG, "Opened " + mName + " in read-only mode");
+
+            if (db.isReadOnly()) {
+                Log.w(TAG, "Opened " + mName + " in read-only mode");
+            }
+
             mDatabase = db;
-            return mDatabase;
+            return db;
         } finally {
             mIsInitializing = false;
-            if (db != null && db != mDatabase) db.close();
+            if (db != null && db != mDatabase) {
+                db.close();
+            }
         }
     }
 
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 2f43cb8..0bc6b58 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -77,7 +77,9 @@
         /** Attempt to connect failed. */
         FAILED,
         /** Access to this network is blocked. */
-        BLOCKED
+        BLOCKED,
+        /** Link has poor connectivity. */
+        VERIFYING_POOR_LINK
     }
 
     /**
@@ -94,6 +96,7 @@
         stateMap.put(DetailedState.CONNECTING, State.CONNECTING);
         stateMap.put(DetailedState.AUTHENTICATING, State.CONNECTING);
         stateMap.put(DetailedState.OBTAINING_IPADDR, State.CONNECTING);
+        stateMap.put(DetailedState.VERIFYING_POOR_LINK, State.CONNECTING);
         stateMap.put(DetailedState.CONNECTED, State.CONNECTED);
         stateMap.put(DetailedState.SUSPENDED, State.SUSPENDED);
         stateMap.put(DetailedState.DISCONNECTING, State.DISCONNECTING);
diff --git a/core/java/android/net/arp/ArpPeer.java b/core/java/android/net/arp/ArpPeer.java
new file mode 100644
index 0000000..8e666bc
--- /dev/null
+++ b/core/java/android/net/arp/ArpPeer.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.arp;
+
+import android.os.SystemClock;
+import android.util.Log;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+import libcore.net.RawSocket;
+
+/**
+ * This class allows simple ARP exchanges over an uninitialized network
+ * interface.
+ *
+ * @hide
+ */
+public class ArpPeer {
+    private String mInterfaceName;
+    private final InetAddress mMyAddr;
+    private final byte[] mMyMac = new byte[6];
+    private final InetAddress mPeer;
+    private final RawSocket mSocket;
+    private final byte[] L2_BROADCAST;  // TODO: refactor from DhcpClient.java
+    private static final int MAX_LENGTH = 1500; // refactor from DhcpPacket.java
+    private static final int ETHERNET_TYPE = 1;
+    private static final int ARP_LENGTH = 28;
+    private static final int MAC_ADDR_LENGTH = 6;
+    private static final int IPV4_LENGTH = 4;
+    private static final String TAG = "ArpPeer";
+
+    public ArpPeer(String interfaceName, InetAddress myAddr, String mac,
+                   InetAddress peer) throws SocketException {
+        mInterfaceName = interfaceName;
+        mMyAddr = myAddr;
+
+        for (int i = 0; i < MAC_ADDR_LENGTH; i++) {
+            mMyMac[i] = (byte) Integer.parseInt(mac.substring(
+                        i*3, (i*3) + 2), 16);
+        }
+
+        if (myAddr instanceof Inet6Address || peer instanceof Inet6Address) {
+            throw new IllegalArgumentException("IPv6 unsupported");
+        }
+
+        mPeer = peer;
+        L2_BROADCAST = new byte[MAC_ADDR_LENGTH];
+        Arrays.fill(L2_BROADCAST, (byte) 0xFF);
+
+        mSocket = new RawSocket(mInterfaceName, RawSocket.ETH_P_ARP);
+    }
+
+    /**
+     * Returns the MAC address (or null if timeout) for the requested
+     * peer.
+     */
+    public byte[] doArp(int timeoutMillis) {
+        ByteBuffer buf = ByteBuffer.allocate(MAX_LENGTH);
+        byte[] desiredIp = mPeer.getAddress();
+        long timeout = SystemClock.elapsedRealtime() + timeoutMillis;
+
+        // construct ARP request packet, using a ByteBuffer as a
+        // convenient container
+        buf.clear();
+        buf.order(ByteOrder.BIG_ENDIAN);
+
+        buf.putShort((short) ETHERNET_TYPE); // Ethernet type, 16 bits
+        buf.putShort(RawSocket.ETH_P_IP); // Protocol type IP, 16 bits
+        buf.put((byte)MAC_ADDR_LENGTH);  // MAC address length, 6 bytes
+        buf.put((byte)IPV4_LENGTH);  // IPv4 protocol size
+        buf.putShort((short) 1); // ARP opcode 1: 'request'
+        buf.put(mMyMac);        // six bytes: sender MAC
+        buf.put(mMyAddr.getAddress());  // four bytes: sender IP address
+        buf.put(new byte[MAC_ADDR_LENGTH]); // target MAC address: unknown
+        buf.put(desiredIp); // target IP address, 4 bytes
+        buf.flip();
+        mSocket.write(L2_BROADCAST, buf.array(), 0, buf.limit());
+
+        byte[] recvBuf = new byte[MAX_LENGTH];
+
+        while (SystemClock.elapsedRealtime() < timeout) {
+            long duration = (long) timeout - SystemClock.elapsedRealtime();
+            int readLen = mSocket.read(recvBuf, 0, recvBuf.length, -1,
+                (int) duration);
+
+            // Verify packet details. see RFC 826
+            if ((readLen >= ARP_LENGTH) // trailing bytes at times
+                && (recvBuf[0] == 0) && (recvBuf[1] == ETHERNET_TYPE) // type Ethernet
+                && (recvBuf[2] == 8) && (recvBuf[3] == 0) // protocol IP
+                && (recvBuf[4] == MAC_ADDR_LENGTH) // mac length
+                && (recvBuf[5] == IPV4_LENGTH) // IPv4 protocol size
+                && (recvBuf[6] == 0) && (recvBuf[7] == 2) // ARP reply
+                // verify desired IP address
+                && (recvBuf[14] == desiredIp[0]) && (recvBuf[15] == desiredIp[1])
+                && (recvBuf[16] == desiredIp[2]) && (recvBuf[17] == desiredIp[3]))
+            {
+                // looks good.  copy out the MAC
+                byte[] result = new byte[MAC_ADDR_LENGTH];
+                System.arraycopy(recvBuf, 8, result, 0, MAC_ADDR_LENGTH);
+                return result;
+            }
+        }
+
+        return null;
+    }
+
+    public void close() {
+        try {
+            mSocket.close();
+        } catch (IOException ex) {
+        }
+    }
+}
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index fd6bed7..97ed235 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -281,9 +281,6 @@
                             e.getCause());
                 } catch (CancellationException e) {
                     postResultIfNotInvoked(null);
-                } catch (Throwable t) {
-                    throw new RuntimeException("An error occured while executing "
-                            + "doInBackground()", t);
                 }
             }
         };
diff --git a/core/java/android/os/IUpdateLock.aidl b/core/java/android/os/IUpdateLock.aidl
new file mode 100644
index 0000000..4492fb8
--- /dev/null
+++ b/core/java/android/os/IUpdateLock.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2012 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.os;
+
+/**
+ * Direct interface to the UpdateLockService's functionality
+ *
+ * {@hide}
+ */
+interface IUpdateLock {
+    void acquireUpdateLock(IBinder token, String tag);
+    void releaseUpdateLock(IBinder token);
+}
diff --git a/core/java/android/os/TokenWatcher.java b/core/java/android/os/TokenWatcher.java
index ac3cc92..9b3a2d6 100755
--- a/core/java/android/os/TokenWatcher.java
+++ b/core/java/android/os/TokenWatcher.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.WeakHashMap;
 import java.util.Set;
 import android.util.Log;
@@ -115,15 +117,31 @@
 
     public void dump()
     {
+        ArrayList<String> a = dumpInternal();
+        for (String s : a) {
+            Log.i(mTag, s);
+        }
+    }
+
+    public void dump(PrintWriter pw) {
+        ArrayList<String> a = dumpInternal();
+        for (String s : a) {
+            pw.println(s);
+        }
+    }
+
+    private ArrayList<String> dumpInternal() {
+        ArrayList<String> a = new ArrayList<String>();
         synchronized (mTokens) {
             Set<IBinder> keys = mTokens.keySet();
-            Log.i(mTag, "Token count: " + mTokens.size());
+            a.add("Token count: " + mTokens.size());
             int i = 0;
             for (IBinder b: keys) {
-                Log.i(mTag, "[" + i + "] " + mTokens.get(b).tag + " - " + b);
+                a.add("[" + i + "] " + mTokens.get(b).tag + " - " + b);
                 i++;
             }
         }
+        return a;
     }
 
     private Runnable mNotificationTask = new Runnable() {
diff --git a/core/java/android/os/UpdateLock.java b/core/java/android/os/UpdateLock.java
new file mode 100644
index 0000000..4060326
--- /dev/null
+++ b/core/java/android/os/UpdateLock.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2012 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.os;
+
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * Advisory wakelock-like mechanism by which processes that should not be interrupted for
+ * OTA/update purposes can so advise the OS.  This is particularly relevant for headless
+ * or kiosk-like operation.
+ *
+ * @hide
+ */
+public class UpdateLock {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "UpdateLock";
+
+    private static IUpdateLock sService;
+    private static void checkService() {
+        if (sService == null) {
+            sService = IUpdateLock.Stub.asInterface(
+                    ServiceManager.getService(Context.UPDATE_LOCK_SERVICE));
+        }
+    }
+
+    IBinder mToken;
+    int mCount = 0;
+    boolean mRefCounted = true;
+    boolean mHeld = false;
+    final String mTag;
+
+    /**
+     * Broadcast Intent action sent when the global update lock state changes,
+     * i.e. when the first locker acquires an update lock, or when the last
+     * locker releases theirs.  The broadcast is sticky but is sent only to
+     * registered receivers.
+     */
+    public static final String UPDATE_LOCK_CHANGED = "android.os.UpdateLock.UPDATE_LOCK_CHANGED";
+
+    /**
+     * Boolean Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, indicating
+     * whether now is an appropriate time to interrupt device activity with an
+     * update operation.  True means that updates are okay right now; false indicates
+     * that perhaps later would be a better time.
+     */
+    public static final String NOW_IS_CONVENIENT = "nowisconvenient";
+
+    /**
+     * Long Intent extra on the UPDATE_LOCK_CHANGED sticky broadcast, marking the
+     * wall-clock time [in UTC] at which the broadcast was sent.  Note that this is
+     * in the System.currentTimeMillis() time base, which may be non-monotonic especially
+     * around reboots.
+     */
+    public static final String TIMESTAMP = "timestamp";
+
+    /**
+     * Construct an UpdateLock instance.
+     * @param tag An arbitrary string used to identify this lock instance in dump output.
+     */
+    public UpdateLock(String tag) {
+        mTag = tag;
+        mToken = new Binder();
+    }
+
+    /**
+     * Change the refcount behavior of this update lock.
+     */
+    public void setReferenceCounted(boolean isRefCounted) {
+        if (DEBUG) {
+            Log.v(TAG, "setting refcounted=" + isRefCounted + " : " + this);
+        }
+        mRefCounted = isRefCounted;
+    }
+
+    /**
+     * Is this lock currently held?
+     */
+    public boolean isHeld() {
+        synchronized (mToken) {
+            return mHeld;
+        }
+    }
+
+    /**
+     * Acquire an update lock.
+     */
+    public void acquire() {
+        if (DEBUG) {
+            Log.v(TAG, "acquire() : " + this, new RuntimeException("here"));
+        }
+        checkService();
+        synchronized (mToken) {
+            acquireLocked();
+        }
+    }
+
+    private void acquireLocked() {
+        if (!mRefCounted || mCount++ == 0) {
+            if (sService != null) {
+                try {
+                    sService.acquireUpdateLock(mToken, mTag);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to contact service to acquire");
+                }
+            }
+            mHeld = true;
+        }
+    }
+
+    /**
+     * Release this update lock.
+     */
+    public void release() {
+        if (DEBUG) Log.v(TAG, "release() : " + this, new RuntimeException("here"));
+        checkService();
+        synchronized (mToken) {
+            releaseLocked();
+        }
+    }
+
+    private void releaseLocked() {
+        if (!mRefCounted || --mCount == 0) {
+            if (sService != null) {
+                try {
+                    sService.releaseUpdateLock(mToken);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to contact service to release");
+                }
+            }
+            mHeld = false;
+        }
+        if (mCount < 0) {
+            throw new RuntimeException("UpdateLock under-locked");
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        synchronized (mToken) {
+            // if mHeld is true, sService must be non-null
+            if (mHeld) {
+                Log.wtf(TAG, "UpdateLock finalized while still held");
+                try {
+                    sService.releaseUpdateLock(mToken);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to contact service to release");
+                }
+            }
+        }
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0aad64a..b42417a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3125,15 +3125,8 @@
          * ms delay before rechecking an 'online' wifi connection when it is thought to be unstable.
          * @hide
          */
-        public static final String WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS =
-                "wifi_watchdog_dns_check_short_interval_ms";
-
-        /**
-         * ms delay before rechecking an 'online' wifi connection when it is thought to be stable.
-         * @hide
-         */
-        public static final String WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS =
-                "wifi_watchdog_dns_check_long_interval_ms";
+        public static final String WIFI_WATCHDOG_ARP_CHECK_INTERVAL_MS =
+                "wifi_watchdog_arp_interval_ms";
 
         /**
          * ms delay before rechecking a connect SSID for walled garden with a http download.
@@ -3143,44 +3136,28 @@
                 "wifi_watchdog_walled_garden_interval_ms";
 
         /**
-         * max blacklist calls on an SSID before full dns check failures disable the network.
+         * Number of ARP pings per check.
          * @hide
          */
-        public static final String WIFI_WATCHDOG_MAX_SSID_BLACKLISTS =
-                "wifi_watchdog_max_ssid_blacklists";
+        public static final String WIFI_WATCHDOG_NUM_ARP_PINGS = "wifi_watchdog_num_arp_pings";
 
         /**
-         * Number of dns pings per check.
+         * Minimum number of responses to the arp pings to consider the test 'successful'.
          * @hide
          */
-        public static final String WIFI_WATCHDOG_NUM_DNS_PINGS = "wifi_watchdog_num_dns_pings";
+        public static final String WIFI_WATCHDOG_MIN_ARP_RESPONSES =
+                "wifi_watchdog_min_arp_responses";
 
         /**
-         * Minimum number of responses to the dns pings to consider the test 'successful'.
+         * Timeout on ARP pings
          * @hide
          */
-        public static final String WIFI_WATCHDOG_MIN_DNS_RESPONSES =
-                "wifi_watchdog_min_dns_responses";
+        public static final String WIFI_WATCHDOG_ARP_PING_TIMEOUT_MS =
+                "wifi_watchdog_arp_ping_timeout_ms";
 
         /**
-         * Timeout on dns pings
-         * @hide
-         */
-        public static final String WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS =
-                "wifi_watchdog_dns_ping_timeout_ms";
-
-        /**
-         * We consider action from a 'blacklist' call to have finished by the end of
-         * this interval.  If we are connected to the same AP with no network connection,
-         * we are likely stuck on an SSID with no external connectivity.
-         * @hide
-         */
-        public static final String WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS =
-                "wifi_watchdog_blacklist_followup_interval_ms";
-
-        /**
-         * Setting to turn off poor network avoidance on Wi-Fi. Feature is disabled by default and
-         * the setting needs to be set to 1 to enable it.
+         * Setting to turn off poor network avoidance on Wi-Fi. Feature is enabled by default and
+         * the setting needs to be set to 0 to disable it.
          * @hide
          */
         public static final String WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED =
@@ -3204,14 +3181,6 @@
                 "wifi_watchdog_walled_garden_url";
 
         /**
-         * Boolean to determine whether to notify on disabling a network.  Secure setting used
-         * to notify user only once.
-         * @hide
-         */
-        public static final String WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP =
-                "wifi_watchdog_show_disabled_network_popup";
-
-        /**
          * The maximum number of times we will retry a connection to an access
          * point for which we have failed in acquiring an IP address from DHCP.
          * A value of N means that we will make N+1 connection attempts in all.
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index ca7263c..8c97293 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import com.android.internal.util.ArrayUtils;
 import org.ccil.cowan.tagsoup.HTMLSchema;
 import org.ccil.cowan.tagsoup.Parser;
 import org.xml.sax.Attributes;
@@ -45,13 +46,11 @@
 import android.text.style.TypefaceSpan;
 import android.text.style.URLSpan;
 import android.text.style.UnderlineSpan;
-import android.util.Log;
 
 import com.android.internal.util.XmlUtils;
 
 import java.io.IOException;
 import java.io.StringReader;
-import java.nio.CharBuffer;
 import java.util.HashMap;
 
 /**
@@ -203,9 +202,26 @@
         }
     }
 
+    private static String getOpenParaTagWithDirection(Spanned text, int start, int end) {
+        final int len = end - start;
+        final byte[] levels = new byte[ArrayUtils.idealByteArraySize(len)];
+        final char[] buffer = TextUtils.obtain(len);
+        TextUtils.getChars(text, start, end, buffer, 0);
+
+        int paraDir = AndroidBidi.bidi(Layout.DIR_REQUEST_DEFAULT_LTR, buffer, levels, len,
+                false /* no info */);
+        switch(paraDir) {
+            case Layout.DIR_RIGHT_TO_LEFT:
+                return "<p dir=rtl>";
+            case Layout.DIR_LEFT_TO_RIGHT:
+            default:
+                return "<p dir=ltr>";
+        }
+    }
+
     private static void withinBlockquote(StringBuilder out, Spanned text,
                                          int start, int end) {
-        out.append("<p>");
+        out.append(getOpenParaTagWithDirection(text, start, end));
 
         int next;
         for (int i = start; i < end; i = next) {
@@ -340,7 +356,7 @@
             }
         }
 
-        String p = last ? "" : "</p>\n<p>";
+        String p = last ? "" : "</p>\n" + getOpenParaTagWithDirection(text, start, end);
 
         if (nl == 1) {
             out.append("<br>\n");
@@ -350,7 +366,6 @@
             for (int i = 2; i < nl; i++) {
                 out.append("<br>");
             }
-
             out.append(p);
         }
     }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 1dd4c8a..299e115 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -716,7 +716,8 @@
             boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
             boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount);
 
-            boolean doEllipsis = (firstLine && !moreChars &&
+            boolean doEllipsis =
+                        (((mMaximumVisibleLineCount == 1 && moreChars) || (firstLine && !moreChars)) &&
                                 ellipsize != TextUtils.TruncateAt.MARQUEE) ||
                         (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
                                 ellipsize == TextUtils.TruncateAt.END);
diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java
index ae41eab..6ca6161 100644
--- a/core/java/android/text/TextDirectionHeuristics.java
+++ b/core/java/android/text/TextDirectionHeuristics.java
@@ -18,6 +18,7 @@
 
 
 import android.util.LocaleUtil;
+import android.view.View;
 
 /**
  * Some objects that implement TextDirectionHeuristic.
@@ -240,7 +241,7 @@
         @Override
         protected boolean defaultIsRtl() {
             final int dir = LocaleUtil.getLayoutDirectionFromLocale(java.util.Locale.getDefault());
-            return (dir == LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE);
+            return (dir == View.LAYOUT_DIRECTION_RTL);
         }
 
         public static final TextDirectionHeuristicLocale INSTANCE =
diff --git a/core/java/android/util/LocaleUtil.java b/core/java/android/util/LocaleUtil.java
index 4d773f6..9953252 100644
--- a/core/java/android/util/LocaleUtil.java
+++ b/core/java/android/util/LocaleUtil.java
@@ -18,6 +18,7 @@
 
 import java.util.Locale;
 
+import android.view.View;
 import libcore.icu.ICU;
 
 /**
@@ -29,16 +30,6 @@
 
     private LocaleUtil() { /* cannot be instantiated */ }
 
-    /**
-     * @hide Do not use. Implementation not finished.
-     */
-    public static final int TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE = 0;
-
-    /**
-     * @hide Do not use. Implementation not finished.
-     */
-    public static final int TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE = 1;
-
     private static String ARAB_SCRIPT_SUBTAG = "Arab";
     private static String HEBR_SCRIPT_SUBTAG = "Hebr";
 
@@ -47,8 +38,8 @@
      *
      * @param locale the Locale for which we want the layout direction. Can be null.
      * @return the layout direction. This may be one of:
-     * {@link #TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE} or
-     * {@link #TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE}.
+     * {@link View#LAYOUT_DIRECTION_LTR} or
+     * {@link View#LAYOUT_DIRECTION_RTL}.
      *
      * Be careful: this code will need to be changed when vertical scripts will be supported
      *
@@ -61,11 +52,11 @@
 
             if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) ||
                     scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) {
-                return TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE;
+                return View.LAYOUT_DIRECTION_RTL;
             }
         }
 
-        return TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
+        return View.LAYOUT_DIRECTION_LTR;
     }
 
     /**
@@ -75,8 +66,8 @@
      *
      * @param locale
      * @return the layout direction. This may be one of:
-     * {@link #TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE} or
-     * {@link #TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE}.
+     * {@link View#LAYOUT_DIRECTION_LTR} or
+     * {@link View#LAYOUT_DIRECTION_RTL}.
      *
      * Be careful: this code will need to be changed when vertical scripts will be supported
      *
@@ -86,11 +77,11 @@
         switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
             case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
             case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
-                return TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE;
+                return View.LAYOUT_DIRECTION_RTL;
 
             case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
             default:
-                return TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
+                return View.LAYOUT_DIRECTION_LTR;
         }
     }
 }
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 93299eb..2883eca 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -25,6 +25,8 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.TimeZone;
 import java.util.Date;
 
@@ -35,18 +37,26 @@
  */
 public class TimeUtils {
     /** @hide */ public TimeUtils() {}
+    private static final boolean DBG = false;
     private static final String TAG = "TimeUtils";
 
+    /** Cached results of getTineZones */
+    private static final Object sLastLockObj = new Object();
+    private static ArrayList<TimeZone> sLastZones = null;
+    private static String sLastCountry = null;
+
+    /** Cached results of getTimeZonesWithUniqueOffsets */
+    private static final Object sLastUniqueLockObj = new Object();
+    private static ArrayList<TimeZone> sLastUniqueZoneOffsets = null;
+    private static String sLastUniqueCountry = null;
+
+
     /**
      * Tries to return a time zone that would have had the specified offset
      * and DST value at the specified moment in the specified country.
      * Returns null if no suitable zone could be found.
      */
     public static TimeZone getTimeZone(int offset, boolean dst, long when, String country) {
-        if (country == null) {
-            return null;
-        }
-
         TimeZone best = null;
 
         Resources r = Resources.getSystem();
@@ -58,6 +68,107 @@
         int currentOffset = current.getOffset(when);
         boolean currentDst = current.inDaylightTime(d);
 
+        for (TimeZone tz : getTimeZones(country)) {
+            // If the current time zone is from the right country
+            // and meets the other known properties, keep it
+            // instead of changing to another one.
+
+            if (tz.getID().equals(currentName)) {
+                if (currentOffset == offset && currentDst == dst) {
+                    return current;
+                }
+            }
+
+            // Otherwise, take the first zone from the right
+            // country that has the correct current offset and DST.
+            // (Keep iterating instead of returning in case we
+            // haven't encountered the current time zone yet.)
+
+            if (best == null) {
+                if (tz.getOffset(when) == offset &&
+                    tz.inDaylightTime(d) == dst) {
+                    best = tz;
+                }
+            }
+        }
+
+        return best;
+    }
+
+    /**
+     * Return list of unique time zones for the country. Do not modify
+     *
+     * @param country to find
+     * @return list of unique time zones, maybe empty but never null. Do not modify.
+     * @hide
+     */
+    public static ArrayList<TimeZone> getTimeZonesWithUniqueOffsets(String country) {
+        synchronized(sLastUniqueLockObj) {
+            if ((country != null) && country.equals(sLastUniqueCountry)) {
+                if (DBG) {
+                    Log.d(TAG, "getTimeZonesWithUniqueOffsets(" +
+                            country + "): return cached version");
+                }
+                return sLastUniqueZoneOffsets;
+            }
+        }
+
+        Collection<TimeZone> zones = getTimeZones(country);
+        ArrayList<TimeZone> uniqueTimeZones = new ArrayList<TimeZone>();
+        for (TimeZone zone : zones) {
+            // See if we already have this offset,
+            // Using slow but space efficient and these are small.
+            boolean found = false;
+            for (int i = 0; i < uniqueTimeZones.size(); i++) {
+                if (uniqueTimeZones.get(i).getRawOffset() == zone.getRawOffset()) {
+                    found = true;
+                    break;
+                }
+            }
+            if (found == false) {
+                if (DBG) {
+                    Log.d(TAG, "getTimeZonesWithUniqueOffsets: add unique offset=" +
+                            zone.getRawOffset() + " zone.getID=" + zone.getID());
+                }
+                uniqueTimeZones.add(zone);
+            }
+        }
+
+        synchronized(sLastUniqueLockObj) {
+            // Cache the last result
+            sLastUniqueZoneOffsets = uniqueTimeZones;
+            sLastUniqueCountry = country;
+
+            return sLastUniqueZoneOffsets;
+        }
+    }
+
+    /**
+     * Returns the time zones for the country, which is the code
+     * attribute of the timezone element in time_zones_by_country.xml. Do not modify.
+     *
+     * @param country is a two character country code.
+     * @return TimeZone list, maybe empty but never null. Do not modify.
+     * @hide
+     */
+    public static ArrayList<TimeZone> getTimeZones(String country) {
+        synchronized (sLastLockObj) {
+            if ((country != null) && country.equals(sLastCountry)) {
+                if (DBG) Log.d(TAG, "getTimeZones(" + country + "): return cached version");
+                return sLastZones;
+            }
+        }
+
+        ArrayList<TimeZone> tzs = new ArrayList<TimeZone>();
+
+        if (country == null) {
+            if (DBG) Log.d(TAG, "getTimeZones(null): return empty list");
+            return tzs;
+        }
+
+        Resources r = Resources.getSystem();
+        XmlResourceParser parser = r.getXml(com.android.internal.R.xml.time_zones_by_country);
+
         try {
             XmlUtils.beginDocument(parser, "timezones");
 
@@ -73,43 +184,33 @@
 
                 if (country.equals(code)) {
                     if (parser.next() == XmlPullParser.TEXT) {
-                        String maybe = parser.getText();
-
-                        // If the current time zone is from the right country
-                        // and meets the other known properties, keep it
-                        // instead of changing to another one.
-
-                        if (maybe.equals(currentName)) {
-                            if (currentOffset == offset && currentDst == dst) {
-                                return current;
-                            }
-                        }
-
-                        // Otherwise, take the first zone from the right
-                        // country that has the correct current offset and DST.
-                        // (Keep iterating instead of returning in case we
-                        // haven't encountered the current time zone yet.)
-
-                        if (best == null) {
-                            TimeZone tz = TimeZone.getTimeZone(maybe);
-
-                            if (tz.getOffset(when) == offset &&
-                                tz.inDaylightTime(d) == dst) {
-                                best = tz;
+                        String zoneIdString = parser.getText();
+                        TimeZone tz = TimeZone.getTimeZone(zoneIdString);
+                        if (tz.getID().startsWith("GMT") == false) {
+                            // tz.getID doesn't start not "GMT" so its valid
+                            tzs.add(tz);
+                            if (DBG) {
+                                Log.d(TAG, "getTimeZone('" + country + "'): found tz.getID=="
+                                    + ((tz != null) ? tz.getID() : "<no tz>"));
                             }
                         }
                     }
                 }
             }
         } catch (XmlPullParserException e) {
-            Log.e(TAG, "Got exception while getting preferred time zone.", e);
+            Log.e(TAG, "Got xml parser exception getTimeZone('" + country + "'): e=", e);
         } catch (IOException e) {
-            Log.e(TAG, "Got exception while getting preferred time zone.", e);
+            Log.e(TAG, "Got IO exception getTimeZone('" + country + "'): e=", e);
         } finally {
             parser.close();
         }
 
-        return best;
+        synchronized(sLastLockObj) {
+            // Cache the last result;
+            sLastZones = tzs;
+            sLastCountry = country;
+            return sLastZones;
+        }
     }
 
     /**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e6b41da..740ea12 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -40,7 +40,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
@@ -957,28 +956,24 @@
     /**
      * Horizontal direction of this view is from Left to Right.
      * Use with {@link #setLayoutDirection}.
-     * {@hide}
      */
     public static final int LAYOUT_DIRECTION_LTR = 0x00000000;
 
     /**
      * Horizontal direction of this view is from Right to Left.
      * Use with {@link #setLayoutDirection}.
-     * {@hide}
      */
     public static final int LAYOUT_DIRECTION_RTL = 0x40000000;
 
     /**
      * Horizontal direction of this view is inherited from its parent.
      * Use with {@link #setLayoutDirection}.
-     * {@hide}
      */
     public static final int LAYOUT_DIRECTION_INHERIT = 0x80000000;
 
     /**
      * Horizontal direction of this view is from deduced from the default language
      * script for the locale. Use with {@link #setLayoutDirection}.
-     * {@hide}
      */
     public static final int LAYOUT_DIRECTION_LOCALE = 0xC0000000;
 
@@ -4834,8 +4829,6 @@
      *   {@link #LAYOUT_DIRECTION_INHERIT} or
      *   {@link #LAYOUT_DIRECTION_LOCALE}.
      * @attr ref android.R.styleable#View_layoutDirection
-     *
-     * @hide
      */
     @ViewDebug.ExportedProperty(category = "layout", mapping = {
         @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR,     to = "LTR"),
@@ -4857,8 +4850,6 @@
      *   {@link #LAYOUT_DIRECTION_LOCALE}.
      *
      * @attr ref android.R.styleable#View_layoutDirection
-     *
-     * @hide
      */
     @RemotableViewMethod
     public void setLayoutDirection(int layoutDirection) {
@@ -4874,8 +4865,6 @@
      *
      * @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns
      * {@link #LAYOUT_DIRECTION_LTR} id the layout direction is not RTL.
-     *
-     * @hide
      */
     @ViewDebug.ExportedProperty(category = "layout", mapping = {
         @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR,     to = "RESOLVED_DIRECTION_LTR"),
@@ -4892,8 +4881,6 @@
      * layout attribute and/or the inherited value from the parent.</p>
      *
      * @return true if the layout is right-to-left.
-     *
-     * @hide
      */
     @ViewDebug.ExportedProperty(category = "layout")
     public boolean isLayoutRtl() {
@@ -9673,11 +9660,11 @@
     }
 
     /**
-     * Return true if layout direction resolution can be done
+     * Check if layout direction resolution can be done.
      *
-     * @hide
+     * @return true if layout direction resolution can be done otherwise return false.
      */
-    protected boolean canResolveLayoutDirection() {
+    public boolean canResolveLayoutDirection() {
         switch (getLayoutDirection()) {
             case LAYOUT_DIRECTION_INHERIT:
                 return (mParent != null);
@@ -9692,27 +9679,22 @@
      * Subclasses need to override this method to clear cached information that depends on the
      * resolved layout direction, or to inform child views that inherit their layout direction.
      * Overrides must also call the superclass implementation at the start of their implementation.
-     *
-     * @hide
      */
-    protected void resetResolvedLayoutDirection() {
-        // Reset the layout direction resolution
+    public void resetResolvedLayoutDirection() {
+        // Reset the current View resolution
         mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED;
         // Reset also the text direction
         resetResolvedTextDirection();
     }
 
     /**
-     * Check if a Locale is corresponding to a RTL script.
+     * Check if a Locale uses an RTL script.
      *
      * @param locale Locale to check
-     * @return true if a Locale is corresponding to a RTL script.
-     *
-     * @hide
+     * @return true if the Locale uses an RTL script.
      */
     protected static boolean isLayoutDirectionRtl(Locale locale) {
-        return (LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE ==
-                LocaleUtil.getLayoutDirectionFromLocale(locale));
+        return (LAYOUT_DIRECTION_RTL == LocaleUtil.getLayoutDirectionFromLocale(locale));
     }
 
     /**
@@ -11867,8 +11849,6 @@
     * Return the layout direction of a given Drawable.
     *
     * @param who the Drawable to query
-    *
-    * @hide
     */
     public int getResolvedLayoutDirection(Drawable who) {
         return (who == mBGDrawable) ? getResolvedLayoutDirection() : LAYOUT_DIRECTION_DEFAULT;
@@ -13061,7 +13041,7 @@
 
         if (mParent != null) {
             if (mLayoutParams != null) {
-                mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());
+                mLayoutParams.onResolveLayoutDirection(getResolvedLayoutDirection());
             }
             if (!mParent.isLayoutRequested()) {
                 mParent.requestLayout();
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 05c2b57..7d96a19 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1041,9 +1041,8 @@
 
     /**
      * {@inheritDoc}
-     *
-     * !!! TODO: write real docs
      */
+    // TODO: Write real docs
     @Override
     public boolean dispatchDragEvent(DragEvent event) {
         boolean retval = false;
@@ -4921,7 +4920,7 @@
     }
 
     @Override
-    protected void resetResolvedLayoutDirection() {
+    public void resetResolvedLayoutDirection() {
         super.resetResolvedLayoutDirection();
 
         // Take care of resetting the children resolution too
@@ -5124,10 +5123,8 @@
          *
          * {@link View#LAYOUT_DIRECTION_LTR}
          * {@link View#LAYOUT_DIRECTION_RTL}
-         *
-         * @hide
          */
-        protected void resolveWithDirection(int layoutDirection) {
+        public void onResolveLayoutDirection(int layoutDirection) {
         }
 
         /**
@@ -5378,12 +5375,10 @@
 
         /**
          * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
-         * maybe overriden depending on layout direction.
-         *
-         * @hide
+         * may be overridden depending on layout direction.
          */
         @Override
-        protected void resolveWithDirection(int layoutDirection) {
+        public void onResolveLayoutDirection(int layoutDirection) {
             switch(layoutDirection) {
                 case View.LAYOUT_DIRECTION_RTL:
                     leftMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : leftMargin;
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 6dbdedb..f56dd10 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -105,6 +105,7 @@
     private View[] mViews;
     private ViewRootImpl[] mRoots;
     private WindowManager.LayoutParams[] mParams;
+    private boolean mNeedsEglTerminate;
 
     private final static Object sLock = new Object();
     private final static WindowManagerImpl sWindowManager = new WindowManagerImpl();
@@ -447,8 +448,7 @@
                                 mRoots[i].terminateHardwareResources();
                             }
                         }
-                        // Terminate the hardware renderer to free all resources
-                        ManagedEGLContext.doTerminate();
+                        mNeedsEglTerminate = true;
                         break;
                     }
                     // high end gfx devices fall through to next case
@@ -461,6 +461,16 @@
     /**
      * @hide
      */
+    public void terminateEgl() {
+        if (mNeedsEglTerminate) {
+            ManagedEGLContext.doTerminate();
+            mNeedsEglTerminate = false;
+        }
+    }
+
+    /**
+     * @hide
+     */
     public void trimLocalMemory() {
         synchronized (this) {
             if (mViews == null) return;
diff --git a/core/java/android/view/textservice/SentenceSuggestionsInfo.java b/core/java/android/view/textservice/SentenceSuggestionsInfo.java
index 8d7c6cf..cb9e496 100644
--- a/core/java/android/view/textservice/SentenceSuggestionsInfo.java
+++ b/core/java/android/view/textservice/SentenceSuggestionsInfo.java
@@ -84,6 +84,13 @@
     /**
      * @hide
      */
+    public int getSuggestionsCount() {
+        return mSuggestionsInfos.length;
+    }
+
+    /**
+     * @hide
+     */
     public SuggestionsInfo getSuggestionsInfoAt(int i) {
         if (i >= 0 && i < mSuggestionsInfos.length) {
             return mSuggestionsInfos[i];
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 3491a537..9105f19 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -180,9 +180,9 @@
     /**
      * @hide
      */
-    public void getSentenceSuggestions(TextInfo textInfo, int suggestionsLimit) {
+    public void getSentenceSuggestions(TextInfo[] textInfo, int suggestionsLimit) {
         mSpellCheckerSessionListenerImpl.getSentenceSuggestionsMultiple(
-                new TextInfo[] {textInfo}, suggestionsLimit);
+                textInfo, suggestionsLimit);
     }
 
     /**
diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java
index 2d5b263..62e812e 100644
--- a/core/java/android/webkit/HTML5VideoInline.java
+++ b/core/java/android/webkit/HTML5VideoInline.java
@@ -92,6 +92,11 @@
 
     @Override
     public void deleteSurfaceTexture() {
+        cleanupSurfaceTexture();
+        return;
+    }
+
+    public static void cleanupSurfaceTexture() {
         mSurfaceTexture = null;
         mVideoLayerUsingSurfaceTexture = -1;
         return;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index af3bb4d..a850379 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -96,6 +96,7 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
+import android.webkit.HTML5VideoInline;
 import android.webkit.WebTextView.AutoCompleteAdapter;
 import android.webkit.WebViewCore.DrawData;
 import android.webkit.WebViewCore.EventHub;
@@ -842,6 +843,11 @@
             if (DebugFlags.WEB_VIEW) {
                 Log.d("WebView", "onTrimMemory: " + level);
             }
+            // When framework reset EGL context during high memory pressure, all
+            // the existing GL resources for the html5 video will be destroyed
+            // at native side.
+            // Here we just need to clean up the Surface Texture which is static.
+            HTML5VideoInline.cleanupSurfaceTexture();
             WebView.nativeOnTrimMemory(level);
         }
 
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index a1cf205..df2996c 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -57,9 +57,14 @@
     // Pause between each spell check to keep the UI smooth
     private final static int SPELL_PAUSE_DURATION = 400; // milliseconds
 
+    private static final int USE_SPAN_RANGE = -1;
+
     private final TextView mTextView;
 
     SpellCheckerSession mSpellCheckerSession;
+    // We assume that the sentence level spell check will always provide better results than words.
+    // Although word SC has a sequential option.
+    private boolean mIsSentenceSpellCheckSupported;
     final int mCookie;
 
     // Paired arrays for the (id, spellCheckSpan) pair. A negative id means the associated
@@ -111,6 +116,7 @@
                     null /* Bundle not currently used by the textServicesManager */,
                     mCurrentLocale, this,
                     false /* means any available languages from current spell checker */);
+            mIsSentenceSpellCheckSupported = mSpellCheckerSession.isSentenceSpellCheckSupported();
         }
 
         // Restore SpellCheckSpans in pool
@@ -272,46 +278,80 @@
                 textInfos = textInfosCopy;
             }
 
-            mSpellCheckerSession.getSuggestions(textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE,
-                    false /* TODO Set sequentialWords to true for initial spell check */);
+            if (mIsSentenceSpellCheckSupported) {
+                mSpellCheckerSession.getSentenceSuggestions(
+                        textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE);
+            } else {
+                mSpellCheckerSession.getSuggestions(textInfos, SuggestionSpan.SUGGESTIONS_MAX_SIZE,
+                        false /* TODO Set sequentialWords to true for initial spell check */);
+            }
         }
     }
 
-    @Override
-    public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) {
-        // TODO: Handle the position and length for each suggestion
-        // do nothing for now
+    private SpellCheckSpan onGetSuggestionsInternal(
+            SuggestionsInfo suggestionsInfo, int offset, int length) {
+        if (suggestionsInfo.getCookie() != mCookie) {
+            return null;
+        }
+        final Editable editable = (Editable) mTextView.getText();
+        final int sequenceNumber = suggestionsInfo.getSequence();
+        for (int k = 0; k < mLength; ++k) {
+            if (sequenceNumber == mIds[k]) {
+                final int attributes = suggestionsInfo.getSuggestionsAttributes();
+                final boolean isInDictionary =
+                        ((attributes & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) > 0);
+                final boolean looksLikeTypo =
+                        ((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0);
+
+                final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[k];
+                //TODO: we need to change that rule for results from a sentence-level spell
+                // checker that will probably be in dictionary.
+                if (!isInDictionary && looksLikeTypo) {
+                    createMisspelledSuggestionSpan(
+                            editable, suggestionsInfo, spellCheckSpan, offset, length);
+                }
+                return spellCheckSpan;
+            }
+        }
+        return null;
     }
 
     @Override
     public void onGetSuggestions(SuggestionsInfo[] results) {
-        Editable editable = (Editable) mTextView.getText();
-
-        for (int i = 0; i < results.length; i++) {
-            SuggestionsInfo suggestionsInfo = results[i];
-            if (suggestionsInfo.getCookie() != mCookie) continue;
-            final int sequenceNumber = suggestionsInfo.getSequence();
-
-            for (int j = 0; j < mLength; j++) {
-                if (sequenceNumber == mIds[j]) {
-                    final int attributes = suggestionsInfo.getSuggestionsAttributes();
-                    boolean isInDictionary =
-                            ((attributes & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) > 0);
-                    boolean looksLikeTypo =
-                            ((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0);
-
-                    SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j];
-
-                    if (!isInDictionary && looksLikeTypo) {
-                        createMisspelledSuggestionSpan(editable, suggestionsInfo, spellCheckSpan);
-                    }
-
-                    editable.removeSpan(spellCheckSpan);
-                    break;
-                }
+        final Editable editable = (Editable) mTextView.getText();
+        for (int i = 0; i < results.length; ++i) {
+            final SpellCheckSpan spellCheckSpan =
+                    onGetSuggestionsInternal(results[i], USE_SPAN_RANGE, USE_SPAN_RANGE);
+            if (spellCheckSpan != null) {
+                editable.removeSpan(spellCheckSpan);
             }
         }
+        scheduleNewSpellCheck();
+    }
 
+    @Override
+    public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) {
+        final Editable editable = (Editable) mTextView.getText();
+
+        for (int i = 0; i < results.length; ++i) {
+            final SentenceSuggestionsInfo ssi = results[i];
+            SpellCheckSpan spellCheckSpan = null;
+            for (int j = 0; j < ssi.getSuggestionsCount(); ++j) {
+                final SuggestionsInfo suggestionsInfo = ssi.getSuggestionsInfoAt(j);
+                final int offset = ssi.getOffsetAt(j);
+                final int length = ssi.getLengthAt(j);
+                final SpellCheckSpan scs = onGetSuggestionsInternal(
+                        suggestionsInfo, offset, length);
+                if (spellCheckSpan == null && scs != null) {
+                    // the spellCheckSpan is shared by all the "SuggestionsInfo"s in the same
+                    // SentenceSuggestionsInfo
+                    spellCheckSpan = scs;
+                }
+            }
+            if (spellCheckSpan != null) {
+                editable.removeSpan(spellCheckSpan);
+            }
+        }
         scheduleNewSpellCheck();
     }
 
@@ -338,10 +378,11 @@
     }
 
     private void createMisspelledSuggestionSpan(Editable editable, SuggestionsInfo suggestionsInfo,
-            SpellCheckSpan spellCheckSpan) {
-        final int start = editable.getSpanStart(spellCheckSpan);
-        final int end = editable.getSpanEnd(spellCheckSpan);
-        if (start < 0 || end <= start) return; // span was removed in the meantime
+            SpellCheckSpan spellCheckSpan, int offset, int length) {
+        final int spellCheckSpanStart = editable.getSpanStart(spellCheckSpan);
+        final int spellCheckSpanEnd = editable.getSpanEnd(spellCheckSpan);
+        if (spellCheckSpanStart < 0 || spellCheckSpanEnd <= spellCheckSpanStart)
+            return; // span was removed in the meantime
 
         final int suggestionsCount = suggestionsInfo.getSuggestionsCount();
         if (suggestionsCount <= 0) {
@@ -349,6 +390,16 @@
             return;
         }
 
+        final int start;
+        final int end;
+        if (offset != USE_SPAN_RANGE && length != USE_SPAN_RANGE) {
+            start = spellCheckSpanStart + offset;
+            end = start + length;
+        } else {
+            start = spellCheckSpanStart;
+            end = spellCheckSpanEnd;
+        }
+
         String[] suggestions = new String[suggestionsCount];
         for (int i = 0; i < suggestionsCount; i++) {
             suggestions[i] = suggestionsInfo.getSuggestionAt(i);
@@ -419,67 +470,97 @@
             int wordCount = 0;
             boolean scheduleOtherSpellCheck = false;
 
-            while (wordStart <= end) {
-                if (wordEnd >= start && wordEnd > wordStart) {
-                    if (wordCount >= MAX_NUMBER_OF_WORDS) {
-                        scheduleOtherSpellCheck = true;
+            if (mIsSentenceSpellCheckSupported) {
+                int regionEnd;
+                if (wordIteratorWindowEnd < end) {
+                    // Several batches needed on that region. Cut after last previous word
+                    regionEnd = mWordIterator.preceding(wordIteratorWindowEnd);
+                    scheduleOtherSpellCheck = true;
+                } else {
+                    regionEnd = mWordIterator.preceding(end);
+                }
+                boolean correct = regionEnd != BreakIterator.DONE;
+                if (correct) {
+                    regionEnd = mWordIterator.getEnd(regionEnd);
+                    correct = regionEnd != BreakIterator.DONE;
+                }
+                if (!correct) {
+                    editable.removeSpan(mRange);
+                    return;
+                }
+                wordStart = regionEnd;
+                // TODO: Find the start position of the sentence.
+                // Set span with the context
+                final int spellCheckStart =  Math.min(
+                        start, Math.max(wordStart, regionEnd - WORD_ITERATOR_INTERVAL));
+                if (regionEnd <= spellCheckStart) {
+                    return;
+                }
+                addSpellCheckSpan(editable, spellCheckStart, regionEnd);
+            } else {
+                while (wordStart <= end) {
+                    if (wordEnd >= start && wordEnd > wordStart) {
+                        if (wordCount >= MAX_NUMBER_OF_WORDS) {
+                            scheduleOtherSpellCheck = true;
+                            break;
+                        }
+                        // A new word has been created across the interval boundaries with this
+                        // edit. The previous spans (that ended on start / started on end) are
+                        // not valid anymore and must be removed.
+                        if (wordStart < start && wordEnd > start) {
+                            removeSpansAt(editable, start, spellCheckSpans);
+                            removeSpansAt(editable, start, suggestionSpans);
+                        }
+
+                        if (wordStart < end && wordEnd > end) {
+                            removeSpansAt(editable, end, spellCheckSpans);
+                            removeSpansAt(editable, end, suggestionSpans);
+                        }
+
+                        // Do not create new boundary spans if they already exist
+                        boolean createSpellCheckSpan = true;
+                        if (wordEnd == start) {
+                            for (int i = 0; i < spellCheckSpans.length; i++) {
+                                final int spanEnd = editable.getSpanEnd(spellCheckSpans[i]);
+                                if (spanEnd == start) {
+                                    createSpellCheckSpan = false;
+                                    break;
+                                }
+                            }
+                        }
+
+                        if (wordStart == end) {
+                            for (int i = 0; i < spellCheckSpans.length; i++) {
+                                final int spanStart = editable.getSpanStart(spellCheckSpans[i]);
+                                if (spanStart == end) {
+                                    createSpellCheckSpan = false;
+                                    break;
+                                }
+                            }
+                        }
+
+                        if (createSpellCheckSpan) {
+                            addSpellCheckSpan(editable, wordStart, wordEnd);
+                        }
+                        wordCount++;
+                    }
+
+                    // iterate word by word
+                    int originalWordEnd = wordEnd;
+                    wordEnd = mWordIterator.following(wordEnd);
+                    if ((wordIteratorWindowEnd < end) &&
+                            (wordEnd == BreakIterator.DONE || wordEnd >= wordIteratorWindowEnd)) {
+                        wordIteratorWindowEnd =
+                                Math.min(end, originalWordEnd + WORD_ITERATOR_INTERVAL);
+                        mWordIterator.setCharSequence(
+                                editable, originalWordEnd, wordIteratorWindowEnd);
+                        wordEnd = mWordIterator.following(originalWordEnd);
+                    }
+                    if (wordEnd == BreakIterator.DONE) break;
+                    wordStart = mWordIterator.getBeginning(wordEnd);
+                    if (wordStart == BreakIterator.DONE) {
                         break;
                     }
-
-                    // A new word has been created across the interval boundaries with this edit.
-                    // The previous spans (that ended on start / started on end) are not valid
-                    // anymore and must be removed.
-                    if (wordStart < start && wordEnd > start) {
-                        removeSpansAt(editable, start, spellCheckSpans);
-                        removeSpansAt(editable, start, suggestionSpans);
-                    }
-
-                    if (wordStart < end && wordEnd > end) {
-                        removeSpansAt(editable, end, spellCheckSpans);
-                        removeSpansAt(editable, end, suggestionSpans);
-                    }
-
-                    // Do not create new boundary spans if they already exist
-                    boolean createSpellCheckSpan = true;
-                    if (wordEnd == start) {
-                        for (int i = 0; i < spellCheckSpans.length; i++) {
-                            final int spanEnd = editable.getSpanEnd(spellCheckSpans[i]);
-                            if (spanEnd == start) {
-                                createSpellCheckSpan = false;
-                                break;
-                            }
-                        }
-                    }
-
-                    if (wordStart == end) {
-                        for (int i = 0; i < spellCheckSpans.length; i++) {
-                            final int spanStart = editable.getSpanStart(spellCheckSpans[i]);
-                            if (spanStart == end) {
-                                createSpellCheckSpan = false;
-                                break;
-                            }
-                        }
-                    }
-
-                    if (createSpellCheckSpan) {
-                        addSpellCheckSpan(editable, wordStart, wordEnd);
-                    }
-                    wordCount++;
-                }
-
-                // iterate word by word
-                int originalWordEnd = wordEnd;
-                wordEnd = mWordIterator.following(wordEnd);
-                if ((wordIteratorWindowEnd < end) &&
-                        (wordEnd == BreakIterator.DONE || wordEnd >= wordIteratorWindowEnd)) {
-                    wordIteratorWindowEnd = Math.min(end, originalWordEnd + WORD_ITERATOR_INTERVAL);
-                    mWordIterator.setCharSequence(editable, originalWordEnd, wordIteratorWindowEnd);
-                    wordEnd = mWordIterator.following(originalWordEnd);
-                }
-                if (wordEnd == BreakIterator.DONE) break;
-                wordStart = mWordIterator.getBeginning(wordEnd);
-                if (wordStart == BreakIterator.DONE) {
-                    break;
                 }
             }
 
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 2cacbdca..89c506f 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -26,6 +26,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -40,13 +41,13 @@
  *
  * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-spinner.html">Spinner
  * tutorial</a>.</p>
- * 
+ *
  * @attr ref android.R.styleable#Spinner_prompt
  */
 @Widget
 public class Spinner extends AbsSpinner implements OnClickListener {
     private static final String TAG = "Spinner";
-    
+
     // Only measure this many items to get a decent max width.
     private static final int MAX_ITEMS_MEASURED = 15;
 
@@ -54,7 +55,7 @@
      * Use a dialog window for selecting spinner options.
      */
     public static final int MODE_DIALOG = 0;
-    
+
     /**
      * Use a dropdown anchored to the Spinner for selecting spinner options.
      */
@@ -759,13 +760,30 @@
 
         @Override
         public void show() {
+            final Drawable background = getBackground();
+            int bgOffset = 0;
+            if (background != null) {
+                background.getPadding(mTempRect);
+                bgOffset = -mTempRect.left;
+            } else {
+                mTempRect.left = mTempRect.right = 0;
+            }
+
             final int spinnerPaddingLeft = Spinner.this.getPaddingLeft();
             if (mDropDownWidth == WRAP_CONTENT) {
                 final int spinnerWidth = Spinner.this.getWidth();
                 final int spinnerPaddingRight = Spinner.this.getPaddingRight();
+
+                int contentWidth =  measureContentWidth(
+                        (SpinnerAdapter) mAdapter, getBackground());
+                final int contentWidthLimit = mContext.getResources()
+                        .getDisplayMetrics().widthPixels - mTempRect.left - mTempRect.right;
+                if (contentWidth > contentWidthLimit) {
+                    contentWidth = contentWidthLimit;
+                }
+
                 setContentWidth(Math.max(
-                        measureContentWidth((SpinnerAdapter) mAdapter, getBackground()),
-                        spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight));
+                       contentWidth, spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight));
             } else if (mDropDownWidth == MATCH_PARENT) {
                 final int spinnerWidth = Spinner.this.getWidth();
                 final int spinnerPaddingRight = Spinner.this.getPaddingRight();
@@ -773,12 +791,6 @@
             } else {
                 setContentWidth(mDropDownWidth);
             }
-            final Drawable background = getBackground();
-            int bgOffset = 0;
-            if (background != null) {
-                background.getPadding(mTempRect);
-                bgOffset = -mTempRect.left;
-            }
             setHorizontalOffset(bgOffset + spinnerPaddingLeft);
             setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
             super.show();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0c0672d..385c7c7 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5996,7 +5996,6 @@
                 if (des < 0) {
                     des = (int) FloatMath.ceil(Layout.getDesiredWidth(mTransformed, mTextPaint));
                 }
-
                 width = des;
             } else {
                 width = boring.width;
@@ -6017,7 +6016,7 @@
                 }
 
                 if (hintDes < 0) {
-                    hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mHintBoring);
+                    hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir, mHintBoring);
                     if (hintBoring != null) {
                         mHintBoring = hintBoring;
                     }
@@ -6025,10 +6024,8 @@
 
                 if (hintBoring == null || hintBoring == UNKNOWN_BORING) {
                     if (hintDes < 0) {
-                        hintDes = (int) FloatMath.ceil(
-                                Layout.getDesiredWidth(mHint, mTextPaint));
+                        hintDes = (int) FloatMath.ceil(Layout.getDesiredWidth(mHint, mTextPaint));
                     }
-
                     hintWidth = hintDes;
                 } else {
                     hintWidth = hintBoring.width;
@@ -6292,20 +6289,25 @@
         if (changed && mEditor != null) getEditor().mTextDisplayListIsValid = false;
     }
 
+    private boolean isShowingHint() {
+        return TextUtils.isEmpty(mText) && !TextUtils.isEmpty(mHint);
+    }
+
     /**
      * Returns true if anything changed.
      */
     private boolean bringTextIntoView() {
+        Layout layout = isShowingHint() ? mHintLayout : mLayout;
         int line = 0;
         if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) {
-            line = mLayout.getLineCount() - 1;
+            line = layout.getLineCount() - 1;
         }
 
-        Layout.Alignment a = mLayout.getParagraphAlignment(line);
-        int dir = mLayout.getParagraphDirection(line);
+        Layout.Alignment a = layout.getParagraphAlignment(line);
+        int dir = layout.getParagraphDirection(line);
         int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
         int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom();
-        int ht = mLayout.getHeight();
+        int ht = layout.getHeight();
 
         int scrollx, scrolly;
 
@@ -6324,8 +6326,8 @@
              * keep leading edge in view.
              */
 
-            int left = (int) FloatMath.floor(mLayout.getLineLeft(line));
-            int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
+            int left = (int) FloatMath.floor(layout.getLineLeft(line));
+            int right = (int) FloatMath.ceil(layout.getLineRight(line));
 
             if (right - left < hspace) {
                 scrollx = (right + left) / 2 - hspace / 2;
@@ -6337,10 +6339,10 @@
                 }
             }
         } else if (a == Layout.Alignment.ALIGN_RIGHT) {
-            int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
+            int right = (int) FloatMath.ceil(layout.getLineRight(line));
             scrollx = right - hspace;
         } else { // a == Layout.Alignment.ALIGN_LEFT (will also be the default)
-            scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line));
+            scrollx = (int) FloatMath.floor(layout.getLineLeft(line));
         }
 
         if (ht < vspace) {
@@ -6368,22 +6370,24 @@
     public boolean bringPointIntoView(int offset) {
         boolean changed = false;
 
-        if (mLayout == null) return changed;
+        Layout layout = isShowingHint() ? mHintLayout: mLayout;
 
-        int line = mLayout.getLineForOffset(offset);
+        if (layout == null) return changed;
+
+        int line = layout.getLineForOffset(offset);
 
         // FIXME: Is it okay to truncate this, or should we round?
-        final int x = (int)mLayout.getPrimaryHorizontal(offset);
-        final int top = mLayout.getLineTop(line);
-        final int bottom = mLayout.getLineTop(line + 1);
+        final int x = (int)layout.getPrimaryHorizontal(offset);
+        final int top = layout.getLineTop(line);
+        final int bottom = layout.getLineTop(line + 1);
 
-        int left = (int) FloatMath.floor(mLayout.getLineLeft(line));
-        int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
-        int ht = mLayout.getHeight();
+        int left = (int) FloatMath.floor(layout.getLineLeft(line));
+        int right = (int) FloatMath.ceil(layout.getLineRight(line));
+        int ht = layout.getHeight();
 
         int grav;
 
-        switch (mLayout.getParagraphAlignment(line)) {
+        switch (layout.getParagraphAlignment(line)) {
             case ALIGN_LEFT:
                 grav = 1;
                 break;
@@ -6391,10 +6395,10 @@
                 grav = -1;
                 break;
             case ALIGN_NORMAL:
-                grav = mLayout.getParagraphDirection(line);
+                grav = layout.getParagraphDirection(line);
                 break;
             case ALIGN_OPPOSITE:
-                grav = -mLayout.getParagraphDirection(line);
+                grav = -layout.getParagraphDirection(line);
                 break;
             case ALIGN_CENTER:
             default:
@@ -8141,24 +8145,26 @@
     @Override
     public boolean performLongClick() {
         boolean handled = false;
-        boolean vibrate = true;
 
         if (super.performLongClick()) {
             handled = true;
         }
 
+        if (mEditor == null) {
+            return handled;
+        }
+
         // Long press in empty space moves cursor and shows the Paste affordance if available.
-        if (!handled && mEditor != null && !isPositionOnText(getEditor().mLastDownPositionX, getEditor().mLastDownPositionY) &&
+        if (!handled && !isPositionOnText(getEditor().mLastDownPositionX, getEditor().mLastDownPositionY) &&
                 getEditor().mInsertionControllerEnabled) {
             final int offset = getOffsetForPosition(getEditor().mLastDownPositionX, getEditor().mLastDownPositionY);
             stopSelectionActionMode();
             Selection.setSelection((Spannable) mText, offset);
             getInsertionController().showWithActionPopup();
             handled = true;
-            vibrate = false;
         }
 
-        if (!handled && mEditor != null && getEditor().mSelectionActionMode != null) {
+        if (!handled && getEditor().mSelectionActionMode != null) {
             if (touchPositionIsInSelection()) {
                 // Start a drag
                 final int start = getSelectionStart();
@@ -8177,15 +8183,12 @@
         }
 
         // Start a new selection
-        if (!handled && mEditor != null) {
-            vibrate = handled = startSelectionActionMode();
+        if (!handled) {
+            handled = startSelectionActionMode();
         }
 
-        if (vibrate) {
+        if (handled) {
             performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-        }
-
-        if (handled && mEditor != null) {
             getEditor().mDiscardNextActionUp = true;
         }
 
@@ -8277,8 +8280,11 @@
     @Override
     protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {
         super.onScrollChanged(horiz, vert, oldHoriz, oldVert);
-        if (mEditor != null && getEditor().mPositionListener != null) {
-            getEditor().mPositionListener.onScrollChanged();
+        if (mEditor != null) {
+            if (getEditor().mPositionListener != null) {
+                getEditor().mPositionListener.onScrollChanged();
+            }
+            getEditor().mTextDisplayListIsValid = false;
         }
     }
 
@@ -8711,6 +8717,8 @@
     @Override
     public void onResolveTextDirection() {
         if (hasPasswordTransformationMethod()) {
+            // TODO: take care of the content direction to show the password text and dots justified
+            // to the left or to the right
             mTextDir = TextDirectionHeuristics.LOCALE;
             return;
         }
@@ -10685,9 +10693,12 @@
                 return;
             }
 
-            if (offset != mPreviousOffset || parentScrolled) {
-                updateSelection(offset);
-                addPositionToTouchUpFilter(offset);
+            boolean offsetChanged = offset != mPreviousOffset;
+            if (offsetChanged || parentScrolled) {
+                if (offsetChanged) {
+                    updateSelection(offset);
+                    addPositionToTouchUpFilter(offset);
+                }
                 final int line = mLayout.getLineForOffset(offset);
 
                 mPositionX = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX);
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 0cadb16..d462d7f 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -44,6 +44,7 @@
     public static final int BASE_WIFI_P2P_MANAGER                                   = 0x00022000;
     public static final int BASE_WIFI_P2P_SERVICE                                   = 0x00023000;
     public static final int BASE_WIFI_MONITOR                                       = 0x00024000;
+    public static final int BASE_WIFI_MANAGER                                       = 0x00025000;
     public static final int BASE_DHCP                                               = 0x00030000;
     public static final int BASE_DATA_CONNECTION                                    = 0x00040000;
     public static final int BASE_DATA_CONNECTION_AC                                 = 0x00041000;
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 7f5d54d..efeba5c 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -36,6 +36,8 @@
 #define TYPE_FACE_HEBREW_REGULAR "/system/fonts/DroidSansHebrew-Regular.ttf"
 #define TYPE_FACE_HEBREW_BOLD "/system/fonts/DroidSansHebrew-Bold.ttf"
 #define TYPEFACE_BENGALI "/system/fonts/Lohit-Bengali.ttf"
+#define TYPEFACE_DEVANAGARI "/system/fonts/Lohit-Devanagari.ttf"
+#define TYPEFACE_TAMIL "/system/fonts/Lohit-Tamil.ttf"
 #define TYPEFACE_THAI "/system/fonts/DroidSansThai.ttf"
 
 ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
@@ -828,6 +830,20 @@
 #endif
         break;
 
+    case HB_Script_Devanagari:
+        typeface = getCachedTypeface(&mDevanagariTypeface, TYPEFACE_DEVANAGARI);
+#if DEBUG_GLYPHS
+        ALOGD("Using Devanagari Typeface");
+#endif
+        break;
+
+    case HB_Script_Tamil:
+        typeface = getCachedTypeface(&mTamilTypeface, TYPEFACE_TAMIL);
+#if DEBUG_GLYPHS
+        ALOGD("Using Tamil Typeface");
+#endif
+        break;
+
     default:
         if (!typeface) {
             typeface = mDefaultTypeface;
@@ -859,6 +875,8 @@
     case HB_Script_Arabic:
     case HB_Script_Hebrew:
     case HB_Script_Bengali:
+    case HB_Script_Devanagari:
+    case HB_Script_Tamil:
     case HB_Script_Thai:{
         const uint16_t* text16 = (const uint16_t*)(mShaperItem.string + mShaperItem.item.pos);
         SkUnichar firstUnichar = SkUTF16_NextUnichar(&text16);
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 7ac2f18..3c834a4 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -194,6 +194,8 @@
     SkTypeface* mHebrewBoldTypeface;
     SkTypeface* mBengaliTypeface;
     SkTypeface* mThaiTypeface;
+    SkTypeface* mDevanagariTypeface;
+    SkTypeface* mTamilTypeface;
 
     /**
      * Cache of Harfbuzz faces
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index 80ecf2f..c8f911f 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -128,15 +128,6 @@
         return 0;
     }
 
-    // Enable WAL auto-checkpointing after a commit whenever at least one frame is in the log.
-    // This ensures that a checkpoint will occur after each transaction if needed.
-    err = sqlite3_wal_autocheckpoint(db, 1);
-    if (err) {
-        throw_sqlite3_exception(env, db, "Could not enable auto-checkpointing.");
-        sqlite3_close(db);
-        return 0;
-    }
-
     // Register custom Android functions.
     err = register_android_functions(db, UTF16_STORAGE);
     if (err) {
@@ -426,11 +417,11 @@
     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
 
+    // We ignore the result of sqlite3_finalize because it is really telling us about
+    // whether any errors occurred while executing the statement.  The statement itself
+    // is always finalized regardless.
     ALOGV("Finalized statement %p on connection %p", statement, connection->db);
-    int err = sqlite3_finalize(statement);
-    if (err != SQLITE_OK) {
-        throw_sqlite3_exception(env, connection->db, NULL);
-    }
+    sqlite3_finalize(statement);
 }
 
 static jint nativeGetParameterCount(JNIEnv* env, jclass clazz, jint connectionPtr,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d4d29ae..17d2212 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -121,6 +121,7 @@
     <protected-broadcast android:name="android.intent.action.CLEAR_DNS_CACHE" />
     <protected-broadcast android:name="android.intent.action.PROXY_CHANGE" />
 
+    <protected-broadcast android:name="android.os.UpdateLock.UPDATE_LOCK_CHANGED" />
 
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
@@ -1538,6 +1539,13 @@
         android:label="@string/permlab_accessContentProvidersExternally"
         android:description="@string/permdesc_accessContentProvidersExternally"
         android:protectionLevel="signature" />
+    <!-- Allows an application to hold an UpdateLock, recommending that a headless
+         OTA reboot *not* occur while the lock is held.
+         @hide -->
+    <permission android:name="android.permission.UPDATE_LOCK"
+        android:label="@string/permlab_updateLock"
+        android:description="@string/permdesc_updateLock"
+        android:protectionLevel="signatureOrSystem" />
 
     <!-- The system process is explicitly the only one allowed to launch the
          confirmation UI for full backup/restore -->
diff --git a/core/res/res/layout/simple_list_item_multiple_choice.xml b/core/res/res/layout/simple_list_item_multiple_choice.xml
index 0305427..cb23dfd 100644
--- a/core/res/res/layout/simple_list_item_multiple_choice.xml
+++ b/core/res/res/layout/simple_list_item_multiple_choice.xml
@@ -17,10 +17,10 @@
 <CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@android:id/text1"
     android:layout_width="match_parent"
-    android:layout_height="?android:attr/listPreferredItemHeight"
-    android:textAppearance="?android:attr/textAppearanceListItem"
+    android:layout_height="?android:attr/listPreferredItemHeightSmall"
+    android:textAppearance="?android:attr/textAppearanceListItemSmall"
     android:gravity="center_vertical"
     android:checkMark="?android:attr/listChoiceIndicatorMultiple"
-    android:paddingLeft="8dip"
-    android:paddingRight="8dip"
+    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
 />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 49e0fd5..1de0390 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -315,10 +315,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"aktiveer of deaktiveer programkomponente"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Laat die program toe om te verander of \'n komponent van ander program geaktiveer is of nie. Kwaadwillige programme kan dit dalk gebruik om belangrike tabletvermoëns te deaktiveer. Wees versigtig met hierdie toestemming, want dit kan programkomponente tot \'n onbruikbare, inkonsekwente of onstabiele toestand bring."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Laat die program toe om te verander of \'n komponent van ander program geaktiveer is of nie. Kwaadwillige programme kan dit gebruik om belangrike foonvermoëns te deaktiveer. Wees versigtig met hierdie toestemming, want dit kan programkomponente tot \'n onbruikbare, inkonsekwente of onstabiele toestand bring."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"toestemmings te verleen of te herroep"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Laat \'n program toe om spesifieke toestemmings te verleen of te herroep vir die betrokke program of ander programme. Skadelike programme kan dit gebruik om toegang te verkry tot kenmerke waarvoor jy nie toestemming verleen het nie."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"stel voorkeurprogramme"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Laat die program toe om jou voorkeur-programme te verander. Kwaadwillige programme kan stilweg die programme wat loop, verander, wat jou bestaande programme bedrieg om private data oor jou in te samel."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"verander globale stelselinstellings"</string>
@@ -1010,8 +1008,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Stel datum"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Stel"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Verstek"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NUUT: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Geen toestemmings benodig nie"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Versteek"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Wys alle"</b></string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 7300fa5..abdda71 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"تمكين مكونات التطبيق أو تعطيلها"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"للسماح للتطبيق بتغيير ما إذا كان سيتم تمكين مكون لتطبيق آخر أم لا. يمكن أن تستخدم التطبيقات الضارة ذلك لتعطيل قدرات الجهاز اللوحي المهمة. يجب توخي الحذر عند استخدام هذا الإذن، وذلك لأنه من الممكن أن يؤدي ذلك إلى جعل حالة مكونات التطبيق غير قابلة للاستخدام أو غير متناسقة أو غير مستقرة."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"للسماح للتطبيق بتغيير ما إذا كان سيتم تمكين مكون لتطبيق آخر أم لا. يمكن أن تستخدم التطبيقات الضارة ذلك لتعطيل قدرات الهاتف المهمة. يجب توخي الحذر عند استخدام هذا الإذن، وذلك لأنه من الممكن أن يؤدي ذلك إلى جعل حالة مكونات التطبيق غير قابلة للاستخدام أو غير متناسقة أو غير مستقرة."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"منح الأذونات أو إلغائها"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"للسماح لأحد التطبيقات بمنح أذونات محددة أو إلغائها لنفسه أو لتطبيقات أخرى. قد تستخدم التطبيقات الضارة هذا للدخول إلى ميزات لم تمنحها إذنًا لدخولها."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"تعيين التطبيقات المفضلة"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"للسماح للتطبيق بتعديل التطبيقات المفضلة. يمكن أن تغيّر التطبيقات الضارة التطبيقات قيد التشغيل بشكل غير ملحوظ، وانتحال صفة التطبيقات الحالية لجمع بيانات خاصة منك."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"تعديل إعدادات النظام العمومية"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"تعيين التاريخ"</string>
     <string name="date_time_set" msgid="5777075614321087758">"تعيين"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"افتراضي"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"جديد: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"لا أذونات مطلوبة"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"إخفاء"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"عرض الكل"</b></string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 9a578da..5cfa349 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"уключыць або адключыць кампаненты прыкладання"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Дазваляе прыкладанням змяняць вызначэнне таго, будзе ўключаны кампанент іншага прыкладання ці не. Шкоднасныя прыкладанні могуць выкарыстоўваць гэта для адключэння важных магчымасцяў планшэта. З гэтым дазволам трэба быць уважлівым, бо можна прывесці кампаненты прыкладання ў непрыдатны, супярэчлівы або няўстойлівы стан."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Дазваляе прыкладанням змяняць вызначэнне таго, будзе ўключаны кампанент іншага прыкладання ці не. Шкоднасныя прыкладанні могуць выкарыстоўваць гэта для адключэння важных магчымасцяў тэлефона. З гэтым дазволам трэба быць уважлівым, бо можна прывесці кампаненты прыкладання ў непрыдатны, супярэчлівы або няўстойлівы стан."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"прадаставiць або адмяніць дазвол"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Дазваляе прыкладанню прадастаўляць або адмяняць пэўныя дазволы для гэтага або іншых прыкладанняў. Шкоднасныя прыкладаннi могуць выкарыстоўваць гэта, каб атрымаць доступ да функцый, якiя вы iм не прадаставiлi."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"усталяваць пажаданыя прыкладанні"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Дазваляе прыкладанням змяняць вашы пажаданыя прыкладанні. Шкоднасныя прыкладанні могуць непрыкметна змяняць запушчаныя прыкладанні, падмяняючы існуючыя прыкладанні, каб збiраць вашы асабістыя дадзеныя."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"змена глабальных параметраў сістэмы"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Усталяваць дату"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Задаць"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Па змаўчанні"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"НОВАЕ: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Дазволу не патрабуецца"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Не паказваць"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Паказаць усе"</b></string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 907392a..c66451a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"activa o desactiva els components de l\'aplicació"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Permet que l\'aplicació canviï si un component d\'una altra aplicació està activat o no. Les aplicacions malicioses poden utilitzar aquesta funció per desactivar funcions importants de la tauleta. Cal anar amb compte amb aquest permís, ja que és possible que els components de l\'aplicació esdevinguin inutilitzables, incoherents o inestables."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Permet que l\'aplicació canviï si un component d\'una altra aplicació està activat o no. Les aplicacions malicioses poden utilitzar aquesta funció per desactivar funcions importants del telèfon. Cal anar amb compte amb aquest permís, perquè és possible que els components de l\'aplicació esdevinguin inutilitzables, incoherents o inestables."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"concedeix o denega permisos"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Permet que una aplicació concedeixi o denegui permisos específics per a aquesta o per a altres aplicacions. És possible que les aplicacions malicioses ho facin servir per accedir a funcions a les quals no heu concedit accés."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"defineix les aplicacions preferides"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Permet que l\'aplicació modifiqui les aplicacions preferides. Les aplicacions malicioses poden canviar silenciosament les aplicacions que s\'executen, falsejar les aplicacions existents o recollir dades privades de l\'usuari."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modificar la configuració global del sistema"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Establiment de data"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Defineix"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Predeterminat"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOU: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"No cal cap permís"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Amaga"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostra\'ls tots"</b></string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index f889c3b..89ff9cff3 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"aktivace či deaktivace komponent aplikací"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Umožňuje aplikaci změnit, zda je komponenta jiné aplikace povolena nebo ne. Škodlivé aplikace mohou toto oprávnění použít k vypnutí důležitých funkcí tabletu. Toto oprávnění je třeba používat opatrně, protože může dojít k nepoužitelnosti, nekonzistenci nebo nestabilitě komponent aplikací."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Umožňuje aplikaci změnit, zda je komponenta jiné aplikace povolena nebo ne. Škodlivé aplikace mohou toto oprávnění použít k vypnutí důležitých funkcí telefonu. Toto oprávnění je třeba používat opatrně, protože může dojít k nepoužitelnosti, nekonzistenci nebo nestabilitě komponent aplikací."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"udělení nebo odebrání oprávnění"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Umožňuje aplikaci udělit nebo odebrat sobě samotné nebo jiným aplikacím určitá oprávnění. Škodlivé aplikace pomocí tohoto oprávnění mohou získat přístup k funkcím, které jste jim nepovolili."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"nastavení upřednostňovaných aplikací"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Umožňuje aplikaci upravit preferované aplikace. Škodlivé aplikace mohou tajně měnit běžící aplikace a přinutit stávající aplikace, aby shromažďovaly vaše soukromé údaje."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"změna globálních nastavení systému"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Nastavení data"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Nastavit"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Výchozí"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOVÉ: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nejsou vyžadována žádná oprávnění"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Skrýt"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Zobrazit vše"</b></string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index b432d46..91da279 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"ενεργοποίηση ή απενεργοποίηση στοιχείων εφαρμογής"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Επιτρέπει στην εφαρμογή την αλλαγή της κατάστασης ενεργοποίησης κάποιου στοιχείου. Τυχόν κακόβουλες εφαρμογές μπορούν να χρησιμοποιήσουν αυτήν τη δυνατότητα για την απενεργοποίηση σημαντικών δυνατοτήτων του tablet. Αυτή η άδεια θα πρέπει να χρησιμοποιείται προσεκτικά, καθώς είναι πιθανό να θέσει τα στοιχεία εφαρμογών σε κατάσταση αχρηστίας, μη συνοχής και αστάθειας."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Επιτρέπει στην εφαρμογή την αλλαγή της κατάστασης ενεργοποίησης κάποιου στοιχείου. Τυχόν κακόβουλες εφαρμογές μπορούν να χρησιμοποιήσουν αυτήν τη δυνατότητα για την απενεργοποίηση σημαντικών δυνατοτήτων του τηλεφώνου. Αυτή η άδεια θα πρέπει να χρησιμοποιείται προσεκτικά, καθώς είναι πιθανό να θέσει τα στοιχεία εφαρμογών σε κατάσταση αχρηστίας, μη συνοχής και αστάθειας."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"εκχώρηση ή ανάκληση δικαιωμάτων"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Επιτρέπει σε μια εφαρμογή να εκχωρήσει ή να ανακαλέσει ειδικά δικαιώματα για αυτήν ή για άλλες εφαρμογές. Οι κακόβουλες εφαρμογές μπορεί να το χρησιμοποιήσουν αυτό ώστε να αποκτήσουν πρόσβαση σε λειτουργίες για τις οποίες δεν τους έχει εκχωρηθεί δικαίωμα."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"ορισμός προτιμώμενων εφαρμογών"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Επιτρέπει στην εφαρμογή την τροποποίηση των εφαρμογών της προτίμησής σας. Τυχόν κακόβουλες εφαρμογές ενδέχεται να να αλλάξουν χωρίς ειδοποίηση τις εφαρμογές που εκτελούνται, \"ξεγελώντας\" τις υπάρχουσες εφαρμογές ώστε να συλλέξουν ιδιωτικά δεδομένα από εσάς."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"τροποποίηση καθολικών ρυθμίσεων συστήματος"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Ορισμός ημερομηνίας"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Ορισμός"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Προεπιλεγμένο"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"ΝΕΟ: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Δεν απαιτούνται άδειες"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Απόκρυψη"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Εμφάνιση όλων"</b></string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 1013b11..cfd9965 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"enable or disable app components"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Allows the app to change whether a component of another app is enabled or not. Malicious apps may use this to disable important tablet capabilities. Care must be taken with this permission, as it is possible to get app components into an unusable, inconsistent or unstable state."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Allows the app to change whether a component of another app is enabled or not. Malicious apps may use this to disable important phone capabilities. Care must be taken with this permission, as it is possible to get app components into an unusable, inconsistent or unstable state."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"grant or revoke permissions"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Allows an application to grant or revoke specific permissions for it or other applications. Malicious applications may use this to access features for which you have not granted them permission."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"set preferred apps"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Allows the app to modify your preferred apps. Malicious apps may silently change the apps that are run, spoofing your existing apps to collect private data from you."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modify global system settings"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Set date"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Set"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Default"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NEW: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"No permission required"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Hide"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Show all"</b></string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index a019c65..fe292e6 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"activar o desactivar componentes de la aplicación"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Permite que la aplicación determine si un componente de otra aplicación está habilitado o no. Las aplicaciones maliciosas pueden utilizar este permiso para desactivar funciones importantes de la tableta. Es necesario ser precavido con este permiso, ya que es posible que los componentes de la aplicación queden inservibles, incoherentes o inestables."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Permite que la aplicación determine si un componente de otra aplicación está habilitado o no. Las aplicaciones maliciosas pueden utilizar este permiso para desactivar funciones importantes del dispositivo. Es necesario ser precavido con este permiso, ya que es posible que los componentes de la aplicación queden inservibles, incoherentes o inestables."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"Otorgar o revocar permisos"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Permite a una aplicación otorgar permisos específicos a otras aplicaciones, autoconcedérselos o revocarlos. Las aplicaciones maliciosas pueden así acceder a funciones para las que no les otorgaste permiso."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"establecer aplicaciones preferidas"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Permite que la aplicación modifique tus aplicaciones preferidas. Las aplicaciones maliciosas pueden modificar sin aviso las aplicaciones que se ejecutan y así engañar a tus aplicaciones existentes para que recopilen tu información privada."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modificar la configuración global del sistema"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Configurar fecha"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Establecer"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Predeterminado"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NUEVO: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"No se requieren permisos"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ocultar"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar todos"</b></string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index a159eb6..ee7efc8 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"sovelluskomponenttien ottaminen käyttöön tai pois käytöstä"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Antaa sovelluksen muuttaa, onko toisen sovelluksen komponentti käytössä vai ei. Haitalliset sovellukset voivat käyttää tätä tablet-laitteen tärkeiden ominaisuuksien poistamiseen käytöstä. Tämän luvan käyttöönotto edellyttää varovaisuutta, sillä sen avulla sovelluskomponentit on mahdollista saada epäkäytettävään, epäyhtenäiseen tai epävakaaseen tilaan."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Antaa sovelluksen muuttaa sitä, onko toisen sovelluksen komponentti käytössä vai ei. Haitalliset sovellukset voivat käyttää tätä puhelimen tärkeiden ominaisuuksien poistamiseen käytöstä. Tämän luvan käyttöönotto edellyttää varovaisuutta, sillä sen avulla sovelluskomponentit on mahdollista saada epäkäytettävään, epäyhtenäiseen tai epävakaaseen tilaan."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"myönnä tai kiellä käyttöluvat"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Antaa sovelluksen myöntää tai kieltää tiettyjä käyttööoikeuksia itselleen tai muille sovelluksille. Haittaohjelmat voivat käyttää tämän ominaisuuden avulla toimintoja, joiden käyttöön et ole antanut lupaa."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"ensisijaisten sovellusten asettaminen"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Antaa sovelluksen muokata ensisijaisia sovelluksia. Haitalliset sovellukset voivat muuttaa käynnistettäviä sovelluksia huomaamattomasti ja kerätä henkilökohtaisia tietoja matkimalla nykyisiä sovelluksia."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"muokkaa yleisiä järjestelmän asetuksia"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Aseta päivämäärä"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Aseta"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Oletus"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"UUTTA: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Lupia ei tarvita"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Piilota"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Näytä kaikki"</b></string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index e9b3b28..bca5701 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -767,7 +767,7 @@
     <string name="permlab_serialPort" msgid="546083327654631076">"accéder aux ports série"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Permet à l\'application autorisée d\'accéder aux ports série avec l\'API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accès externe fournisseurs de contenu"</string>
-    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permettre à l\'application titulaire d\'accéder à des fournisseurs de contenu depuis l\'interface. Les applications standards ne devraient jamais avoir recours à cette autorisation."</string>
+    <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permettre à l\'application titulaire d\'accéder à des fournisseurs de contenu depuis la commande shell. Les applications standards ne devraient jamais avoir recours à cette autorisation."</string>
     <string name="save_password_message" msgid="767344687139195790">"Voulez-vous que le navigateur se souvienne de ce mot de passe ?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Pas maintenant"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Mémoriser"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index f32322e..1454253 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"एप्‍लिकेशन घटकों को सक्षम या अक्षम करें"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"एप्‍लिकेशन को यह बदलने देता है कि किसी अन्‍य एप्‍लिकेशन का घटक सक्षम है या नहीं. दुर्भावनापूर्ण एप्‍लिकेशन महत्‍वपूर्ण फ़ोन क्षमताओं को अक्षम करने में इसका उपयोग कर सकते हैं. इस अनुमति का उपयोग सावधानी के साथ करना चाहिए, क्योंकि इससे एप्‍लिकेशन घटकों के अनुपयोगी, असंगत, या अस्‍थिर स्‍थिति में जाने की संभावना है."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"एप्‍लिकेशन को यह बदलने देता है कि किसी अन्‍य एप्‍लिकेशन का घटक सक्षम है या नहीं. दुर्भावनापूर्ण एप्‍लिकेशन महत्‍वपूर्ण फ़ोन क्षमताओं को अक्षम करने में इसका उपयोग कर सकते हैं. इस अनुमति का उपयोग सावधानी के साथ करना चाहिए, क्योंकि इससे एप्‍लिकेशन घटकों के अनुपयोगी, असंगत, या अस्‍थिर स्‍थिति में जाने की संभावना है."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"अनुमति दें या रद्द करें"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"एप्लिकेशन को उसके या अन्य एप्लिकेशन के लिए विशेष अनुमतियां देने या रद्द करने देता है. दुर्भावनापूर्ण एप्लिकेशन इसका उपयोग उन विशेषताओं तक पहुंचने के लिए कर सकते हैं जो आपने उन्हें नहीं दी हैं."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"पसंदीदा एप्‍लिकेशन सेट करें"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"एप्लिकेशन को आपके पसंदीदा एप्लिकेशन को संशोधित करने देता है. दुर्भावनापूर्ण एप्लिकेशन आपसे निजी डेटा एकत्रित करने के लिए आपके मौजूदा एप्लिकेशन को स्पूफ़ करके, चलाए जाने वाले एप्लिकेशन को चुपचाप बदल सकते हैं."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"वैश्विक सिस्‍टम सेटिंग संशोधित करें"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"दिनांक सेट करें"</string>
     <string name="date_time_set" msgid="5777075614321087758">"सेट करें"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"डिफ़ॉल्ट"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"नया: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"किसी अनुमति की आवश्‍यकता नहीं है"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"छुपाएं"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"सभी दिखाएं"</b></string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 8b8a51f..1908840 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"omogućavanje ili onemogućavanje komponenti aplikacije"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Omogućuje aplikaciji da promijeni hoće li komponenta neke druge aplikacije biti omogućena ili neće. Zlonamjerne aplikacije mogu to upotrijebiti da bi onemogućile važne mogućnosti tabletnog računala. Treba biti oprezan s tom dozvolom jer je moguće dovesti komponente aplikacija u neupotrebljivo, nedosljedno ili nestabilno stanje."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Omogućuje aplikaciji da promijeni hoće li komponenta neke druge aplikacije biti omogućena. Zlonamjerne aplikacije mogu to upotrijebiti da bi onemogućile važne mogućnosti telefona. Treba biti oprezan s tom dozvolom jer je moguće dovesti komponente aplikacija u neupotrebljivo, nedosljedno ili nestabilno stanje."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"dati ili oduzeti dopuštenja"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Aplikaciji omogućuje da odobri ili odbije određena dopuštenja za sebe ili druge aplikacije. Zlonamjerne aplikacije to mogu upotrijebiti za pristup značajkama za koje im niste odobrili pristup."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"postavljanje željenih aplikacija"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Omogućuje aplikaciji promjenu vaših željenih aplikacija. Zlonamjerne aplikacije mogu potajno promijeniti aplikacije koje su pokrenute, zavaravajući vaše postojeće aplikacije kako bi prikupljale privatne podatke od vas."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"izmjena postavki globalnog sustava"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Postavi datum"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Postavi"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Zadano"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOVO: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nije potrebno dopuštenje"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Sakrij"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Pokaži sve"</b></string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 7f89990..81f697b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"alkalmazáskomponensek be- és kikapcsolása"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Lehetővé teszi az alkalmazás számára annak módosítását, hogy más alkalmazások komponensei engedélyezve vannak-e vagy sem. A rosszindulatú alkalmazások ezt a táblagép fontos funkcióinak kikapcsolására használhatják. Óvatosan kell eljárni az engedély megadásával, mert lehetséges, hogy a komponensek használhatatlanok, inkonzisztensek vagy instabilak lesznek."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Lehetővé teszi az alkalmazás számára annak módosítását, hogy más alkalmazások komponensei engedélyezve vannak-e vagy sem. A rosszindulatú alkalmazások ezt a telefon fontos funkcióinak kikapcsolására használhatják. Óvatosan kell eljárni az engedély megadásával, mert lehetséges, hogy a komponensek használhatatlanok, inkonzisztensek vagy instabilak lesznek."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"engedélyek megadása vagy visszavonása"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Lehetővé teszi, hogy egy alkalmazás engedélyeket adjon vagy vonjon vissza saját maga vagy más alkalmazás számára. A rosszindulatú alkalmazások olyan funkciókhoz való hozzáféréshez használhatják ezt, amelyeket nem engedélyezett számukra."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"előnyben részesített alkalmazások beállítása"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Lehetővé teszi az alkalmazás számára a preferált alkalmazások módosítását. A rosszindulatú alkalmazások ezáltal észrevétlenül megváltoztathatják a futó alkalmazásokat, személyes adatokat gyűjtve Öntől."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"rendszer globális beállításainak módosítása"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Dátum beállítása"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Beállítás"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Alapértelmezett"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"ÚJ: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nincs szükség engedélyre"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Elrejtés"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Az összes megjelenítése"</b></string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 5245306..08b0f60 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"mengaktifkan atau menonaktifkan komponen apl"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Mengizinkan apl mengubah apakah komponen apl lain diaktifkan atau tidak. Apl berbahaya dapat menggunakan ini untuk menonaktifkan kemampuan tablet yang penting. Izin ini harus digunakan dengan hati-hati karena dapat menjadikan komponen apl tidak dapat digunakan, tidak konsisten, atau tidak stabil."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Mengizinkan apl mengubah apakah komponen apl lain diaktifkan atau tidak. Apl berbahaya dapat menggunakan izin ini untuk menonaktifkan kemampuan ponsel yang penting. Izin ini harus digunakan dengan hati-hati, karena mungkin saja menjadikan komponen apl tidak dapat digunakan, tidak konsisten, atau tidak stabil."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"memberi atau mencabut izin"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Memungkinkan aplikasi memberikan atau mencabut izin khusus untuk aplikasi tersebut atau aplikasi lainnya. Aplikasi berbahaya dapat menggunakannya untuk mengakses fitur yang tidak Anda beri izin."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"menyetel apl yang disukai"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Mengizinkan apl memodifikasi apl pilihan Anda. Apl berbahaya dapat diam-diam mengubah apl yang berjalan, menipu apl yang ada untuk mengumpulkan data pribadi dari Anda."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"ubah setelan sistem global"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Setel tanggal"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Setel"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Bawaan"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"BARU: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Tidak perlu izin"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Sembunyikan"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Tampilkan semua"</b></string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 4e4a1aa..9898734 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"アプリのコンポーネントの有効/無効化"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"別のアプリのコンポーネントの有効/無効を変更することをアプリに許可します。この許可を悪意のあるアプリに利用されると、タブレットの重要な機能が無効にされる恐れがあります。アプリのコンポーネントが利用できなくなったり、整合性が取れなくなったり、不安定な状態になったりする恐れがあるので許可には注意が必要です。"</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"別のアプリのコンポーネントの有効/無効を変更することをアプリに許可します。この許可を悪意のあるアプリに利用されると、携帯端末の重要な機能が無効にされる恐れがあります。アプリのコンポーネントが利用できなくなったり、整合性が取れなくなったり、不安定な状態になったりする恐れがあるので許可には注意が必要です。"</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"権限の許可または取り消し"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"このアプリケーションや他のアプリケーションに対して特定の権限を許可したり取り消したりすることをアプリケーションに許可します。悪意のあるアプリケーションがユーザーの許可なく複数の機能にアクセスする恐れがあります。"</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"優先アプリの設定"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"優先アプリを変更することをアプリに許可します。この許可を悪意のあるアプリに利用されると、実行中のアプリが密かに変更され、既存のアプリへのなりすましにより非公開データがだまし取られる恐れがあります。"</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"システムの全般設定の変更"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"日付設定"</string>
     <string name="date_time_set" msgid="5777075614321087758">"設定"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"端末既定"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NEW: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"権限の許可は必要ありません"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"隠す"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"すべて表示"</b></string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index f3cc2fe..5eea0ba 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"앱 구성요소 사용 또는 사용 안함"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"앱이 다른 앱 구성요소 사용 여부를 변경할 수 있도록 허용합니다. 이 경우 악성 앱이 이 기능을 이용하여 중요한 태블릿 기능을 중지시킬 수 있습니다. 이 권한을 허용할 경우 앱 구성요소가 사용 불가능하게 되거나 일관성이 맞지 않거나 불안정해질 수 있으므로 주의해야 합니다."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"앱이 다른 앱 구성요소 사용 여부를 변경할 수 있도록 허용합니다. 이 경우 악성 앱이 이 기능을 이용하여 중요한 휴대전화 기능을 중지시킬 수 있습니다. 이 권한을 허용할 경우 앱 구성요소가 사용 불가능하게 되거나 일관성이 맞지 않거나 불안정해질 수 있으므로 주의해야 합니다."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"권한 승인 또는 취소"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"애플리케이션이 해당 애플리케이션 및 기타 애플리케이션에 대한 특정 권한을 승인 또는 취소하도록 허용합니다. 이 경우 악성 애플리케이션이 승인되지 않은 기능에 액세스할 수 있습니다."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"기본 앱 설정"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"앱이 기본 앱을 수정할 수 있도록 허용합니다. 이 경우 악성 앱이 실행되는 앱을 몰래 변경하고 기존 앱으로 위장하여 사용자의 개인 정보를 수집할 수 있습니다."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"전체 시스템 설정 수정"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"날짜 설정"</string>
     <string name="date_time_set" msgid="5777075614321087758">"설정"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"기본값"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"신규: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"권한 필요 없음"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"숨기기"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"모두 표시"</b></string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index c99d9ba..7ea172f 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"įgalinti programos komponentus arba jų neleisti"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Leidžiama programai pakeisti, ar įgalintas kitos programos komponentas, ar ne. Kenkėjiškos programos gali tai naudoti, kad neleistų svarbių planšetinio kompiuterio funkcijų. Šį leidimą reikia naudoti atsargiai, nes programos komponentai gali tapti nenaudojami, nenuoseklūs ar nestabilūs."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Leidžiama programai pakeisti nustatymą, ar įgalintas kitos programos komponentas, ar ne. Kenkėjiškos programos gali tai naudoti, kad neleistų svarbių telefono funkcijų. Šį leidimą reikia naudoti atsargiai, nes programos komponentai gali tapti nenaudojami, nenuoseklūs ar nestabilūs."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"suteikti arba panaikinti leidimus"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Programai leidžiama suteikti arba panaikinti konkrečius savo arba kitų programų leidimus. Tuo pasinaudoję kenkėjiškos programos gali pasiekti funkcijas, kurių pasiekti joms neleidote."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"nustatyti pageidaujamas programas"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Leidžiama programai keisti jūsų pageidaujamas programas. Kenkėjiškos programos gali nepastebimai pakeisti vykdomas programas, klastodama esamas programas, kad rinktų jūsų privačius duomenis."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"keisti visuotinius sistemos nustatymus"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Nustatyti datą"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Nustatyti"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Numatytasis"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NAUJAS: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nereikia leidimų"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Slėpti"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Rodyti viską"</b></string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index d2233e3..1581341 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"iespējot vai atspējot lietotnes komponentus"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Ļauj lietotnei mainīt to, vai tiek iespējots citas lietotnes komponents. Ļaunprātīgas lietotnes to var izmantot, lai atspējotu svarīgas planšetdatora iespējas. Izmantojiet šo atļauju uzmanīgi, jo pastāv iespēja, ka lietotnes komponenti var kļūt neizmantojami, neatbilstīgi vai nestabili."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Ļauj lietotnei mainīt to, vai tiek iespējots citas lietotnes komponents. Ļaunprātīgas lietotnes to var izmantot, lai atspējotu svarīgas tālruņa iespējas. Izmantojiet šo atļauju uzmanīgi, jo pastāv iespēja, ka lietotnes komponenti var kļūt neizmantojami, neatbilstīgi vai nestabili."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"atļauju piešķiršana vai atsaukšana"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Ļauj lietojumprogrammai piešķirt sev vai citām lietojumprogrammām noteiktas atļaujas un atsaukt tās. Ļaunprātīgas lietojumprogrammas var izmantot šo iespēju, lai piekļūtu funkcijām, kuras neesat atļāvis."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"iestatīt vēlamās lietotnes"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Ļauj lietotnei modificēt jūsu vēlamās lietotnes. Ļaunprātīgas lietotnes var nemanāmi mainīt izmantotās lietotnes, atveidojot esošās lietotnes, lai no jums iegūtu privātus datus."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"pārveidot globālos sistēmas iestatījumus"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Datuma iestatīšana"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Iestatīt"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Noklusējums"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"JAUNA: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Atļaujas nav nepieciešamas."</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Slēpt"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Rādīt visu"</b></string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index bef0b20..ee58558 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"aktivere eller deaktivere appkomponenter"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Lar appen endre hvorvidt en komponent i en annen app er aktivert eller ikke. Ondsinnede apper kan bruke dette til å deaktivere viktige nettbrettfunksjoner. Denne tillatelsen må brukes med forsiktighet, ettersom det er mulig å få appkomponenter inn i en ubrukelig, inkonsistent eller ustabil tilstand."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Lar appen endre hvorvidt en komponent i en annen app er aktivert eller ikke. Ondsinnede apper kan bruke dette til å deaktivere viktige telefonfunksjoner. Denne tillatelsen må brukes med forsiktighet, ettersom det er mulig å få appkomponenter inn i en ubrukelig, inkonsistent eller ustabil tilstand."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"gi eller trekke tilbake tillatelser"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Lar et program gi eller trekke tilbake spesielle tillatelser for eget bruk eller for andre programmer. Skadelige programmer kan bruke dette for å få tilgang til funksjoner de ikke skal ha tilgang til."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"angi foretrukne apper"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Lar appen endre de foretrukne appene dine. Ondsinnede apper kan ubemerket endre apper som kjøres, og forfalske eksisterende apper til å samle private data fra deg."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"endre globale systeminnstillinger"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Angi dato"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Lagre"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Standard"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NY: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Trenger ingen rettigheter"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Skjul"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Vis alle"</b></string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 25833aa..4d0e0fa 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"włączanie lub wyłączanie składników aplikacji"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Pozwala aplikacji na włączenie lub wyłączenie składnika innej aplikacji. Złośliwe aplikacje mogą wykorzystać to uprawnienie do wyłączenia ważnych funkcji tabletu. W przypadku tego uprawnienia należy zachować ostrożność, ponieważ istnieje możliwość wprowadzenia składników aplikacji w stan nieużywalności, niespójności lub niestabilności."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Pozwala aplikacji na włączenie lub wyłączenie składnika innej aplikacji. Złośliwe aplikacje mogą wykorzystać to uprawnienie do wyłączenia ważnych funkcji telefonu. W przypadku tego uprawnienia należy zachować ostrożność, ponieważ istnieje możliwość wprowadzenia składników aplikacji w stan nieużywalności, niespójności lub niestabilności."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"przyznaj lub cofnij uprawnienia"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Pozwala aplikacji na przyznanie lub cofnięcie określonych uprawnień do niej lub do innych aplikacji. Złośliwe aplikacje mogą to wykorzystać, by uzyskać dostęp do nieprzyznanych im funkcji."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"ustawianie preferowanych aplikacji"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Pozwala aplikacji na zmianę Twoich preferowanych aplikacji. Złośliwe aplikacje mogą dyskretnie zmienić uruchamiane aplikacje, podszywając się pod dotychczasowe aplikacje w celu zebrania od Ciebie prywatnych danych."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modyfikowanie ogólnych ustawień systemu"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Ustaw datę"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Ustaw"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Domyślne"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOWE: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nie są wymagane żadne uprawnienia"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ukryj"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Pokaż wszystko"</b></string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 374102c..4826ddf 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"ativar ou desativar os componentes do aplicativo"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Permite que o aplicativo ative ou desative um componente de outro aplicativo. Aplicativos maliciosos podem usar esse recurso para desativar recursos importantes do tablet. Tenha cuidado com essa permissão, pois é possível fazer com que os componentes do aplicativo fiquem inutilizáveis, inconsistentes ou instáveis."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Permite que o aplicativo altere se um componente de outro aplicativo está ativado ou não. Aplicativos maliciosos podem usar esse recurso para desativar recursos importantes do telefone. Deve-se tomar cuidado com essa permissão, uma vez que isso pode deixar os componentes do aplicativo inutilizáveis, inconsistentes ou instáveis."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"conceder ou revogar permissões"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Permite que um aplicativo conceda ou revogue permissões específicas para ele ou outros aplicativos. Aplicativos maliciosos podem usar isso para acessar recursos aos quais você não concedeu permissão."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"definir aplicativos preferidos"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Permite que o aplicativo modifique seus aplicativos preferidos. Aplicativos maliciosos podem alterar os aplicativos que são executados, falsificando seus aplicativos existentes para coletar seus dados particulares."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"modificar configurações globais do sistema"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Definir data"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Definir"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Padrão"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOVO: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nenhuma permissão necessária"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ocultar"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Mostrar todas"</b></string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 5dfa648..662a54e 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"включение и отключение компонентов приложения"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Приложение сможет включать и отключать компоненты других программ. Вредоносные программы смогут таким образом отключать важные функции планшетного ПК. Используйте это разрешение с особой осторожностью, чтобы случайно не нарушить работу компонентов приложения."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Приложение сможет включать и отключать компоненты других программ. Вредоносные программы смогут таким образом отключать важные функции телефона. Используйте это разрешение с особой осторожностью, чтобы случайно не нарушить работу компонентов приложения."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"предоставление и отзыв разрешений"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Позволяет приложению предоставлять и отзывать разрешения самому себе и другим программам. Вредоносные приложения могут использовать эту функцию для получения прав, которых вы им не предоставляли."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"настройка предпочтительных приложений"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Приложение сможет изменять предпочтительные приложения. Вредоносные программы смогут незаметно изменять запускаемые вами программы и собирать ваши личные данные от имени существующих приложений."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"изменять общие настройки системы"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Настройка даты"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Установить"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"По умолчанию"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"НОВОЕ: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Не требуется разрешений"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Скрыть"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Показать все"</b></string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index b65f535..98b829f 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"povoliť alebo zakázať súčasti aplikácie"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Umožňuje aplikácii zmeniť to, či je súčasť inej aplikácie povolená alebo zakázaná. Škodlivé aplikácie môžu pomocou tohto nastavenia zakázať dôležité funkcie tabletu. S týmto povolením musíte zaobchádzať opatrne, pretože súčasti aplikácie môžete dostať do nepoužiteľného, nekonzistentného alebo nestabilného stavu."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Umožňuje aplikácii zmeniť to, či je súčasť inej aplikácie povolená alebo zakázaná. Škodlivé aplikácie môžu pomocou tohto nastavenia zakázať dôležité funkcie telefónu. S týmto povolením musíte zaobchádzať opatrne, pretože súčasti aplikácie môžete dostať do nepoužiteľného, nekonzistentného alebo nestabilného stavu."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"povoliť alebo zakázať povolenia"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Umožňuje aplikácii povoliť alebo zakázať konkrétne povolenia pre seba alebo iné aplikácie. Škodlivé aplikácie môžu použiť túto možnosť na pristupovanie k funkciám, ktoré ste im nepovolili."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"nastaviť preferované aplikácie"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Umožňuje aplikácii zmeniť vaše preferované aplikácie. Škodlivé aplikácie môžu v tichosti zmeniť spustené aplikácie a oklamať existujúce aplikácie, aby zhromažďovali vaše súkromné údaje."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"zmeny globálnych nastavení systému"</string>
@@ -1010,8 +1008,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Nastaviť dátum"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Nastaviť"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Predvolené"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOVINKA: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Nevyžadujú sa žiadne oprávnenia."</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Skryť"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Zobraziť všetky"</b></string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 83515dd..9d99013 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"omogočanje ali onemogočanje komponent programa"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Programu omogoča, da spremeni, ali je komponenta drugega programa omogočena ali ne. Zlonamerni programi lahko to uporabijo za onemogočanje pomembnih zmožnosti tabličnega računalnika. Pri dodeljevanju dovoljenja je treba biti previden, saj lahko komponente programa nastavite tako, da jih ni mogoče uporabiti, da niso dosledne ali da niso stabilne."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Programu omogoča, da spremeni, ali je komponenta drugega programa omogočena ali ne. Zlonamerni programi lahko to uporabijo za onemogočanje pomembnih zmožnosti telefona. Pri dodeljevanju dovoljenja je treba biti previden, saj lahko komponente programa nastavite tako, da jih ni mogoče uporabiti, da niso dosledne ali da niso stabilne."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"dodeljevanje ali preklic dovoljenj"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Programu omogoča dodeljevanje ali preklic posebnih dovoljenj zanj ali za druge programe. Zlonamerni programi lahko to uporabijo za dostop do funkcij, za katere jim niste dodelili pravic."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"nastavitev prednostnih programov"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Programu omogoča spreminjanje priljubljenih programov. Zlonamerni programi lahko s tem neopazno spremenijo programe, ki se izvajajo, tako da se izdajajo za obstoječe programe in zbirajo osebne podatke."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"spreminjanje splošnih nastavitev sistema"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Nastavi datum"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Nastavi"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Privzeto"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"NOVO: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Ni zahtevanih dovoljenj"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Skrij"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Pokaži vse"</b></string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 2992fbe..a9e7b9b 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"омогућавање или онемогућавање компоненти апликације"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Дозвољава апликацији да промени да ли је компонента друге апликације омогућена или онемогућена. Злонамерне апликације могу то да искористе да онемогуће важне функције таблета. Треба бити опрезан при додељивању ове дозволе, јер компоненте апликација могу постати неупотребљиве, непоуздане или нестабилне."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Дозвољава апликацији да промени да ли је компонента друге апликације омогућена или онемогућена. Злонамерне апликације могу то да искористе да онемогуће важне функције телефона. Треба бити опрезан при додељивању ове дозволе, јер компоненте апликација могу да постану неупотребљиве, непоуздане или нестабилне."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"додела или опозив дозвола"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Омогућава апликацији да додели или опозове посебне дозволе за њу или друге апликације. Злонамерне апликације могу то да користе да би приступале функцијама које им нисте одобрили."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"подешавање жељених апликација"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Дозвољава апликацији да мења омиљене апликације. Злонамерне апликације могу без обавештења да промене апликације које су покренуте и да преко њих прикупљају ваше приватне податке."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"измена глобалних подешавања система"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Подешавање датума"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Подеси"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Подразумевано"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"НОВО: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Није потребна ниједна дозвола"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Сакриј"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Прикажи све"</b></string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 329a807..a787a2a 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"เปิดหรือปิดใช้งานคอมโพเนนต์ของแอปพลิเคชัน"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"อนุญาตให้แอปพลิเคชันเปลี่ยนแปลงการเปิดใช้งานส่วนประกอบของแอปพลิเคชันอื่น แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ปิดใช้งานความสามารถของแท็บเล็ตที่สำคัญ ต้องใช้ความระมัดระวังสำหรับการอนุญาตนี้ เนื่องจากอาจทำให้ส่วนประกอบต่างๆ ของแอปพลิเคชันไม่สามารถใช้งาน ไม่สอดคล้อง หรือมีสถานะที่ไม่เสถียร"</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"อนุญาตให้แอปพลิเคชันเปลี่ยนแปลงการเปิดใช้งานส่วนประกอบของแอปพลิเคชันอื่น แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ปิดใช้งานความสามารถที่สำคัญของโทรศัพท์ ต้องใช้ความระมัดระวังสำหรับการอนุญาตนี้เนื่องจากอาจทำให้ส่วนประกอบต่างๆ ของแอปพลิเคชันไม่สามารถใช้งาน ไม่สอดคล้อง หรือมีสถานะที่ไม่เสถียร"</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"อนุญาตหรือยกเลิกการอนุญาต"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"อนุญาตให้แอปพลิเคชันให้หรือยกเลิกการอนุญาตบางอย่างของตัวเองหรือแอปพลิเคชันอื่น แอปพลิเคชันที่เป็นอันตรายอาจใช้การทำงานนี้ในการเข้าถึงคุณลักษณะที่คุณไม่อนุญาต"</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"ตั้งค่าแอปพลิเคชันที่ต้องการ"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"อนุญาตให้แอปพลิเคชันแก้ไขแอปพลิเคชันต่างๆ ที่คุณชอบใช้ แอปพลิเคชันที่เป็นอันตรายอาจจะแอบเปลี่ยนแอปพลิเคชันที่กำลังทำงาน ปลอมแปลงเป็นแอปพลิเคชันที่มีอยู่ของคุณเพื่อรวบรวมข้อมูลส่วนบุคคลจากคุณ"</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"แก้ไขการตั้งค่าระบบสากล"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"ตั้งวันที่"</string>
     <string name="date_time_set" msgid="5777075614321087758">"ตั้งค่า"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"เริ่มต้น"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"ใหม่: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"ไม่ต้องการการอนุญาต"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"ซ่อน"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"แสดงทั้งหมด"</b></string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 40e8cc5..99731af 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"paganahin o huwag paganahin ang mga bahagi ng app"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Pinapayagan ang app na baguhin kung ang bahagi ng isa pang app ay pinagana o hindi. Maaari itong gamitin ng nakakahamak na apps upang huwag paganahin ang mahahalagang kakayahan ng tablet. Dapat na mayroong pag-iingat sa pahintulot na ito, dahil posibleng mailagay ng mga bahagi ng app sa isang hindi nagagamit, pabagu-bago, o hindi matatag na katayuan."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Pinapayagan ang app na baguhin kung ang isang bahagi ng isa pang app ay pinapagana o hindi. Maaari itong gamitin ng nakakahamak na apps upang huwag paganahin ang mga mahalagang kakayahan ng telepono. Dapat na mayroong pag-iingat sa pahintulot na ito, dahil posibleng mailagay ang mga bahagi ng app sa isang hindi nagagamit, pabagu-bago, o hindi matatag na katayuan."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"ibigay o bawiin ang mga pahintulot"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Binibigyang-daan ang isang application na ibigay o bawiin ang mga tukoy na pahintulot para dito o sa iba pang mga application. Maaari itong gamitin ng mga nakakapahamak na application upang i-access ang mga tampok na hindi mo ibinigay sa mga ito."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"itakda ang gustong apps"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Pinapayagan ang app na baguhin ang iyong gustong apps. Maaaring tahimik na baguhin ng nakakahamak na apps ang apps na tumatakbo, na dinadaya ang iyong umiiral nang apps upang mangolekta ng pribadong data mula sa iyo."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"baguhin ang mga setting ng global system"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Itakda ang petsa"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Itakda"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Default"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"BAGO: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Walang mga kinakailangang pahintulot"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Itago"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Ipakita lahat"</b></string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index fd0beaf..4d3c8537 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"вмикати чи вимикати компоненти програми"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Дозволяє програмі змінювати статус ввімкнення чи вимкнення компонента іншої програми. Шкідливі програми можуть використовувати це для вимкнення важливих характеристик планшетного ПК. З цим типом дозволу треба поводитися обережно, оскільки компоненти програми можуть стати непридатними, невідповідними чи нестабільними."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Дозволяє програмі змінювати статус ввімкнення чи вимкнення компонента іншої програми. Шкідливі програми можуть використовувати це для вимкнення важливих характеристик телефону. З цим типом дозволу треба поводитися обережно, оскільки компоненти програми можуть стати непридатними, невідповідними чи нестабільними."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"надавати або скасовувати дозволи"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Дозволяє програмі надавати або скасовувати певні дозволи для себе чи інших програм. Шкідливі програми можуть використовувати це для доступу до функцій, якого ви їм не надавали."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"установлювати потрібні програми"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Дозволяє програмі змінювати ваші вибрані програми. Шкідливі програми можуть непомітно змінювати запущені програми, примушуючи існуючі програми оманливим шляхом збирати ваші особисті дані."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"змін. загальні налашт-ня сист."</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Установити дату"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Застосувати"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"За умовч."</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"НОВИЙ: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Дозвіл не потрібний"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Сховати"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Показ. всі"</b></string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 37af091..c9b200d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -313,10 +313,8 @@
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"bật hoặc tắt cấu phần ứng dụng"</string>
     <string name="permdesc_changeComponentState" product="tablet" msgid="8887435740982237294">"Cho phép ứng dụng thay đổi việc có bật cấu phần của ứng dụng khác hay không. Ứng dụng độc hại có thể sử dụng quyền này để vô hiệu hóa những tính năng quan trọng của máy tính bảng. Phải cẩn trọng khi sử dụng quyền này vì quyền này có thể khiến các cấu phần rơi vào trạng thái không sử dụng được, không đồng nhất hoặc không ổn định."</string>
     <string name="permdesc_changeComponentState" product="default" msgid="1827232484416505615">"Cho phép ứng dụng thay đổi việc có bật cấu phần của một ứng dụng khác hay không. Ứng dụng độc hại có thể sử dụng quyền này để tắt những tính năng quan trọng của điện thoại. Phải sử dụng quyền này thận trọng vì có thể khiến các cấu phần của ứng dụng rơi vào trạng thái không thể sử dụng được, không đồng nhất hoặc không ổn định."</string>
-    <!-- no translation found for permlab_grantRevokePermissions (4627315351093508795) -->
-    <skip />
-    <!-- no translation found for permdesc_grantRevokePermissions (4088642654085850662) -->
-    <skip />
+    <string name="permlab_grantRevokePermissions" msgid="4627315351093508795">"cấp hoặc thu hồi quyền"</string>
+    <string name="permdesc_grantRevokePermissions" msgid="4088642654085850662">"Cho phép ứng dụng cấp hoặc thu hồi quyền cụ thể đối với ứng dụng đó hoặc các ứng dụng khác. Các ứng dụng độc hại có thể lợi dụng điều này để truy cập các tính năng mà bạn không cấp."</string>
     <string name="permlab_setPreferredApplications" msgid="8463181628695396391">"đặt ứng dụng ưa thích"</string>
     <string name="permdesc_setPreferredApplications" msgid="4973986762241783712">"Cho phép ứng dụng sửa đổi ứng dụng ưa thích của bạn. Ứng dụng độc hại có thể ngầm thay đổi các ứng dụng đã được chạy, giả mạo các ứng dụng hiện có để thu thập dữ liệu cá nhân của bạn."</string>
     <string name="permlab_writeSettings" msgid="1365523497395143704">"sửa đổi cài đặt hệ thống chung"</string>
@@ -1008,8 +1006,7 @@
     <string name="date_picker_dialog_title" msgid="5879450659453782278">"Đặt ngày"</string>
     <string name="date_time_set" msgid="5777075614321087758">"Đặt"</string>
     <string name="default_permission_group" msgid="2690160991405646128">"Mặc định"</string>
-    <!-- no translation found for perms_new_perm_prefix (8257740710754301407) -->
-    <skip />
+    <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ffffa3a3">"MỚI: "</font></string>
     <string name="no_permissions" msgid="7283357728219338112">"Không yêu cầu quyền"</string>
     <string name="perms_hide" msgid="7283915391320676226"><b>"Ẩn"</b></string>
     <string name="perms_show_all" msgid="2671791163933091180"><b>"Hiển thị tất cả"</b></string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 806406d..8e5b509 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -646,9 +646,29 @@
     <bool translatable="false" name="skip_restoring_network_selection">false</bool>
 
     <!-- Maximum number of database connections opened and managed by framework layer
-         to handle queries on each database. -->
+         to handle queries on each database when using Write-Ahead Logging. -->
     <integer name="db_connection_pool_size">4</integer>
 
+    <!-- The default journal mode to use use when Write-Ahead Logging is not active.
+         Choices are: OFF, DELETE, TRUNCATE, PERSIST and MEMORY.
+         PERSIST may improve performance by reducing how often journal blocks are
+         reallocated (compared to truncation) resulting in better data block locality
+         and less churn of the storage media. -->
+    <string name="db_default_journal_mode">TRUNCATE</string>
+
+    <!-- Maximum size of the persistent journal file in bytes.
+         If the journal file grows to be larger than this amount then SQLite will
+         truncate it after committing the transaction. -->
+    <integer name="db_journal_size_limit">524288</integer>
+
+    <!-- The database synchronization mode.
+         Choices are: FULL, NORMAL, OFF. -->
+    <string name="db_sync_mode">FULL</string>
+
+    <!-- The Write-Ahead Log auto-checkpoint interval in database pages.
+         The log is checkpointed automatically whenever it exceeds this many pages. -->
+    <integer name="db_wal_autocheckpoint">1</integer>
+
     <!-- Max space (in MB) allocated to DownloadManager to store the downloaded
          files if they are to be stored in DownloadManager's data dir,
          which typically is /data/data/com.android.providers.downloads/files -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 94a671d..2c80fb7 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -250,6 +250,8 @@
   <java-symbol type="integer" name="config_wifi_framework_scan_interval" />
   <java-symbol type="integer" name="config_wifi_supplicant_scan_interval" />
   <java-symbol type="integer" name="db_connection_pool_size" />
+  <java-symbol type="integer" name="db_journal_size_limit" />
+  <java-symbol type="integer" name="db_wal_autocheckpoint" />
   <java-symbol type="integer" name="max_action_buttons" />
 
   <java-symbol type="color" name="tab_indicator_text_v4" />
@@ -438,6 +440,8 @@
   <java-symbol type="string" name="day_of_week_shortest_thursday" />
   <java-symbol type="string" name="day_of_week_shortest_tuesday" />
   <java-symbol type="string" name="day_of_week_shortest_wednesday" />
+  <java-symbol type="string" name="db_default_journal_mode" />
+  <java-symbol type="string" name="db_sync_mode" />
   <java-symbol type="string" name="decline" />
   <java-symbol type="string" name="default_permission_group" />
   <java-symbol type="string" name="default_text_encoding" />
@@ -3521,6 +3525,8 @@
 
   <public type="attr" name="textDirection"/>
 
+  <public type="attr" name="layoutDirection" />
+
   <public type="attr" name="paddingStart"/>
   <public type="attr" name="paddingEnd"/>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f548165..c8df649 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2263,6 +2263,10 @@
          content providers from outside an ApplicationThread. [CHAR LIMIT=NONE] -->
     <string name="permdesc_accessContentProvidersExternally">Allows the holder to access content
      providers from the shell. Should never be needed for normal apps.</string>
+    <!-- Title of an application permission which allows the application to suggest that now is a bad time to reboot the device in order to apply an OTA.  [CHAR LIMIT=40] -->
+    <string name="permlab_updateLock">discourage automatic device updates</string>
+    <!-- Description of an application permission which allows the application to suggest that now is a bad time to reboot the device in order to apply an OTA.  [CHAR LIMIT=NONE] -->
+    <string name="permdesc_updateLock">Allows the holder to offer information to the system about when would be a good time for a noninteractive reboot to upgrade the device.</string>
 
     <!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Text in the save password dialog, asking if the browser should remember a password. -->
     <string name="save_password_message">Do you want the browser to remember this password?</string>
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index d375d4c..259f15f 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -94,6 +94,7 @@
      * Control Wifi States
      */
     public WifiManager mWifiManager;
+    public WifiManager.Channel mChannel;
 
     /*
      * Verify connectivity state
@@ -240,7 +241,7 @@
         mCM = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
         // Get an instance of WifiManager
         mWifiManager =(WifiManager)getSystemService(Context.WIFI_SERVICE);
-        mWifiManager.asyncConnect(this, new WifiServiceHandler());
+        mChannel = mWifiManager.initialize(mContext, mContext.getMainLooper(), null);
 
         initializeNetworkStates();
 
@@ -594,7 +595,14 @@
                         log("found " + ssid + " in the scan result list");
                         log("retry: " + retry);
                         foundApInScanResults = true;
-                        mWifiManager.connectNetwork(config);
+                        mWifiManager.connect(mChannel, config,
+                                new WifiManager.ActionListener() {
+                                    public void onSuccess() {
+                                    }
+                                    public void onFailure(int reason) {
+                                        log("connect failure " + reason);
+                                    }
+                                });
                         break;
                    }
                 }
@@ -641,7 +649,13 @@
         for (WifiConfiguration wifiConfig: wifiConfigList) {
             log("remove wifi configuration: " + wifiConfig.networkId);
             int netId = wifiConfig.networkId;
-            mWifiManager.forgetNetwork(netId);
+            mWifiManager.forget(mChannel, netId, new WifiManager.ActionListener() {
+                    public void onSuccess() {
+                    }
+                    public void onFailure(int reason) {
+                        log("Failed to forget " + reason);
+                    }
+                });
         }
         return true;
     }
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
index d33a445..8d73bc0 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java
@@ -63,6 +63,7 @@
     private ConnectivityManagerTestActivity mAct;
     private ConnectivityManagerTestRunner mRunner;
     private WifiManager mWifiManager = null;
+    private WifiManager.Channel mChannel;
     private Set<WifiConfiguration> enabledNetworks = null;
 
     public WifiConnectionTest() {
@@ -76,7 +77,8 @@
         mWifiManager = (WifiManager) mRunner.getContext().getSystemService(Context.WIFI_SERVICE);
 
         mAct = getActivity();
-        mWifiManager.asyncConnect(mAct, new WifiServiceHandler());
+        mChannel = mWifiManager.initialize(mAct, mAct.getMainLooper(), null);
+
         networks = mAct.loadNetworkConfigurations();
         if (DEBUG) {
             printNetworkConfigurations();
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
index 8d778c4..e799d15 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
@@ -74,6 +74,7 @@
     private int mWifiState;
     private NetworkInfo mWifiNetworkInfo;
     private WifiManager mWifiManager;
+    private WifiManager.Channel mChannel;
     private Context mContext;
     // Verify connectivity state
     private static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE + 1;
@@ -114,7 +115,7 @@
 
         // Get an instance of WifiManager
         mWifiManager =(WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
-        mWifiManager.asyncConnect(mContext, new WifiServiceHandler());
+        mChannel = mWifiManager.initialize(mContext, mContext.getMainLooper(), null);
 
         mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
 
@@ -567,7 +568,14 @@
                         Log.v(LOG_TAG, "Found " + ssid + " in the scan result list.");
                         Log.v(LOG_TAG, "Retry: " + retry);
                         foundApInScanResults = true;
-                        mWifiManager.connectNetwork(config);
+                        mWifiManager.connect(mChannel, config, new WifiManager.ActionListener() {
+                                public void onSuccess() {
+                                }
+                                public void onFailure(int reason) {
+                                    Log.e(LOG_TAG, "connect failed " + reason);
+                                }
+                            });
+
                         break;
                     }
                 }
@@ -614,7 +622,13 @@
         for (WifiConfiguration wifiConfig: wifiConfigList) {
             Log.v(LOG_TAG, "Remove wifi configuration: " + wifiConfig.networkId);
             int netId = wifiConfig.networkId;
-            mWifiManager.forgetNetwork(netId);
+            mWifiManager.forget(mChannel, netId, new WifiManager.ActionListener() {
+                    public void onSuccess() {
+                    }
+                    public void onFailure(int reason) {
+                        Log.e(LOG_TAG, "forget failed " + reason);
+                    }
+                });
         }
         return true;
     }
@@ -723,4 +737,4 @@
         }
         return false;
     }
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/src/android/util/LocaleUtilTest.java b/core/tests/coretests/src/android/util/LocaleUtilTest.java
index ff3d539..0ca6043 100644
--- a/core/tests/coretests/src/android/util/LocaleUtilTest.java
+++ b/core/tests/coretests/src/android/util/LocaleUtilTest.java
@@ -22,7 +22,8 @@
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetNew;
 
-import static android.util.LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
+import static android.view.View.LAYOUT_DIRECTION_LTR;
+import static android.view.View.LAYOUT_DIRECTION_RTL;
 
 public class LocaleUtilTest extends AndroidTestCase {
 
@@ -32,168 +33,168 @@
         args = {Locale.class}
     )
     public void testGetLayoutDirectionFromLocale() {
-        assertEquals(TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
                 LocaleUtil.getLayoutDirectionFromLocale(null));
 
-        assertEquals(TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.ENGLISH));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.CANADA));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.CANADA_FRENCH));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.FRANCE));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.FRENCH));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.GERMAN));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.GERMANY));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.ITALIAN));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.ITALY));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.UK));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.US));
 
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.ROOT));
 
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.CHINA));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.CHINESE));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.JAPAN));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.JAPANESE));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.KOREA));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.KOREAN));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.PRC));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.SIMPLIFIED_CHINESE));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.TAIWAN));
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(Locale.TRADITIONAL_CHINESE));
 
         Locale locale = new Locale("ar");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "AE");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "BH");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "DZ");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "EG");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "IQ");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "JO");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "KW");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "LB");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "LY");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "MA");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "OM");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "QA");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "SA");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "SD");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "SY");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "TN");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ar", "YE");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("fa");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("fa", "AF");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("fa", "IR");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("iw");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("iw", "IL");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("he");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("he", "IL");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("pa_Arab");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("pa_Arab", "PK");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("ps");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ps", "AF");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("ur");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ur", "IN");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("ur", "PK");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         locale = new Locale("uz_Arab");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
         locale = new Locale("uz_Arab", "AF");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_RTL,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
 
         // Locale without a real language
         locale = new Locale("zz");
-        assertEquals(LocaleUtil.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
+        assertEquals(LAYOUT_DIRECTION_LTR,
             LocaleUtil.getLayoutDirectionFromLocale(locale));
     }
 }
diff --git a/docs/html/design/building-blocks/buttons.html b/docs/html/design/building-blocks/buttons.html
index cc43fcb..9f9652f9 100644
--- a/docs/html/design/building-blocks/buttons.html
+++ b/docs/html/design/building-blocks/buttons.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -110,7 +114,8 @@
   <img src="../static/content/buttons_basic.png">
 </div>
 
-<h2>Basic Buttons</h2>
+<h2 id="basic">Basic Buttons</h2>
+
 <p>Basic buttons are traditional buttons with borders and background. Android supports two styles for
 basic buttons: default and small. Default buttons have slightly larger font size and are optimized
 for display outside of form content. Small buttons are intended for display alongside other content.
@@ -131,7 +136,8 @@
   </div>
 </div>
 
-<h2>Borderless Buttons</h2>
+<h2 id="borderless">Borderless Buttons</h2>
+
 <p>Borderless buttons resemble basic buttons except that they have no borders or background. You can
 use borderless buttons with both icons and text. Borderless buttons are visually more lightweight
 than basic buttons and integrate nicely with other content.</p>
diff --git a/docs/html/design/building-blocks/dialogs.html b/docs/html/design/building-blocks/dialogs.html
index fc00780..f03a57a 100644
--- a/docs/html/design/building-blocks/dialogs.html
+++ b/docs/html/design/building-blocks/dialogs.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/building-blocks/grid-lists.html b/docs/html/design/building-blocks/grid-lists.html
index b4cfdcb..3f60216 100644
--- a/docs/html/design/building-blocks/grid-lists.html
+++ b/docs/html/design/building-blocks/grid-lists.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -159,7 +163,7 @@
 </div>
 
 
-<h2 id="with_labels">Grid List with Labels</h2>
+<h2 id="with-labels">Grid List with Labels</h2>
 
 <p>Use labels to display additional contextual information for your grid list items.</p>
 
diff --git a/docs/html/design/building-blocks/index.html b/docs/html/design/building-blocks/index.html
index c99d85c..029cabf 100644
--- a/docs/html/design/building-blocks/index.html
+++ b/docs/html/design/building-blocks/index.html
@@ -93,6 +93,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/building-blocks/lists.html b/docs/html/design/building-blocks/lists.html
index 914ae9e..dfd13d9 100644
--- a/docs/html/design/building-blocks/lists.html
+++ b/docs/html/design/building-blocks/lists.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/building-blocks/pickers.html b/docs/html/design/building-blocks/pickers.html
index 4c95a9f..fc9989c 100644
--- a/docs/html/design/building-blocks/pickers.html
+++ b/docs/html/design/building-blocks/pickers.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -123,7 +127,7 @@
   </div>
 </div>
 
-<h2>Date and time pickers</h2>
+<h2 id="date-time">Date and time pickers</h2>
 
 <p>Android provides these as ready-to-use dialogs. Each picker is a dialog with a set of controls for
 entering the parts of the date (month, day, year) or time (hour, minute, AM/PM). Using these in your
diff --git a/docs/html/design/building-blocks/progress.html b/docs/html/design/building-blocks/progress.html
index 7aae913..32183bc 100644
--- a/docs/html/design/building-blocks/progress.html
+++ b/docs/html/design/building-blocks/progress.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -105,7 +109,8 @@
 
 <p>When an operation of interest to the user is taking place over a relatively long period of time,
 provide visual feedback that it's still happening and in the process of being completed.</p>
-<h2>Progress</h2>
+<h2 id="progress">Progress</h2>
+
 <p>If you know the percentage of the operation that has been completed, use a determinate progress bar
 to give the user a sense of how much longer it will take.</p>
 
diff --git a/docs/html/design/building-blocks/scrolling.html b/docs/html/design/building-blocks/scrolling.html
index 3f1167c..3599a97 100644
--- a/docs/html/design/building-blocks/scrolling.html
+++ b/docs/html/design/building-blocks/scrolling.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -104,7 +108,8 @@
 
 <p>Scrolling allows the user to navigate to content in the overflow using a swipe gesture. The
 scrolling speed is proportional to the speed of the gesture.</p>
-<h2>Scroll Indicator</h2>
+<h2 id="indicator">Scroll Indicator</h2>
+
 <p>Appears during scrolling to indicate what portion of the content is currently in view.</p>
 
 <div class="framed-galaxynexus-land-span-13">
@@ -118,7 +123,8 @@
   <div class="video-instructions">&nbsp;</div>
 </div>
 
-<h2>Index Scrolling</h2>
+<h2 id="index-scrolling">Index Scrolling</h2>
+
 <p>In addition to traditional scrolling, a long alphabetical list can also offer index scrolling: a way
 to quickly navigate to the items that begin with a particular letter. With index scrolling, a scroll
 indicator appears even when the user isn't scrolling. Touching or dragging it causes the current
diff --git a/docs/html/design/building-blocks/seek-bars.html b/docs/html/design/building-blocks/seek-bars.html
index 84cf5d2..aef1823 100644
--- a/docs/html/design/building-blocks/seek-bars.html
+++ b/docs/html/design/building-blocks/seek-bars.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/building-blocks/spinners.html b/docs/html/design/building-blocks/spinners.html
index bf21fe8..5ef9d04 100644
--- a/docs/html/design/building-blocks/spinners.html
+++ b/docs/html/design/building-blocks/spinners.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/building-blocks/switches.html b/docs/html/design/building-blocks/switches.html
index 3d7bc9c..09af540 100644
--- a/docs/html/design/building-blocks/switches.html
+++ b/docs/html/design/building-blocks/switches.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -104,7 +108,8 @@
 
 <p>Switches allow the user to select options. There are three kinds of switches: checkboxes, radio
 buttons, and on/off switches.</p>
-<h2>Checkboxes</h2>
+<h2 id="checkboxes">Checkboxes</h2>
+
 <p>Checkboxes allow the user to select multiple options from a set. Avoid using a single checkbox to
 turn an option off or on. Instead, use an on/off switch.</p>
 
@@ -112,7 +117,8 @@
   <img src="../static/content/switches_checkboxes.png">
 </div>
 
-<h2>Radio Buttons</h2>
+<h2 id="radio-buttons">Radio Buttons</h2>
+
 <p>Radio buttons allow the user to select one option from a set. Use radio buttons for exclusive
 selection if you think that the user needs to see all available options side-by-side. Otherwise,
 consider a spinner, which uses less space.</p>
@@ -121,7 +127,8 @@
   <img src="../static/content/switches_radios.png">
 </div>
 
-<h2>On/off Switches</h2>
+<h2 id="switches">On/off Switches</h2>
+
 <p>On/off switches toggle the state of a single settings option.</p>
 
 <div style="text-align: center">
diff --git a/docs/html/design/building-blocks/tabs.html b/docs/html/design/building-blocks/tabs.html
index c094cce..d4b0e52 100644
--- a/docs/html/design/building-blocks/tabs.html
+++ b/docs/html/design/building-blocks/tabs.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/building-blocks/text-fields.html b/docs/html/design/building-blocks/text-fields.html
index 6496fa5..b9ec42d 100644
--- a/docs/html/design/building-blocks/text-fields.html
+++ b/docs/html/design/building-blocks/text-fields.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -141,7 +145,7 @@
   </div>
 </div>
 
-<h2>Text Selection</h2>
+<h2 id="text-selection">Text Selection</h2>
 
 <p>Users can select any word in a text field with a long press. This action triggers a text selection
 mode that facilitates extending the selection or choosing an action to perform on the selected text.
diff --git a/docs/html/design/downloads/index.html b/docs/html/design/downloads/index.html
new file mode 100644
index 0000000..f910b29
--- /dev/null
+++ b/docs/html/design/downloads/index.html
@@ -0,0 +1,278 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <title>
+
+Android Design - Downloads
+    </title>
+    <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
+    <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Roboto:regular,medium,thin,italic,mediumitalic">
+    <link rel="stylesheet" href="../static/yui-3.3.0-reset-min.css">
+    <link rel="stylesheet" href="../static/default.css">
+
+  </head>
+  <body>
+
+    <div id="page-container">
+
+      <div id="page-header"><a href="../index.html">Android Design</a></div>
+
+      <div id="main-row">
+
+        <ul id="nav">
+
+          <li class="nav-section">
+            <div class="nav-section-header"><a href="../index.html">Get Started</a></div>
+            <ul>
+              <li><a href="../get-started/creative-vision.html">Creative Vision</a></li>
+              <li><a href="../get-started/principles.html">Design Principles</a></li>
+              <li><a href="../get-started/ui-overview.html">UI Overview</a></li>
+            </ul>
+          </li>
+
+          <li class="nav-section">
+            <div class="nav-section-header"><a href="../style/index.html">Style</a></div>
+            <ul>
+              <li><a href="../style/devices-displays.html">Devices and Displays</a></li>
+              <li><a href="../style/themes.html">Themes</a></li>
+              <li><a href="../style/touch-feedback.html">Touch Feedback</a></li>
+              <li><a href="../style/metrics-grids.html">Metrics and Grids</a></li>
+              <li><a href="../style/typography.html">Typography</a></li>
+              <li><a href="../style/color.html">Color</a></li>
+              <li><a href="../style/iconography.html">Iconography</a></li>
+              <li><a href="../style/writing.html">Writing Style</a></li>
+            </ul>
+          </li>
+
+          <li class="nav-section">
+            <div class="nav-section-header"><a href="../patterns/index.html">Patterns</a></div>
+            <ul>
+              <li><a href="../patterns/new-4-0.html">New in Android 4.0</a></li>
+              <li><a href="../patterns/gestures.html">Gestures</a></li>
+              <li><a href="../patterns/app-structure.html">App Structure</a></li>
+              <li><a href="../patterns/navigation.html">Navigation</a></li>
+              <li><a href="../patterns/actionbar.html">Action Bar</a></li>
+              <li><a href="../patterns/multi-pane-layouts.html">Multi-pane Layouts</a></li>
+              <li><a href="../patterns/swipe-views.html">Swipe Views</a></li>
+              <li><a href="../patterns/selection.html">Selection</a></li>
+              <li><a href="../patterns/notifications.html">Notifications</a></li>
+              <li><a href="../patterns/compatibility.html">Compatibility</a></li>
+              <li><a href="../patterns/pure-android.html">Pure Android</a></li>
+            </ul>
+          </li>
+
+          <li class="nav-section">
+            <div class="nav-section-header"><a href="../building-blocks/index.html">Building Blocks</a></div>
+            <ul>
+              <li><a href="../building-blocks/tabs.html">Tabs</a></li>
+              <li><a href="../building-blocks/lists.html">Lists</a></li>
+              <li><a href="../building-blocks/grid-lists.html">Grid Lists</a></li>
+              <li><a href="../building-blocks/scrolling.html">Scrolling</a></li>
+              <li><a href="../building-blocks/spinners.html">Spinners</a></li>
+              <li><a href="../building-blocks/buttons.html">Buttons</a></li>
+              <li><a href="../building-blocks/text-fields.html">Text Fields</a></li>
+              <li><a href="../building-blocks/seek-bars.html">Seek Bars</a></li>
+              <li><a href="../building-blocks/progress.html">Progress &amp; Activity</a></li>
+              <li><a href="../building-blocks/switches.html">Switches</a></li>
+              <li><a href="../building-blocks/dialogs.html">Dialogs</a></li>
+              <li><a href="../building-blocks/pickers.html">Pickers</a></li>
+            </ul>
+          </li>
+
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
+          <li>
+            <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
+          </li>
+
+        </ul>
+
+        <div id="content">
+
+          
+          <div class="layout-content-row content-header">
+            <div class="layout-content-col span-9">
+              <h2>Downloads</h2>
+            </div>
+            <div class="paging-links layout-content-col span-4">
+              <a href="#" class="prev-page-link">Previous</a>
+              <a href="#" class="next-page-link">Next</a>
+            </div>
+          </div>
+          
+
+          
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-9">
+
+<p>Want everything? We've bundled all the downloads available on Android Design into a single ZIP file.
+You can also download individual files listed below.</p>
+<p>You may use these materials without restriction in your apps and to develop your apps.</p>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+<p>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Downloads_20120229.zip">Download All</a>
+</p>
+
+  </div>
+</div>
+
+<h2 id="stencils">Stencils and Sources</h2>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-5">
+
+<p>Drag and drop your way to beautifully designed Ice Cream Sandwich apps. The stencils feature the
+rich typography, colors, interactive controls, and icons found throughout Android 4.0, along with
+phone and tablet outlines to frame your creations. Source files for icons and controls are also
+available.</p>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+    <img src="../static/content/downloads_stencils.png">
+
+  </div>
+  <div class="layout-content-col span-4">
+
+<p>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Fireworks_Stencil_20120229.png">Adobe&reg; Fireworks&reg; PNG Stencil</a>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_OmniGraffle_Stencil_20120229.graffle">Omni&reg; OmniGraffle&reg; Stencil</a>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Holo_Widgets_20120229.zip">Adobe&reg; Photoshop&reg; Sources</a>
+</p>
+
+  </div>
+</div>
+
+<h2 id="action-bar-icon-pack">Action Bar Icon Pack</h2>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-5">
+
+<p>Action bar icons are graphic buttons that represent the most important actions people can take
+within your app. <a href="../style/iconography.html">More on Action Bar Iconography</a></p>
+<p>The download package includes icons that are scaled for various screen densities and suitable for
+use with the Holo Light and Holo Dark themes. The package also includes unstyled icons that you can
+modify to match your theme, plus source files.</p>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+    <img src="../static/content/iconography_actionbar_style.png">
+
+  </div>
+  <div class="layout-content-col span-4">
+
+<p>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Icons_20120229.zip">Action Bar Icon Pack</a>
+</p>
+
+  </div>
+</div>
+
+<h2 id="style">Style</h2>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-5">
+
+<h4>Roboto</h4>
+<p>Ice Cream Sandwich introduced a new type family named Roboto, created specifically for the
+requirements of UI and high-resolution screens.</p>
+<p><a href="../style/typography.html#actionbar">More on Roboto</a></p>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+    <img src="../static/content/downloads_roboto_specimen_preview.png">
+
+  </div>
+  <div class="layout-content-col span-4">
+
+<p>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Roboto_Hinted_20111129.zip">Roboto</a>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Roboto_Specimen_Book_20111129.pdf">Specimen Book</a>
+</p>
+
+  </div>
+</div>
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-5">
+
+<h4>Color</h4>
+<p>Blue is the standard accent color in Android's color palette. Each color has a corresponding darker
+shade that can be used as a complement when needed.</p>
+<p><a href="../style/color.html">More on Color</a></p>
+
+  </div>
+  <div class="layout-content-col span-4">
+
+    <img src="../static/content/downloads_color_swatches.png">
+
+  </div>
+  <div class="layout-content-col span-4">
+
+<p>
+  <a class="download-button" href="https://dl-ssl.google.com/android/design/Android_Design_Color_Swatches_20120229.zip">Color Swatches</a>
+</p>
+
+  </div>
+</div>
+
+
+
+          
+          <div class="layout-content-row content-footer">
+            <div class="paging-links layout-content-col span-9">&nbsp;</div>
+            <div class="paging-links layout-content-col span-4">
+              <a href="#" class="prev-page-link">Previous</a>
+              <a href="#" class="next-page-link">Next</a>
+            </div>
+          </div>
+          
+        </div>
+
+      </div>
+
+      <div id="page-footer">
+
+        <p id="copyright">
+          Except as noted, this content is licensed under
+          <a href="http://creativecommons.org/licenses/by/2.5/">
+          Creative Commons Attribution 2.5</a>.<br>
+          For details and restrictions, see the
+          <a href="http://developer.android.com/license.html">Content License</a>.
+        </p>
+
+        <p>
+          <a href="http://www.android.com/terms.html">Site Terms of Service</a> &ndash;
+          <a href="http://www.android.com/privacy.html">Privacy Policy</a> &ndash;
+          <a href="http://www.android.com/branding.html">Brand Guidelines</a>
+        </p>
+
+      </div>
+    </div>
+
+    <script src="../static/jquery-1.6.2.min.js"></script>
+    <script>
+    var SITE_ROOT = '../';
+    </script>
+    <script src="../static/default.js"></script>
+
+
+    <script type="text/javascript">
+    var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+    document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+    </script>
+    <script type="text/javascript">
+    var pageTracker = _gat._getTracker("UA-5831155-1");
+    pageTracker._trackPageview();
+    </script>
+  </body>
+</html>
diff --git a/docs/html/design/get-started/creative-vision.html b/docs/html/design/get-started/creative-vision.html
index 11783c4..154f8d0 100644
--- a/docs/html/design/get-started/creative-vision.html
+++ b/docs/html/design/get-started/creative-vision.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/get-started/principles.html b/docs/html/design/get-started/principles.html
index 0d9ef20..f10a90d 100644
--- a/docs/html/design/get-started/principles.html
+++ b/docs/html/design/get-started/principles.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -106,7 +110,7 @@
 best interests in mind. Consider them as you apply your own creativity and design thinking. Deviate
 with purpose.</p>
 
-<h2>Enchant Me</h2>
+<h2 id="enchant-me">Enchant Me</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
@@ -176,7 +180,7 @@
   </div>
 </div>
 
-<h2>Simplify My Life</h2>
+<h2 id="simplify-my-life">Simplify My Life</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
@@ -312,7 +316,7 @@
   </div>
 </div>
 
-<h2>Make Me Amazing</h2>
+<h2 id="make-me-amazing">Make Me Amazing</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-7">
diff --git a/docs/html/design/get-started/ui-overview.html b/docs/html/design/get-started/ui-overview.html
index bd5ff9c..a4881d5 100644
--- a/docs/html/design/get-started/ui-overview.html
+++ b/docs/html/design/get-started/ui-overview.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -109,7 +113,7 @@
 in your app.</p>
 <p>Read on for a quick overview of the most important aspects of the Android user interface.</p>
 
-<h2>Home, All Apps, and Recents</h2>
+<h2 id="home-all-apps-recents">Home, All Apps, and Recents</h2>
 
 <div class="vspace size-1">&nbsp;</div>
 
@@ -153,7 +157,7 @@
   </div>
 </div>
 
-<h2>System Bars</h2>
+<h2 id="system-bars">System Bars</h2>
 
 <p>The system bars are screen areas dedicated to the display of notifications, communication of device
 status, and device navigation. Typically the system bars are displayed concurrently with your app.
@@ -185,7 +189,7 @@
 
 </div>
 
-<h2>Notifications</h2>
+<h2 id="notifications">Notifications</h2>
 
 <p>Notifications are brief messages that users can access at any time from the status bar. They
 provide updates, reminders, or information that's important, but not critical enough to warrant
diff --git a/docs/html/design/index.html b/docs/html/design/index.html
index 14d7b63..8583aa4 100644
--- a/docs/html/design/index.html
+++ b/docs/html/design/index.html
@@ -93,6 +93,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/patterns/actionbar.html b/docs/html/design/patterns/actionbar.html
index 911c549..1566d04 100644
--- a/docs/html/design/patterns/actionbar.html
+++ b/docs/html/design/patterns/actionbar.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -118,7 +122,8 @@
 <p>If you're new to writing Android apps, note that the action bar is one of the most important design
 elements you can implement. Following the guidelines described here will go a long way toward making
 your app's interface consistent with the core Android apps.</p>
-<h2>General Organization</h2>
+<h2 id="organization">General Organization</h2>
+
 <p>The action bar is split into four different functional areas that apply to most apps.</p>
 <img src="../static/content/action_bar_basics.png">
 
@@ -129,8 +134,8 @@
       <li class="value-1"><h4>App icon</h4>
         <p>
 
-The app icon establishes your app's identity. It can be replaced with a different logo or branding if
-you wish.
+The app icon establishes your app's identity. It can be replaced with a different logo or branding
+if you wish.
 Important: If the app is currently not displaying the top-level screen, be sure to display the Up
 caret to the left of the app icon, so the user can navigate up the hierarchy. For more discussion of
 Up navigation, see the <a href="../patterns/navigation.html">Navigation</a> pattern.
@@ -180,12 +185,11 @@
         </p>
       </li>
     </ol>
-
   </div>
 </div>
 
+<h2 id="adapting-rotation">Adapting to Rotation and Different Screen Sizes</h2>
 
-<h2>Adapting to Rotation and Different Screen Sizes</h2>
 <p>One of the most important UI issues to consider when creating an app is how to adjust to screen
 rotation on different screen sizes.</p>
 <p>You can adapt to such changes by using <em>split action bars</em>, which allow you to distribute action bar
@@ -196,7 +200,7 @@
   Split action bar showing action buttons at the bottom of the screen in vertical orientation.
 </div>
 
-<h2>Layout Considerations for Split Action Bars</h2>
+<h2 id="considerations-split-action-bars">Layout Considerations for Split Action Bars</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-8 with-callouts">
@@ -222,7 +226,8 @@
   </div>
 </div>
 
-<h2>Contextual Action Bars</h2>
+<h2 id="contextual">Contextual Action Bars</h2>
+
 <p>A <em>contextual action bar (CAB)</em> is a temporary action bar that overlays the app's action bar for the
 duration of a particular sub-task. CABs are most typically used for tasks that involve acting on
 selected data or text.</p>
@@ -244,7 +249,8 @@
 <p>Use CABs whenever you allow the user to select data via long press. You can control the action
 content of a CAB in order to insert the actions you would like the user to be able to perform.</p>
 <p>For more information, refer to the "Selection" pattern.</p>
-<h2>Action Bar Elements</h2>
+<h2 id="elements">Action Bar Elements</h2>
+
 <h4>Tabs</h4>
 <p><em>Tabs</em> display app views concurrently and make it easy to explore and switch between them. Use tabs
 if you expect your users to switch views frequently.</p>
@@ -326,7 +332,9 @@
 <p><em>Action buttons</em> on the action bar surface your app's most important activities. Think about which
 buttons will get used most often, and order them accordingly. Depending on available screen real
 estate, the system shows your most important actions as action buttons and moves the rest to the
-action overflow.</p>
+action overflow. The action bar and the action overflow should only present actions to the user that
+are available. If an action is unavailable in the current context, hide it. Do not show it as
+disabled.</p>
 
 <img src="../static/content/action_bar_pattern_action_icons.png">
 <div class="figure-caption">
@@ -380,8 +388,7 @@
 </p>
 <p>
 
-<a href="../static/download/action_bar_icons-v4.0.zip">Download the Action Bar Icon
-Pack</a>
+<a href="https://dl-ssl.google.com/android/design/Android_Design_Icons_20120229.zip">Download the Action Bar Icon Pack</a>
 
 </p>
 
@@ -436,7 +443,8 @@
   The Gallery app's share action provider with extended spinner for additional sharing options.
 </div>
 
-<h2>Action Bar Checklist</h2>
+<h2 id="checklist">Action Bar Checklist</h2>
+
 <p>When planning your split action bars, ask yourself questions like these:</p>
 <h4>How important is view navigation to the task?</h4>
 <p>If view navigation is very important to your app, use tabs (for fastest view-switching) or spinners.</p>
diff --git a/docs/html/design/patterns/app-structure.html b/docs/html/design/patterns/app-structure.html
index fb9205b..1b48280 100644
--- a/docs/html/design/patterns/app-structure.html
+++ b/docs/html/design/patterns/app-structure.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -111,7 +115,8 @@
 <li>Apps such as Gmail or Market that combine a broad set of data views with deep navigation</li>
 </ul>
 <p>Your app's structure depends largely on the content and tasks you want to surface for your users.</p>
-<h2>General Structure</h2>
+<h2 id="general-structure">General Structure</h2>
+
 <p>A typical Android app consists of top level and detail/edit views. If the navigation hierarchy is
 deep and complex, category views connect top level and detail views.</p>
 
@@ -139,7 +144,8 @@
   </div>
 </div>
 
-<h2>Top Level</h2>
+<h2 id="top-level">Top Level</h2>
+
 <p>The layout of your start screen requires special attention. This is the first screen people see
 after launching your app, so it should be an equally rewarding experience for new and frequent
 visitors alike.</p>
@@ -219,7 +225,8 @@
   </div>
 </div>
 
-<h2>Categories</h2>
+<h2 id="categories">Categories</h2>
+
 <p>Generally, the purpose of a deep, data-driven app is to navigate through organizational categories
 to the detail level, where data can be viewed and managed. Minimize perceived navigation effort by
 keeping your apps shallow.</p>
@@ -281,7 +288,8 @@
 delete multiple items in the category view. Analyze which detail view actions are applicable to
 collections of items. Then use multi-select to allow application of those actions to multiple items
 in a category view.</p>
-<h2>Details</h2>
+<h2 id="details">Details</h2>
+
 <p>The detail view allows you to view and act on your data. The layout of the detail view depends on
 the data type being displayed, and therefore differs widely among apps.</p>
 
@@ -330,7 +338,8 @@
   filmstrip control that lets people quickly jump to specific images.
 </div>
 
-<h2>Checklist</h2>
+<h2 id="checklist">Checklist</h2>
+
 <ul>
 <li>
 <p>Find ways to display useful content on your start screen.</p>
diff --git a/docs/html/design/patterns/compatibility.html b/docs/html/design/patterns/compatibility.html
index f18c62d..d6e59f5 100644
--- a/docs/html/design/patterns/compatibility.html
+++ b/docs/html/design/patterns/compatibility.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -110,7 +114,7 @@
 </ul>
 <p>Android 4.0 brings these changes for tablets to the phone platform.</p>
 
-<h2>Adapting Android 4.0 to Older Hardware and Apps</h2>
+<h2 id="older-hardware">Adapting Android 4.0 to Older Hardware and Apps</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-6">
diff --git a/docs/html/design/patterns/gestures.html b/docs/html/design/patterns/gestures.html
index f8585e4..c88817f 100644
--- a/docs/html/design/patterns/gestures.html
+++ b/docs/html/design/patterns/gestures.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/patterns/index.html b/docs/html/design/patterns/index.html
index ff797db..863baa9 100644
--- a/docs/html/design/patterns/index.html
+++ b/docs/html/design/patterns/index.html
@@ -93,6 +93,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/patterns/multi-pane-layouts.html b/docs/html/design/patterns/multi-pane-layouts.html
index af05e31..7925c98 100644
--- a/docs/html/design/patterns/multi-pane-layouts.html
+++ b/docs/html/design/patterns/multi-pane-layouts.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -108,7 +112,8 @@
 <p><em>Panels</em> are a great way for your app to achieve this. They allow you to combine multiple views into
 one compound view when a lot of horizontal screen real estate is available and by splitting them up
 when less space is available.</p>
-<h2>Combining Multiple Views Into One</h2>
+<h2 id="combining-views">Combining Multiple Views Into One</h2>
+
 <p>On smaller devices your content is typically divided into a master grid or list view and a detail
 view. Touching an item in the master view opens a different screen showing that item's detail
 information.</p>
@@ -124,7 +129,8 @@
 <p>In general, use the pane on the right to present more information about the item you selected in the
 left pane. Make sure to keep the item in the left pane selected in order to establish the
 relationship between the panels.</p>
-<h2>Compound Views and Orientation Changes</h2>
+<h2 id="orientation">Compound Views and Orientation Changes</h2>
+
 <p>Screens should have the same functionality regardless of orientation. If you use a compound view in
 one orientation, don't split it up when the user rotates the screen. There are several techniques
 you can use to adjust the layout after orientation change while keeping functional parity intact.</p>
@@ -189,7 +195,8 @@
   </div>
 </div>
 
-<h2>Checklist</h2>
+<h2 id="checklist">Checklist</h2>
+
 <ul>
 <li>
 <p>Plan in advance on how your app scales to different screen sizes and screen orientations.</p>
diff --git a/docs/html/design/patterns/navigation.html b/docs/html/design/patterns/navigation.html
index cad3682..6287b5e 100644
--- a/docs/html/design/patterns/navigation.html
+++ b/docs/html/design/patterns/navigation.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -112,7 +116,8 @@
 
 <img src="../static/content/navigation_with_back_and_up.png">
 
-<h2>Up vs. Back</h2>
+<h2 id="up-vs-back">Up vs. Back</h2>
+
 <p>The Up button is used to navigate within an application based on the hierarchical relationships
 between screens. For instance, if screen A displays a list of items, and selecting an item leads to
 screen B (which presents that item in more detail), then screen B should offer an Up button that
@@ -134,7 +139,8 @@
 <li>Back dismisses contextual action bars, and removes the highlight from the selected items</li>
 <li>Back hides the onscreen keyboard (IME)</li>
 </ul>
-<h2>Navigation Within Your App</h2>
+<h2 id="within-app">Navigation Within Your App</h2>
+
 <h4>Navigating to screens with multiple entry points</h4>
 <p>Sometimes a screen doesn't have a strict position within the app's hierarchy, and can be reached
 from multiple entry points&mdash;e.g., a settings screen which can be navigated to from any screen
@@ -176,7 +182,8 @@
 
 <img src="../static/content/navigation_between_siblings_market2.png">
 
-<h2>Navigation From Outside Your App</h2>
+<h2 id="from-outside">Navigation From Outside Your App</h2>
+
 <p>There are two categories of navigation from outside your app to screens deep within the app's
 hierarchy:</p>
 <ul>
diff --git a/docs/html/design/patterns/new-4-0.html b/docs/html/design/patterns/new-4-0.html
index 272b079..2e2cbc2 100644
--- a/docs/html/design/patterns/new-4-0.html
+++ b/docs/html/design/patterns/new-4-0.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/patterns/notifications.html b/docs/html/design/patterns/notifications.html
index c5045aed..99af418 100644
--- a/docs/html/design/patterns/notifications.html
+++ b/docs/html/design/patterns/notifications.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -173,7 +177,7 @@
   </div>
 </div>
 
-<h2>Design Guidelines</h2>
+<h2 id="design-guidelines">Design Guidelines</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-6">
@@ -269,7 +273,7 @@
 <li>Use color to distinguish your app from others. Notification icons should generally be monochrome.</li>
 </ul>
 
-<h2>Interacting With Notifications</h2>
+<h2 id="interacting-with-notifications">Interacting With Notifications</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-6">
diff --git a/docs/html/design/patterns/pure-android.html b/docs/html/design/patterns/pure-android.html
index 507558a..f5a8042 100644
--- a/docs/html/design/patterns/pure-android.html
+++ b/docs/html/design/patterns/pure-android.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -227,7 +231,7 @@
   </div>
 </div>
 
-<h2>Device Independence</h2>
+<h2 id="device-independence">Device Independence</h2>
 
 <p>Remember that your app will run on a wide variety of different screen sizes. Create visual assets
 for different screen sizes and densities and make use of concepts such as multi-pane layouts to
diff --git a/docs/html/design/patterns/selection.html b/docs/html/design/patterns/selection.html
index 37dcab5..44c6a84 100644
--- a/docs/html/design/patterns/selection.html
+++ b/docs/html/design/patterns/selection.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -180,7 +184,8 @@
   </div>
 </div>
 
-<h2>Checklist</h2>
+<h2 id="checklist">Checklist</h2>
+
 <ul>
 <li>
 <p>Whenever your app supports the selection of multiple data items, make use of the contextual action
diff --git a/docs/html/design/patterns/swipe-views.html b/docs/html/design/patterns/swipe-views.html
index 4e8cd03..acc9f39 100644
--- a/docs/html/design/patterns/swipe-views.html
+++ b/docs/html/design/patterns/swipe-views.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -107,7 +111,8 @@
 vertical hierarchies and make access to related data items faster and more enjoyable. Swipe views
 allow the user to efficiently move from item to item using a simple gesture and thereby make
 browsing and consuming data a more fluent experience.</p>
-<h2>Swiping Between Detail Views</h2>
+<h2 id="detail-views">Swiping Between Detail Views</h2>
+
 <p>An app's data is often organized in a master/detail relationship: The user can view a list of
 related data items, such as images, chats, or emails, and then pick one of the items to see the
 detail contents in a separate screen.</p>
@@ -127,7 +132,7 @@
   Navigating between consecutive Email messages using the swipe gesture.
 </div>
 
-<h2>Swiping Between Tabs</h2>
+<h2 id="between-tabs">Swiping Between Tabs</h2>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-5">
@@ -150,7 +155,8 @@
 <p>If your app uses action bar tabs, use swipe to navigate between the different views.</p>
 <div class="vspace size-2">&nbsp;</div>
 
-<h2>Checklist</h2>
+<h2 id="checklist">Checklist</h2>
+
 <ul>
 <li>
 <p>Use swipe to quickly navigate between detail views or tabs.</p>
diff --git a/docs/html/design/static/content/downloads_color_swatches.png b/docs/html/design/static/content/downloads_color_swatches.png
new file mode 100644
index 0000000..af2b24f
--- /dev/null
+++ b/docs/html/design/static/content/downloads_color_swatches.png
Binary files differ
diff --git a/docs/html/design/static/content/downloads_roboto_specimen_preview.png b/docs/html/design/static/content/downloads_roboto_specimen_preview.png
new file mode 100644
index 0000000..2954cbf
--- /dev/null
+++ b/docs/html/design/static/content/downloads_roboto_specimen_preview.png
Binary files differ
diff --git a/docs/html/design/static/content/downloads_stencils.png b/docs/html/design/static/content/downloads_stencils.png
new file mode 100644
index 0000000..9e09319
--- /dev/null
+++ b/docs/html/design/static/content/downloads_stencils.png
Binary files differ
diff --git a/docs/html/design/static/default.css b/docs/html/design/static/default.css
index 42ab527..3aa1db3 100644
--- a/docs/html/design/static/default.css
+++ b/docs/html/design/static/default.css
@@ -3,8 +3,8 @@
 /* clearfix idiom */
 /* common mixins */
 /* page layout + top-level styles */
-::-moz-selection,
 ::-webkit-selection,
+::-moz-selection,
 ::selection {
   background-color: #0099cc;
   color: #fff; }
@@ -256,6 +256,8 @@
       position: absolute;
       top: 10px;
       right: 10px; }
+    #nav .nav-section-header.empty:after {
+      display: none; }
   #nav li.expanded .nav-section-header {
     background: rgba(0, 0, 0, 0.05); }
     #nav li.expanded .nav-section-header:after {
@@ -268,9 +270,9 @@
     overflow: hidden;
     margin-bottom: 0; }
     #nav > li > ul.animate-height {
-      transition: height 0.25s ease-in;
       -webkit-transition: height 0.25s ease-in;
-      -moz-transition: height 0.25s ease-in; }
+      -moz-transition: height 0.25s ease-in;
+      transition: height 0.25s ease-in; }
     #nav > li > ul li {
       padding: 10px 10px 11px 10px; }
   #nav > li.expanded > ul {
@@ -330,6 +332,39 @@
       margin-left: 5px; }
 
 /* content body */
+@-webkit-keyframes glowheader {
+  from {
+    background-color: #33b5e5;
+    color: #000;
+    border-bottom-color: #000; }
+
+  to {
+    background-color: transparent;
+    color: #33b5e5;
+    border-bottom-color: #33b5e5; } }
+
+@-moz-keyframes glowheader {
+  from {
+    background-color: #33b5e5;
+    color: #000;
+    border-bottom-color: #000; }
+
+  to {
+    background-color: transparent;
+    color: #33b5e5;
+    border-bottom-color: #33b5e5; } }
+
+@keyframes glowheader {
+  from {
+    background-color: #33b5e5;
+    color: #000;
+    border-bottom-color: #000; }
+
+  to {
+    background-color: transparent;
+    color: #33b5e5;
+    border-bottom-color: #33b5e5; } }
+
 #content p,
 #content ul,
 #content ol,
@@ -345,6 +380,16 @@
   color: #33b5e5;
   border-bottom: 1px solid #33b5e5;
   height: 30px; }
+  #content h2:target {
+    -webkit-animation-name: glowheader;
+    -moz-animation-name: glowheader;
+    animation-name: glowheader;
+    -webkit-animation-duration: 0.7s;
+    -moz-animation-duration: 0.7s;
+    animation-duration: 0.7s;
+    -webkit-animation-timing-function: ease-out;
+    -moz-animation-timing-function: ease-out;
+    animation-timing-function: ease-out; }
 #content hr {
   border: 0;
   border-bottom: 1px solid #33b5e5;
@@ -569,3 +614,20 @@
     margin-right: 8px; }
   .video-instructions:after {
     content: 'Click to replay movie.'; }
+
+/* download buttons */
+.download-button {
+  display: block;
+  margin-bottom: 5px;
+  text-decoration: none;
+  background-color: #33b5e5;
+  color: #fff !important;
+  font-weight: 500;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.12);
+  padding: 6px 12px;
+  border-radius: 2px; }
+  .download-button:hover, .download-button:focus {
+    background-color: #0099cc;
+    color: #fff !important; }
+  .download-button:active {
+    background-color: #006699; }
diff --git a/docs/html/design/static/default.js b/docs/html/design/static/default.js
index 6721ab8..b213dd9 100644
--- a/docs/html/design/static/default.js
+++ b/docs/html/design/static/default.js
@@ -158,4 +158,12 @@
       $tooltip.hide();
     });
   });
+
+  // Set up <h2> deeplinks
+  $('h2').click(function() {
+    var id = $(this).attr('id');
+    if (id) {
+      document.location.hash = id;
+    }
+  });
 });
\ No newline at end of file
diff --git a/docs/html/design/static/download/RobotoSpecimenBook.pdf b/docs/html/design/static/download/RobotoSpecimenBook.pdf
deleted file mode 100644
index 594a366..0000000
--- a/docs/html/design/static/download/RobotoSpecimenBook.pdf
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/static/download/Roboto_Hinted_20111129.zip b/docs/html/design/static/download/Roboto_Hinted_20111129.zip
deleted file mode 100644
index 3d3ab77..0000000
--- a/docs/html/design/static/download/Roboto_Hinted_20111129.zip
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/static/download/action_bar_icons-v4.0.zip b/docs/html/design/static/download/action_bar_icons-v4.0.zip
deleted file mode 100644
index 4568894..0000000
--- a/docs/html/design/static/download/action_bar_icons-v4.0.zip
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/static/download/color_swatches.zip b/docs/html/design/static/download/color_swatches.zip
deleted file mode 100644
index 0221d7b..0000000
--- a/docs/html/design/static/download/color_swatches.zip
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/static/ico_styleguide.png b/docs/html/design/static/ico_styleguide.png
deleted file mode 100644
index c12907c..0000000
--- a/docs/html/design/static/ico_styleguide.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/design/style/color.html b/docs/html/design/style/color.html
index 893e09e..bca3c45 100644
--- a/docs/html/design/style/color.html
+++ b/docs/html/design/style/color.html
@@ -173,6 +173,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -218,10 +222,11 @@
       </ul>
     </div>
 
-<h2>Palette</h2>
+<h2 id="palette">Palette</h2>
+
 <p>Blue is the standard accent color in Android's color palette. Each color has a corresponding darker
 shade that can be used as a complement when needed.</p>
-<p><a href="../static/download/color_swatches.zip">Download the swatches</a></p>
+<p><a href="https://dl-ssl.google.com/android/design/Android_Design_Color_Swatches_20120229.zip">Download the swatches</a></p>
 
 <img src="../static/content/color_spectrum.png">
 
diff --git a/docs/html/design/style/devices-displays.html b/docs/html/design/style/devices-displays.html
index 9fba719..89e0876 100644
--- a/docs/html/design/style/devices-displays.html
+++ b/docs/html/design/style/devices-displays.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/style/iconography.html b/docs/html/design/style/iconography.html
index 5d5d200..96954de 100644
--- a/docs/html/design/style/iconography.html
+++ b/docs/html/design/style/iconography.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -191,7 +195,7 @@
 </div>
 
 
-<h2 id="actionbar">Action Bar</h2>
+<h2 id="action-bar">Action Bar</h2>
 
 <p>
 
@@ -211,8 +215,7 @@
 </p>
 <p>
 
-<a href="../static/download/action_bar_icons-v4.0.zip">Download the Action Bar Icon
-Pack</a>
+<a href="https://dl-ssl.google.com/android/design/Android_Design_Icons_20120229.zip">Download the Action Bar Icon Pack</a>
 
 </p>
 
@@ -290,7 +293,7 @@
 </div>
 
 
-<h2 id="small_contextual">Small / Contextual Icons</h2>
+<h2 id="small-contextual">Small / Contextual Icons</h2>
 
 <p>Within the body of your app, use small icons to surface actions and/or provide status for specific
 items. For example, in the Gmail app, each message has a star icon that marks the message as
diff --git a/docs/html/design/style/index.html b/docs/html/design/style/index.html
index 5ecbafa..c7ac58f 100644
--- a/docs/html/design/style/index.html
+++ b/docs/html/design/style/index.html
@@ -93,6 +93,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/style/metrics-grids.html b/docs/html/design/style/metrics-grids.html
index 17d4937..7bb9dd0 100644
--- a/docs/html/design/style/metrics-grids.html
+++ b/docs/html/design/style/metrics-grids.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -130,7 +134,7 @@
   </div>
 </div>
 
-<h2>48dp Rhythm</h2>
+<h2 id="48dp-rhythm">48dp Rhythm</h2>
 
 <p>Touchable UI components are generally laid out along 48dp units.</p>
 
@@ -157,7 +161,7 @@
 <h4>Mind the gaps</h4>
 <p>Spacing between each UI element is 8dp.</p>
 
-<h2>Examples</h2>
+<h2 id="examples">Examples</h2>
 
 <img src="../static/content/metrics_forms.png">
 
diff --git a/docs/html/design/style/themes.html b/docs/html/design/style/themes.html
index ada974d..a629978 100644
--- a/docs/html/design/style/themes.html
+++ b/docs/html/design/style/themes.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/style/touch-feedback.html b/docs/html/design/style/touch-feedback.html
index 0d49832..d1c08f8 100644
--- a/docs/html/design/style/touch-feedback.html
+++ b/docs/html/design/style/touch-feedback.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
diff --git a/docs/html/design/style/typography.html b/docs/html/design/style/typography.html
index d3cc769..d9b6d4b 100644
--- a/docs/html/design/style/typography.html
+++ b/docs/html/design/style/typography.html
@@ -80,6 +80,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -119,8 +123,8 @@
 
     <img src="../static/content/typography_alphas.png">
 
-<p><a href="../static/download/Roboto_Hinted_20111129.zip">Download Roboto</a></p>
-<p><a href="../static/download/RobotoSpecimenBook.pdf">Specimen Book</a></p>
+<p><a href="https://dl-ssl.google.com/android/design/Roboto_Hinted_20111129.zip">Download Roboto</a></p>
+<p><a href="https://dl-ssl.google.com/android/design/Roboto_Specimen_Book_20111129.pdf">Specimen Book</a></p>
 
   </div>
 </div>
diff --git a/docs/html/design/style/writing.html b/docs/html/design/style/writing.html
index e5f1ea8..146ce88 100644
--- a/docs/html/design/style/writing.html
+++ b/docs/html/design/style/writing.html
@@ -132,6 +132,10 @@
             </ul>
           </li>
 
+          <li class="nav-section">
+            <div class="nav-section-header empty"><a href="../downloads/index.html">Downloads</a></div>
+          </li>
+
           <li>
             <div id="back-dac-section"><a href="../../index.html">Developers</a></div>
           </li>
@@ -186,7 +190,7 @@
 </li>
 </ol>
 
-<h2>Examples</h2>
+<h2 id="examples">Examples</h2>
 
 <ol><li class="value-1"><strong>Keep it brief.</strong> From the setup wizard:</ol>
 
diff --git a/docs/html/guide/topics/graphics/hardware-accel.jd b/docs/html/guide/topics/graphics/hardware-accel.jd
index 39ccbf4..04fb564 100644
--- a/docs/html/guide/topics/graphics/hardware-accel.jd
+++ b/docs/html/guide/topics/graphics/hardware-accel.jd
@@ -42,19 +42,20 @@
         <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL with the Framework
         APIs</a></li>
 
-        <li><a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a></li>
+        <li><a href="{@docRoot}guide/topics/renderscript/index.html">Renderscript</a></li>
       </ol>
     </div>
   </div>
 
   <p>Beginning in Android 3.0 (API level 11), the Android 2D rendering pipeline is designed to
   better support hardware acceleration. Hardware acceleration carries out all drawing operations
-  that are performed on a {@link android.view.View}'s canvas using the GPU.</p>
+  that are performed on a {@link android.view.View}'s canvas using the GPU. Because of the
+  increased resources required to enable hardware acceleration, your app will consume more RAM.</p>
 
   <p>The easiest way to enable hardware acceleration is to turn it on
   globally for your entire application. If your application uses only standard views and {@link
   android.graphics.drawable.Drawable}s, turning it on globally should not cause any adverse
-  effects. However, because hardware acceleration is not supported for all of the 2D drawing
+  drawing effects. However, because hardware acceleration is not supported for all of the 2D drawing
   operations, turning it on might affect some of your applications that use custom views or drawing
   calls. Problems usually manifest themselves as invisible elements, exceptions, or wrongly
   rendered pixels. To remedy this, Android gives you the option to enable or disable hardware
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index f44901b..8b131c8 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -307,7 +307,7 @@
 <dd>Whether or not an existing instance of the activity should be shut down 
 (finished) whenever the user again launches its task (chooses the task on the 
 home screen) &mdash; "{@code true}" if it should be shut down, and "{@code false}" 
-if not.  The default value is "{@code false}". 
+if not. The default value is "{@code false}".
 
 <p>
 If this attribute and 
@@ -321,13 +321,15 @@
 Activity &mdash; "{@code true}" if it should be enabled, and "{@code false}" if
 not. The default value is "{@code false}".
 
+
 <p>Starting from Android 3.0, a hardware-accelerated OpenGL renderer is
 available to applications, to improve performance for many common 2D graphics
 operations. When the hardware-accelerated renderer is enabled, most operations
 in Canvas, Paint, Xfermode, ColorFilter, Shader, and Camera are accelerated.
 This results in smoother animations, smoother scrolling, and improved
 responsiveness overall, even for applications that do not explicitly make use
-the framework's OpenGL libraries. </p>
+the framework's OpenGL libraries. Because of the increased resources required to
+enable hardware acceleration, your app will consume more RAM.</p>
 
 <p>Note that not all of the OpenGL 2D operations are accelerated. If you enable
 the hardware-accelerated renderer, test your application to ensure that it can
@@ -587,9 +589,9 @@
 </p></dd>
 
 <dt><a name="proc"></a>{@code android:process}</dt>
-<dd>The name of the process in which the activity should run.  Normally, 
+<dd>The name of the process in which the activity should run. Normally, 
 all components of an application run in the default process created for the 
-application.  It has the same name as the application package.  The <code><a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a></code> element's 
+application.  It has the same name as the application package. The <code><a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a></code> element's 
 <code><a href="{@docRoot}guide/topics/manifest/application-element.html#proc">process</a></code> 
 attribute can set a different default for all components.  But each component 
 can override the default, allowing you to spread your application across 
@@ -706,7 +708,7 @@
 A "{@code true}" setting ensures that the activity can be restarted in the 
 absence of retained state.  For example, the activity that displays the 
 home screen uses this setting to make sure that it does not get removed if it 
-crashes for some reason. 
+crashes for some reason.
 </p></dd>
 
 <dt><a name="aff"></a>{@code android:taskAffinity}</dt>
diff --git a/drm/drmserver/DrmManagerService.cpp b/drm/drmserver/DrmManagerService.cpp
index caeb026..8ba0203 100644
--- a/drm/drmserver/DrmManagerService.cpp
+++ b/drm/drmserver/DrmManagerService.cpp
@@ -159,12 +159,18 @@
 status_t DrmManagerService::consumeRights(
             int uniqueId, DecryptHandle* decryptHandle, int action, bool reserve) {
     ALOGV("Entering consumeRights");
+    if (!isProtectedCallAllowed()) {
+        return DRM_ERROR_NO_PERMISSION;
+    }
     return mDrmManager->consumeRights(uniqueId, decryptHandle, action, reserve);
 }
 
 status_t DrmManagerService::setPlaybackStatus(
             int uniqueId, DecryptHandle* decryptHandle, int playbackStatus, int64_t position) {
     ALOGV("Entering setPlaybackStatus");
+    if (!isProtectedCallAllowed()) {
+        return DRM_ERROR_NO_PERMISSION;
+    }
     return mDrmManager->setPlaybackStatus(uniqueId, decryptHandle, playbackStatus, position);
 }
 
@@ -229,12 +235,18 @@
 
 status_t DrmManagerService::closeDecryptSession(int uniqueId, DecryptHandle* decryptHandle) {
     ALOGV("Entering closeDecryptSession");
+    if (!isProtectedCallAllowed()) {
+        return DRM_ERROR_NO_PERMISSION;
+    }
     return mDrmManager->closeDecryptSession(uniqueId, decryptHandle);
 }
 
 status_t DrmManagerService::initializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle,
             int decryptUnitId, const DrmBuffer* headerInfo) {
     ALOGV("Entering initializeDecryptUnit");
+    if (!isProtectedCallAllowed()) {
+        return DRM_ERROR_NO_PERMISSION;
+    }
     return mDrmManager->initializeDecryptUnit(uniqueId,decryptHandle, decryptUnitId, headerInfo);
 }
 
@@ -242,18 +254,27 @@
             int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
             const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
     ALOGV("Entering decrypt");
+    if (!isProtectedCallAllowed()) {
+        return DRM_ERROR_NO_PERMISSION;
+    }
     return mDrmManager->decrypt(uniqueId, decryptHandle, decryptUnitId, encBuffer, decBuffer, IV);
 }
 
 status_t DrmManagerService::finalizeDecryptUnit(
             int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) {
     ALOGV("Entering finalizeDecryptUnit");
+    if (!isProtectedCallAllowed()) {
+        return DRM_ERROR_NO_PERMISSION;
+    }
     return mDrmManager->finalizeDecryptUnit(uniqueId, decryptHandle, decryptUnitId);
 }
 
 ssize_t DrmManagerService::pread(int uniqueId, DecryptHandle* decryptHandle,
             void* buffer, ssize_t numBytes, off64_t offset) {
     ALOGV("Entering pread");
+    if (!isProtectedCallAllowed()) {
+        return DRM_ERROR_NO_PERMISSION;
+    }
     return mDrmManager->pread(uniqueId, decryptHandle, buffer, numBytes, offset);
 }
 
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 14d5fae..482ab0a 100755
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -49,6 +49,8 @@
      */
     public static final int ERROR_UNKNOWN = -2000;
 
+    HandlerThread mInfoThread;
+    HandlerThread mEventThread;
     private static final String TAG = "DrmManagerClient";
 
     static {
@@ -105,6 +107,7 @@
 
     private int mUniqueId;
     private int mNativeContext;
+    private boolean mReleased;
     private Context mContext;
     private InfoHandler mInfoHandler;
     private EventHandler mEventHandler;
@@ -238,32 +241,73 @@
      */
     public DrmManagerClient(Context context) {
         mContext = context;
-
-        HandlerThread infoThread = new HandlerThread("DrmManagerClient.InfoHandler");
-        infoThread.start();
-        mInfoHandler = new InfoHandler(infoThread.getLooper());
-
-        HandlerThread eventThread = new HandlerThread("DrmManagerClient.EventHandler");
-        eventThread.start();
-        mEventHandler = new EventHandler(eventThread.getLooper());
+        mReleased = false;
 
         // save the unique id
-        mUniqueId = _initialize(new WeakReference<DrmManagerClient>(this));
+        mUniqueId = _initialize();
     }
 
     protected void finalize() {
-        _finalize(mUniqueId);
+        if (!mReleased) {
+            Log.w(TAG, "You should have called release()");
+            release();
+        }
     }
 
     /**
+     * Releases resources associated with the current session of DrmManagerClient.
+     *
+     * It is considered good practice to call this method when the {@link DrmManagerClient} object
+     * is no longer needed in your application. After release() is called,
+     * {@link DrmManagerClient} is no longer usable since it has lost all of its required resource.
+     */
+    public void release() {
+        if (mReleased) {
+            Log.w(TAG, "You have already called release()");
+            return;
+        }
+        mReleased = true;
+        if (mEventHandler != null) {
+            mEventThread.quit();
+            mEventThread = null;
+        }
+        if (mInfoHandler != null) {
+            mInfoThread.quit();
+            mInfoThread = null;
+        }
+        mEventHandler = null;
+        mInfoHandler = null;
+        mOnEventListener = null;
+        mOnInfoListener = null;
+        mOnErrorListener = null;
+        _release(mUniqueId);
+    }
+
+
+    private void createListeners() {
+        if (mEventHandler == null && mInfoHandler == null) {
+            mInfoThread = new HandlerThread("DrmManagerClient.InfoHandler");
+            mInfoThread.start();
+            mInfoHandler = new InfoHandler(mInfoThread.getLooper());
+
+            mEventThread = new HandlerThread("DrmManagerClient.EventHandler");
+            mEventThread.start();
+            mEventHandler = new EventHandler(mEventThread.getLooper());
+            _setListeners(mUniqueId, new WeakReference<DrmManagerClient>(this));
+        }
+    }
+
+
+    /**
      * Registers an {@link DrmManagerClient.OnInfoListener} callback, which is invoked when the 
      * DRM framework sends status or warning information during registration or rights acquisition.
      *
      * @param infoListener Interface definition for the callback.
      */
     public synchronized void setOnInfoListener(OnInfoListener infoListener) {
+        mOnInfoListener = infoListener;
         if (null != infoListener) {
-            mOnInfoListener = infoListener;
+            createListeners();
         }
     }
 
@@ -274,8 +318,9 @@
      * @param eventListener Interface definition for the callback.
      */
     public synchronized void setOnEventListener(OnEventListener eventListener) {
+        mOnEventListener = eventListener;
         if (null != eventListener) {
-            mOnEventListener = eventListener;
+            createListeners();
         }
     }
 
@@ -286,8 +331,9 @@
      * @param errorListener Interface definition for the callback.
      */
     public synchronized void setOnErrorListener(OnErrorListener errorListener) {
+        mOnErrorListener = errorListener;
         if (null != errorListener) {
-            mOnErrorListener = errorListener;
+            createListeners();
         }
     }
 
@@ -793,9 +839,11 @@
     }
 
     // private native interfaces
-    private native int _initialize(Object weak_this);
+    private native int _initialize();
 
-    private native void _finalize(int uniqueId);
+    private native void _setListeners(int uniqueId, Object weak_this);
+
+    private native void _release(int uniqueId);
 
     private native void _installDrmEngine(int uniqueId, String engineFilepath);
 
diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
index dfc7fb2..cf58177 100644
--- a/drm/jni/android_drm_DrmManagerClient.cpp
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -225,25 +225,32 @@
 }
 
 static jint android_drm_DrmManagerClient_initialize(
-        JNIEnv* env, jobject thiz, jobject weak_thiz) {
+        JNIEnv* env, jobject thiz) {
     ALOGV("initialize - Enter");
 
     int uniqueId = 0;
     sp<DrmManagerClientImpl> drmManager = DrmManagerClientImpl::create(&uniqueId, false);
     drmManager->addClient(uniqueId);
 
-    // Set the listener to DrmManager
-    sp<DrmManagerClient::OnInfoListener> listener = new JNIOnInfoListener(env, thiz, weak_thiz);
-    drmManager->setOnInfoListener(uniqueId, listener);
-
     setDrmManagerClientImpl(env, thiz, drmManager);
     ALOGV("initialize - Exit");
-
     return uniqueId;
 }
 
-static void android_drm_DrmManagerClient_finalize(JNIEnv* env, jobject thiz, jint uniqueId) {
-    ALOGV("finalize - Enter");
+static void android_drm_DrmManagerClient_setListeners(
+        JNIEnv* env, jobject thiz, jint uniqueId, jobject weak_thiz) {
+    ALOGV("setListeners - Enter");
+
+    // Set the listener to DrmManager
+    sp<DrmManagerClient::OnInfoListener> listener = new JNIOnInfoListener(env, thiz, weak_thiz);
+    getDrmManagerClientImpl(env, thiz)->setOnInfoListener(uniqueId, listener);
+
+    ALOGV("setListeners - Exit");
+}
+
+static void android_drm_DrmManagerClient_release(
+        JNIEnv* env, jobject thiz, jint uniqueId) {
+    ALOGV("release - Enter");
     DrmManagerClientImpl::remove(uniqueId);
     getDrmManagerClientImpl(env, thiz)->setOnInfoListener(uniqueId, NULL);
 
@@ -252,7 +259,7 @@
         oldClient->setOnInfoListener(uniqueId, NULL);
         oldClient->removeClient(uniqueId);
     }
-    ALOGV("finalize - Exit");
+    ALOGV("release - Exit");
 }
 
 static jobject android_drm_DrmManagerClient_getConstraintsFromContent(
@@ -714,11 +721,14 @@
 
 static JNINativeMethod nativeMethods[] = {
 
-    {"_initialize", "(Ljava/lang/Object;)I",
+    {"_initialize", "()I",
                                     (void*)android_drm_DrmManagerClient_initialize},
 
-    {"_finalize", "(I)V",
-                                    (void*)android_drm_DrmManagerClient_finalize},
+    {"_setListeners", "(ILjava/lang/Object;)V",
+                                    (void*)android_drm_DrmManagerClient_setListeners},
+
+    {"_release", "(I)V",
+                                    (void*)android_drm_DrmManagerClient_release},
 
     {"_getConstraints", "(ILjava/lang/String;I)Landroid/content/ContentValues;",
                                     (void*)android_drm_DrmManagerClient_getConstraintsFromContent},
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 4ad5b9f..7c7cd01 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -102,7 +102,12 @@
 
     /**
      * <p>Starts the animation, looping if necessary. This method has no effect
-     * if the animation is running.</p>
+     * if the animation is running. Do not call this in the {@link android.app.Activity#onCreate}
+     * method of your activity, because the {@link android.graphics.drawable.AnimationDrawable} is
+     * not yet fully attached to the window. If you want to play
+     * the animation immediately, without requiring interaction, then you might want to call it
+     * from the {@link android.app.Activity#onWindowFocusChanged} method in your activity,
+     * which will get called when Android brings your window into focus.</p>
      *
      * @see #isRunning()
      * @see #stop()
diff --git a/include/binder/MemoryHeapPmem.h b/include/binder/MemoryHeapPmem.h
deleted file mode 100644
index e1660c4..0000000
--- a/include/binder/MemoryHeapPmem.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2008 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_MEMORY_HEAP_PMEM_H
-#define ANDROID_MEMORY_HEAP_PMEM_H
-
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <binder/MemoryHeapBase.h>
-#include <binder/IMemory.h>
-#include <utils/SortedVector.h>
-#include <utils/threads.h>
-
-namespace android {
-
-class MemoryHeapBase;
-
-// ---------------------------------------------------------------------------
-
-class MemoryHeapPmem : public MemoryHeapBase
-{
-public:
-    class MemoryPmem : public BnMemory {
-    public:
-        MemoryPmem(const sp<MemoryHeapPmem>& heap);
-        ~MemoryPmem();
-    protected:
-        const sp<MemoryHeapPmem>&  getHeap() const { return mClientHeap; }
-    private:
-        friend class MemoryHeapPmem;
-        virtual void revoke() = 0;
-        sp<MemoryHeapPmem>  mClientHeap;
-    };
-    
-    MemoryHeapPmem(const sp<MemoryHeapBase>& pmemHeap, uint32_t flags = 0);
-    ~MemoryHeapPmem();
-
-    /* HeapInterface additions */
-    virtual sp<IMemory> mapMemory(size_t offset, size_t size);
-
-    /* make the whole heap visible (you know who you are) */
-    virtual status_t slap();
-    
-    /* hide (revoke) the whole heap (the client will see the garbage page) */
-    virtual status_t unslap();
-    
-    /* revoke all allocations made by this heap */
-    virtual void revoke();
-
-private:
-    /* use this to create your own IMemory for mapMemory */
-    virtual sp<MemoryPmem> createMemory(size_t offset, size_t size);
-    void remove(const wp<MemoryPmem>& memory);
-
-private:
-    sp<MemoryHeapBase>              mParentHeap;
-    mutable Mutex                   mLock;
-    SortedVector< wp<MemoryPmem> >  mAllocations;
-};
-
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_MEMORY_HEAP_PMEM_H
diff --git a/include/drm/drm_framework_common.h b/include/drm/drm_framework_common.h
index 2632cbd..637409c 100644
--- a/include/drm/drm_framework_common.h
+++ b/include/drm/drm_framework_common.h
@@ -43,6 +43,7 @@
     DRM_ERROR_DECRYPT                       = ERROR_BASE - 5,
     DRM_ERROR_CANNOT_HANDLE                 = ERROR_BASE - 6,
     DRM_ERROR_TAMPER_DETECTED               = ERROR_BASE - 7,
+    DRM_ERROR_NO_PERMISSION                 = ERROR_BASE - 8,
 
     DRM_NO_ERROR                            = NO_ERROR
 };
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 437a89c..b0c581a 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -225,7 +225,7 @@
 
     /* get sample rate for this record track
      */
-            uint32_t    getSampleRate();
+            uint32_t    getSampleRate() const;
 
     /* Sets marker position. When record reaches the number of frames specified,
      * a callback with event type EVENT_MARKER is called. Calling setMarkerPosition
@@ -242,7 +242,7 @@
      *  - INVALID_OPERATION: the AudioRecord has no callback installed.
      */
             status_t    setMarkerPosition(uint32_t marker);
-            status_t    getMarkerPosition(uint32_t *marker);
+            status_t    getMarkerPosition(uint32_t *marker) const;
 
 
     /* Sets position update period. Every time the number of frames specified has been recorded,
@@ -261,7 +261,7 @@
      *  - INVALID_OPERATION: the AudioRecord has no callback installed.
      */
             status_t    setPositionUpdatePeriod(uint32_t updatePeriod);
-            status_t    getPositionUpdatePeriod(uint32_t *updatePeriod);
+            status_t    getPositionUpdatePeriod(uint32_t *updatePeriod) const;
 
 
     /* Gets record head position. The position is the  total number of frames
@@ -275,7 +275,7 @@
      *  - NO_ERROR: successful operation
      *  - BAD_VALUE:  position is NULL
      */
-            status_t    getPosition(uint32_t *position);
+            status_t    getPosition(uint32_t *position) const;
 
     /* returns a handle on the audio input used by this AudioRecord.
      *
@@ -285,7 +285,7 @@
      * Returned value:
      *  handle on audio hardware input
      */
-            audio_io_handle_t    getInput();
+            audio_io_handle_t    getInput() const;
 
     /* returns the audio session ID associated to this AudioRecord.
      *
@@ -295,7 +295,7 @@
      * Returned value:
      *  AudioRecord session ID.
      */
-            int    getSessionId();
+            int    getSessionId() const;
 
     /* obtains a buffer of "frameCount" frames. The buffer must be
      * filled entirely. If the track is stopped, obtainBuffer() returns
@@ -326,7 +326,7 @@
      * Such loss typically occurs when the user space process is blocked longer than the capacity of audio driver buffers.
      * Unit: the number of input audio frames
      */
-            unsigned int  getInputFramesLost();
+            unsigned int  getInputFramesLost() const;
 
 private:
     /* copying audio tracks is not allowed */
@@ -360,7 +360,7 @@
     sp<IMemory>             mCblkMemory;
     sp<ClientRecordThread>  mClientRecordThread;
     status_t                mReadyToRun;
-    Mutex                   mLock;
+    mutable Mutex           mLock;
     Condition               mCondition;
 
     uint32_t                mFrameCount;
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 1916ac5..89bc95d 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -157,7 +157,7 @@
                                         uint32_t samplingRate = 0,
                                         audio_format_t format = AUDIO_FORMAT_DEFAULT,
                                         uint32_t channels = AUDIO_CHANNEL_OUT_STEREO,
-                                        audio_policy_output_flags_t flags = AUDIO_POLICY_OUTPUT_FLAG_INDIRECT);
+                                        audio_policy_output_flags_t flags = AUDIO_POLICY_OUTPUT_FLAG_NONE);
     static status_t startOutput(audio_io_handle_t output,
                                 audio_stream_type_t stream,
                                 int session = 0);
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index 4d88297..bdd7747 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -52,7 +52,7 @@
                                         uint32_t samplingRate = 0,
                                         audio_format_t format = AUDIO_FORMAT_DEFAULT,
                                         uint32_t channels = 0,
-                                        audio_policy_output_flags_t flags = AUDIO_POLICY_OUTPUT_FLAG_INDIRECT) = 0;
+                                        audio_policy_output_flags_t flags = AUDIO_POLICY_OUTPUT_FLAG_NONE) = 0;
     virtual status_t startOutput(audio_io_handle_t output,
                                  audio_stream_type_t stream,
                                  int session = 0) = 0;
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index 6425886..39d58ab 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -23,6 +23,10 @@
 #include <utils/KeyedVector.h>
 #include <system/audio.h>
 
+// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
+// global, and not in android::
+struct sockaddr_in;
+
 namespace android {
 
 class Parcel;
@@ -59,6 +63,7 @@
     virtual status_t        attachAuxEffect(int effectId) = 0;
     virtual status_t        setParameter(int key, const Parcel& request) = 0;
     virtual status_t        getParameter(int key, Parcel* reply) = 0;
+    virtual status_t        setRetransmitEndpoint(const struct sockaddr_in* endpoint) = 0;
 
     // Invoke a generic method on the player by using opaque parcels
     // for the request and reply.
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 23a3e49..f7491d8 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -29,6 +29,10 @@
 #include <media/AudioSystem.h>
 #include <media/Metadata.h>
 
+// Fwd decl to make sure everyone agrees that the scope of struct sockaddr_in is
+// global, and not in android::
+struct sockaddr_in;
+
 namespace android {
 
 class Parcel;
@@ -141,6 +145,14 @@
     virtual status_t    setParameter(int key, const Parcel &request) = 0;
     virtual status_t    getParameter(int key, Parcel *reply) = 0;
 
+    // Right now, only the AAX TX player supports this functionality.  For now,
+    // provide a default implementation which indicates a lack of support for
+    // this functionality to make life easier for all of the other media player
+    // maintainers out there.
+    virtual status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint) {
+        return INVALID_OPERATION;
+    }
+
     // Invoke a generic method on the player by using opaque parcels
     // for the request and reply.
     //
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index d0b87c8..9cd5f9f 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_MEDIAPLAYER_H
 #define ANDROID_MEDIAPLAYER_H
 
+#include <arpa/inet.h>
+
 #include <binder/IMemory.h>
 #include <media/IMediaPlayerClient.h>
 #include <media/IMediaPlayer.h>
@@ -204,6 +206,7 @@
             status_t        attachAuxEffect(int effectId);
             status_t        setParameter(int key, const Parcel& request);
             status_t        getParameter(int key, Parcel* reply);
+            status_t        setRetransmitEndpoint(const char* addrString, uint16_t port);
 
 private:
             void            clear_l();
@@ -212,6 +215,7 @@
             status_t        getDuration_l(int *msec);
             status_t        attachNewPlayer(const sp<IMediaPlayer>& player);
             status_t        reset_l();
+            status_t        doSetRetransmitEndpoint(const sp<IMediaPlayer>& player);
 
     sp<IMediaPlayer>            mPlayer;
     thread_id_t                 mLockThreadId;
@@ -234,6 +238,8 @@
     int                         mVideoHeight;
     int                         mAudioSessionId;
     float                       mSendLevel;
+    struct sockaddr_in          mRetransmitEndpoint;
+    bool                        mRetransmitEndpointValid;
 };
 
 }; // namespace android
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 6735aff..fa1a416 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -118,6 +118,7 @@
     sp<FlushingState> mFlushingState;
 
     AString mComponentName;
+    uint32_t mQuirks;
     sp<IOMX> mOMX;
     IOMX::node_id mNode;
     sp<MemoryDealer> mDealer[2];
diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h
new file mode 100644
index 0000000..14dc1b8
--- /dev/null
+++ b/include/media/stagefright/MediaCodecList.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012, 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 MEDIA_CODEC_LIST_H_
+
+#define MEDIA_CODEC_LIST_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+
+#include <sys/types.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct MediaCodecList {
+    static const MediaCodecList *getInstance();
+
+    ssize_t findCodecByType(
+            const char *type, bool encoder, size_t startIndex = 0) const;
+
+    ssize_t findCodecByName(const char *name) const;
+
+    const char *getCodecName(size_t index) const;
+    bool codecHasQuirk(size_t index, const char *quirkName) const;
+
+private:
+    enum Section {
+        SECTION_TOPLEVEL,
+        SECTION_DECODERS,
+        SECTION_DECODER,
+        SECTION_ENCODERS,
+        SECTION_ENCODER,
+    };
+
+    struct CodecInfo {
+        AString mName;
+        bool mIsEncoder;
+        uint32_t mTypes;
+        uint32_t mQuirks;
+    };
+
+    static MediaCodecList *sCodecList;
+
+    status_t mInitCheck;
+    Section mCurrentSection;
+    int32_t mDepth;
+
+    Vector<CodecInfo> mCodecInfos;
+    KeyedVector<AString, size_t> mCodecQuirks;
+    KeyedVector<AString, size_t> mTypes;
+
+    MediaCodecList();
+    ~MediaCodecList();
+
+    status_t initCheck() const;
+    void parseXMLFile(FILE *file);
+
+    static void StartElementHandlerWrapper(
+            void *me, const char *name, const char **attrs);
+
+    static void EndElementHandlerWrapper(void *me, const char *name);
+
+    void startElementHandler(const char *name, const char **attrs);
+    void endElementHandler(const char *name);
+
+    status_t addMediaCodecFromAttributes(bool encoder, const char **attrs);
+    void addMediaCodec(bool encoder, const char *name, const char *type = NULL);
+
+    status_t addQuirk(const char **attrs);
+    status_t addTypeFromAttributes(const char **attrs);
+    void addType(const char *name);
+
+    DISALLOW_EVIL_CONSTRUCTORS(MediaCodecList);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_CODEC_LIST_H_
+
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index e541c18..392ea87 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -26,6 +26,7 @@
 
 namespace android {
 
+struct MediaCodecList;
 class MemoryDealer;
 struct OMXCodecObserver;
 struct CodecProfileLevel;
@@ -82,12 +83,35 @@
     // from MediaBufferObserver
     virtual void signalBufferReturned(MediaBuffer *buffer);
 
+    enum Quirks {
+        kNeedsFlushBeforeDisable              = 1,
+        kWantsNALFragments                    = 2,
+        kRequiresLoadedToIdleAfterAllocation  = 4,
+        kRequiresAllocateBufferOnInputPorts   = 8,
+        kRequiresFlushCompleteEmulation       = 16,
+        kRequiresAllocateBufferOnOutputPorts  = 32,
+        kRequiresFlushBeforeShutdown          = 64,
+        kDefersOutputBufferAllocation         = 128,
+        kDecoderLiesAboutNumberOfChannels     = 256,
+        kInputBufferSizesAreBogus             = 512,
+        kSupportsMultipleFramesPerInputBuffer = 1024,
+        kAvoidMemcopyInputRecordingFrames     = 2048,
+        kRequiresLargerEncoderOutputBuffer    = 4096,
+        kOutputBuffersAreUnreadable           = 8192,
+    };
+
     // for use by ACodec
     static void findMatchingCodecs(
             const char *mime,
             bool createEncoder, const char *matchComponentName,
             uint32_t flags,
-            Vector<String8> *matchingCodecs);
+            Vector<String8> *matchingCodecs,
+            Vector<uint32_t> *matchingCodecQuirks = NULL);
+
+    static uint32_t getComponentQuirks(
+            const MediaCodecList *list, size_t index);
+
+    static bool findCodecQuirks(const char *componentName, uint32_t *quirks);
 
 protected:
     virtual ~OMXCodec();
@@ -125,23 +149,6 @@
         SHUTTING_DOWN,
     };
 
-    enum Quirks {
-        kNeedsFlushBeforeDisable              = 1,
-        kWantsNALFragments                    = 2,
-        kRequiresLoadedToIdleAfterAllocation  = 4,
-        kRequiresAllocateBufferOnInputPorts   = 8,
-        kRequiresFlushCompleteEmulation       = 16,
-        kRequiresAllocateBufferOnOutputPorts  = 32,
-        kRequiresFlushBeforeShutdown          = 64,
-        kDefersOutputBufferAllocation         = 128,
-        kDecoderLiesAboutNumberOfChannels     = 256,
-        kInputBufferSizesAreBogus             = 512,
-        kSupportsMultipleFramesPerInputBuffer = 1024,
-        kAvoidMemcopyInputRecordingFrames     = 2048,
-        kRequiresLargerEncoderOutputBuffer    = 4096,
-        kOutputBuffersAreUnreadable           = 8192,
-    };
-
     enum BufferStatus {
         OWNED_BY_US,
         OWNED_BY_COMPONENT,
@@ -327,9 +334,6 @@
 
     status_t configureCodec(const sp<MetaData> &meta);
 
-    static uint32_t getComponentQuirks(
-            const char *componentName, bool isEncoder);
-
     void restorePatchedDataPointer(BufferInfo *info);
 
     status_t applyRotation();
diff --git a/include/utils/Vector.h b/include/utils/Vector.h
index b908e2a..5b5296b 100644
--- a/include/utils/Vector.h
+++ b/include/utils/Vector.h
@@ -186,8 +186,8 @@
      inline const_iterator end() const   { return array() + size(); }
      inline void reserve(size_t n) { setCapacity(n); }
      inline bool empty() const{ return isEmpty(); }
-     inline void push_back(const TYPE& item)  { insertAt(item, size()); }
-     inline void push_front(const TYPE& item) { insertAt(item, 0); }
+     inline void push_back(const TYPE& item)  { insertAt(item, size(), 1); }
+     inline void push_front(const TYPE& item) { insertAt(item, 0, 1); }
      inline iterator erase(iterator pos) {
          return begin() + removeItemsAt(pos-array());
      }
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 0fe7bd8..fba5bab 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -224,9 +224,6 @@
      * <p>{@code alias} allows the chooser to preselect an existing
      * alias which will still be subject to user confirmation.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#USE_CREDENTIALS}.
-     *
      * @param activity The {@link Activity} context to use for
      *     launching the new sub-Activity to prompt the user to select
      *     a private key; used only to call startActivity(); must not
@@ -294,9 +291,6 @@
      * Returns the {@code PrivateKey} for the requested alias, or null
      * if no there is no result.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#USE_CREDENTIALS}.
-     *
      * @param alias The alias of the desired private key, typically
      * returned via {@link KeyChainAliasCallback#alias}.
      * @throws KeyChainException if the alias was valid but there was some problem accessing it.
@@ -325,9 +319,6 @@
      * Returns the {@code X509Certificate} chain for the requested
      * alias, or null if no there is no result.
      *
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#USE_CREDENTIALS}.
-     *
      * @param alias The alias of the desired certificate chain, typically
      * returned via {@link KeyChainAliasCallback#alias}.
      * @throws KeyChainException if the alias was valid but there was some problem accessing it.
diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk
index fd116b7..d449298 100644
--- a/libs/binder/Android.mk
+++ b/libs/binder/Android.mk
@@ -24,7 +24,6 @@
     MemoryDealer.cpp \
     MemoryBase.cpp \
     MemoryHeapBase.cpp \
-    MemoryHeapPmem.cpp \
     Parcel.cpp \
     PermissionCache.cpp \
     ProcessState.cpp \
diff --git a/libs/binder/MemoryHeapPmem.cpp b/libs/binder/MemoryHeapPmem.cpp
deleted file mode 100644
index 66bcf4d..0000000
--- a/libs/binder/MemoryHeapPmem.cpp
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2008 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 "MemoryHeapPmem"
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-
-#include <cutils/log.h>
-
-#include <binder/MemoryHeapPmem.h>
-#include <binder/MemoryHeapBase.h>
-
-#ifdef HAVE_ANDROID_OS
-#include <linux/android_pmem.h>
-#endif
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-MemoryHeapPmem::MemoryPmem::MemoryPmem(const sp<MemoryHeapPmem>& heap)
-    : BnMemory(), mClientHeap(heap)
-{
-}
-
-MemoryHeapPmem::MemoryPmem::~MemoryPmem() {
-    if (mClientHeap != NULL) {
-        mClientHeap->remove(this);
-    }
-}
-
-// ---------------------------------------------------------------------------
-
-class SubRegionMemory : public MemoryHeapPmem::MemoryPmem {
-public:
-    SubRegionMemory(const sp<MemoryHeapPmem>& heap, ssize_t offset, size_t size);
-    virtual ~SubRegionMemory();
-    virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;
-private:
-    friend class MemoryHeapPmem;
-    void revoke();
-    size_t              mSize;
-    ssize_t             mOffset;
-};
-
-SubRegionMemory::SubRegionMemory(const sp<MemoryHeapPmem>& heap,
-        ssize_t offset, size_t size)
-    : MemoryHeapPmem::MemoryPmem(heap), mSize(size), mOffset(offset)
-{
-#ifndef NDEBUG
-    void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + offset);
-    memset(start_ptr, 0xda, size);
-#endif
-
-#ifdef HAVE_ANDROID_OS
-    if (size > 0) {
-        const size_t pagesize = getpagesize();
-        size = (size + pagesize-1) & ~(pagesize-1);
-        int our_fd = heap->heapID();
-        struct pmem_region sub = { offset, size };
-        int err = ioctl(our_fd, PMEM_MAP, &sub);
-        ALOGE_IF(err<0, "PMEM_MAP failed (%s), "
-                "mFD=%d, sub.offset=%lu, sub.size=%lu",
-                strerror(errno), our_fd, sub.offset, sub.len);
-}
-#endif
-}
-
-sp<IMemoryHeap> SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const
-{
-    if (offset) *offset = mOffset;
-    if (size)   *size = mSize;
-    return getHeap();
-}
-
-SubRegionMemory::~SubRegionMemory()
-{
-    revoke();
-}
-
-
-void SubRegionMemory::revoke()
-{
-    // NOTE: revoke() doesn't need to be protected by a lock because it
-    // can only be called from MemoryHeapPmem::revoke(), which means
-    // that we can't be in ~SubRegionMemory(), or in ~SubRegionMemory(),
-    // which means MemoryHeapPmem::revoke() wouldn't have been able to 
-    // promote() it.
-    
-#ifdef HAVE_ANDROID_OS
-    if (mSize != 0) {
-        const sp<MemoryHeapPmem>& heap(getHeap());
-        int our_fd = heap->heapID();
-        struct pmem_region sub;
-        sub.offset = mOffset;
-        sub.len = mSize;
-        int err = ioctl(our_fd, PMEM_UNMAP, &sub);
-        ALOGE_IF(err<0, "PMEM_UNMAP failed (%s), "
-                "mFD=%d, sub.offset=%lu, sub.size=%lu",
-                strerror(errno), our_fd, sub.offset, sub.len);
-        mSize = 0;
-    }
-#endif
-}
-
-// ---------------------------------------------------------------------------
-
-MemoryHeapPmem::MemoryHeapPmem(const sp<MemoryHeapBase>& pmemHeap,
-        uint32_t flags)
-    : MemoryHeapBase()
-{
-    char const * const device = pmemHeap->getDevice();
-#ifdef HAVE_ANDROID_OS
-    if (device) {
-        int fd = open(device, O_RDWR | (flags & NO_CACHING ? O_SYNC : 0));
-        ALOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno));
-        if (fd >= 0) {
-            int err = ioctl(fd, PMEM_CONNECT, pmemHeap->heapID());
-            if (err < 0) {
-                ALOGE("PMEM_CONNECT failed (%s), mFD=%d, sub-fd=%d",
-                        strerror(errno), fd, pmemHeap->heapID());
-                close(fd);
-            } else {
-                // everything went well...
-                mParentHeap = pmemHeap;
-                MemoryHeapBase::init(fd, 
-                        pmemHeap->getBase(),
-                        pmemHeap->getSize(),
-                        pmemHeap->getFlags() | flags,
-                        device);
-            }
-        }
-    }
-#else
-    mParentHeap = pmemHeap;
-    MemoryHeapBase::init( 
-            dup(pmemHeap->heapID()),
-            pmemHeap->getBase(),
-            pmemHeap->getSize(),
-            pmemHeap->getFlags() | flags,
-            device);
-#endif
-}
-
-MemoryHeapPmem::~MemoryHeapPmem()
-{
-}
-
-sp<IMemory> MemoryHeapPmem::mapMemory(size_t offset, size_t size)
-{
-    sp<MemoryPmem> memory = createMemory(offset, size);
-    if (memory != 0) {
-        Mutex::Autolock _l(mLock);
-        mAllocations.add(memory);
-    }
-    return memory;
-}
-
-sp<MemoryHeapPmem::MemoryPmem> MemoryHeapPmem::createMemory(
-        size_t offset, size_t size)
-{
-    sp<SubRegionMemory> memory;
-    if (heapID() > 0) 
-        memory = new SubRegionMemory(this, offset, size);
-    return memory;
-}
-
-status_t MemoryHeapPmem::slap()
-{
-#ifdef HAVE_ANDROID_OS
-    size_t size = getSize();
-    const size_t pagesize = getpagesize();
-    size = (size + pagesize-1) & ~(pagesize-1);
-    int our_fd = getHeapID();
-    struct pmem_region sub = { 0, size };
-    int err = ioctl(our_fd, PMEM_MAP, &sub);
-    ALOGE_IF(err<0, "PMEM_MAP failed (%s), "
-            "mFD=%d, sub.offset=%lu, sub.size=%lu",
-            strerror(errno), our_fd, sub.offset, sub.len);
-    return -errno;
-#else
-    return NO_ERROR;
-#endif
-}
-
-status_t MemoryHeapPmem::unslap()
-{
-#ifdef HAVE_ANDROID_OS
-    size_t size = getSize();
-    const size_t pagesize = getpagesize();
-    size = (size + pagesize-1) & ~(pagesize-1);
-    int our_fd = getHeapID();
-    struct pmem_region sub = { 0, size };
-    int err = ioctl(our_fd, PMEM_UNMAP, &sub);
-    ALOGE_IF(err<0, "PMEM_UNMAP failed (%s), "
-            "mFD=%d, sub.offset=%lu, sub.size=%lu",
-            strerror(errno), our_fd, sub.offset, sub.len);
-    return -errno;
-#else
-    return NO_ERROR;
-#endif
-}
-
-void MemoryHeapPmem::revoke()
-{
-    SortedVector< wp<MemoryPmem> > allocations;
-
-    { // scope for lock
-        Mutex::Autolock _l(mLock);
-        allocations = mAllocations;
-    }
-    
-    ssize_t count = allocations.size();
-    for (ssize_t i=0 ; i<count ; i++) {
-        sp<MemoryPmem> memory(allocations[i].promote());
-        if (memory != 0)
-            memory->revoke();
-    }
-}
-
-void MemoryHeapPmem::remove(const wp<MemoryPmem>& memory)
-{
-    Mutex::Autolock _l(mLock);
-    mAllocations.remove(memory);
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index f96fe50..9fa412c 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -286,8 +286,8 @@
 {
     if (mThreadPoolStarted) {
         int32_t s = android_atomic_add(1, &mThreadPoolSeq);
-        char buf[32];
-        sprintf(buf, "Binder Thread #%d", s);
+        char buf[16];
+        snprintf(buf, sizeof(buf), "Binder_%X", s);
         ALOGV("Spawning new pooled thread, name=%s\n", buf);
         sp<Thread> t = new PoolThread(isMain);
         t->run(buf);
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 3df105b..92a7573 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -39,6 +39,8 @@
 #define MAX_TEXT_CACHE_WIDTH 2048
 #define TEXTURE_BORDER_SIZE 2
 
+#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
+
 ///////////////////////////////////////////////////////////////////////////////
 // CacheTextureLine
 ///////////////////////////////////////////////////////////////////////////////
@@ -163,6 +165,43 @@
     }
 }
 
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+        SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
+    const float halfWidth = glyph->mBitmapWidth * 0.5f;
+    const float height = glyph->mBitmapHeight;
+
+    vOffset += glyph->mBitmapTop + height;
+
+    SkPoint destination[4];
+    measure.getPosTan(x + hOffset +  glyph->mBitmapLeft + halfWidth, position, tangent);
+
+    // Move along the tangent and offset by the normal
+    destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
+            -tangent->fY * halfWidth + tangent->fX * vOffset);
+    destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
+            tangent->fY * halfWidth + tangent->fX * vOffset);
+    destination[2].set(destination[1].fX + tangent->fY * height,
+            destination[1].fY - tangent->fX * height);
+    destination[3].set(destination[0].fX + tangent->fY * height,
+            destination[0].fY - tangent->fX * height);
+
+    const float u1 = glyph->mBitmapMinU;
+    const float u2 = glyph->mBitmapMaxU;
+    const float v1 = glyph->mBitmapMinV;
+    const float v2 = glyph->mBitmapMaxV;
+
+    mState->appendRotatedMeshQuad(
+            position->fX + destination[0].fX,
+            position->fY + destination[0].fY, u1, v2,
+            position->fX + destination[1].fX,
+            position->fY + destination[1].fY, u2, v2,
+            position->fX + destination[2].fX,
+            position->fY + destination[2].fY, u2, v1,
+            position->fX + destination[3].fX,
+            position->fY + destination[3].fY, u1, v1,
+            glyph->mCachedTextureLine->mCacheTexture);
+}
+
 CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
     CachedGlyphInfo* cachedGlyph = NULL;
     ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
@@ -198,6 +237,56 @@
             0, 0, NULL, positions);
 }
 
+void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+        int numGlyphs, SkPath* path, float hOffset, float vOffset) {
+    if (numGlyphs == 0 || text == NULL || len == 0) {
+        return;
+    }
+
+    text += start;
+
+    int glyphsCount = 0;
+    SkFixed prevRsbDelta = 0;
+
+    float penX = 0.0f;
+
+    SkPoint position;
+    SkVector tangent;
+
+    SkPathMeasure measure(*path, false);
+    float pathLength = SkScalarToFloat(measure.getLength());
+
+    if (paint->getTextAlign() != SkPaint::kLeft_Align) {
+        float textWidth = SkScalarToFloat(paint->measureText(text, len));
+        float pathOffset = pathLength;
+        if (paint->getTextAlign() == SkPaint::kCenter_Align) {
+            textWidth *= 0.5f;
+            pathOffset *= 0.5f;
+        }
+        penX += pathOffset - textWidth;
+    }
+
+    while (glyphsCount < numGlyphs && penX < pathLength) {
+        glyph_t glyph = GET_GLYPH(text);
+
+        if (IS_END_OF_STRING(glyph)) {
+            break;
+        }
+
+        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+        penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
+        prevRsbDelta = cachedGlyph->mRsbDelta;
+
+        if (cachedGlyph->mIsValid) {
+            drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
+        }
+
+        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+
+        glyphsCount++;
+    }
+}
+
 void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
         int numGlyphs, Rect *bounds) {
     if (bounds == NULL) {
@@ -208,19 +297,13 @@
     render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, NULL);
 }
 
-#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
-
 void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
         int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
-        uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions) {
+        uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
     if (numGlyphs == 0 || text == NULL || len == 0) {
         return;
     }
 
-    int glyphsCount = 0;
-
-    text += start;
-
     static RenderGlyph gRenderGlyph[] = {
             &android::uirenderer::Font::drawCachedGlyph,
             &android::uirenderer::Font::drawCachedGlyphBitmap,
@@ -228,14 +311,15 @@
     };
     RenderGlyph render = gRenderGlyph[mode];
 
+    text += start;
+    int glyphsCount = 0;
+
     if (CC_LIKELY(positions == NULL)) {
         SkFixed prevRsbDelta = 0;
 
-        float penX = x;
+        float penX = x + 0.5f;
         int penY = y;
 
-        penX += 0.5f;
-
         while (glyphsCount < numGlyphs) {
             glyph_t glyph = GET_GLYPH(text);
 
@@ -245,7 +329,7 @@
             }
 
             CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
-            penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
+            penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
             prevRsbDelta = cachedGlyph->mRsbDelta;
 
             // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
@@ -582,6 +666,7 @@
             cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
         }
     }
+
     cachedGlyph->mIsValid = true;
 }
 
@@ -758,15 +843,9 @@
     mDrawn = true;
 }
 
-void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
-        float x2, float y2, float u2, float v2,
-        float x3, float y3, float u3, float v3,
+void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
+        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
         float x4, float y4, float u4, float v4, CacheTexture* texture) {
-
-    if (mClip &&
-            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
-        return;
-    }
     if (texture != mCurrentCacheTexture) {
         if (mCurrentQuadIndex != 0) {
             // First, draw everything stored already which uses the previous texture
@@ -802,6 +881,18 @@
     (*currentPos++) = v4;
 
     mCurrentQuadIndex++;
+}
+
+void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
+        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
+        float x4, float y4, float u4, float v4, CacheTexture* texture) {
+
+    if (mClip &&
+            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
+        return;
+    }
+
+    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
 
     if (mBounds) {
         mBounds->left = fmin(mBounds->left, x1);
@@ -816,6 +907,25 @@
     }
 }
 
+void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
+        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
+        float x4, float y4, float u4, float v4, CacheTexture* texture) {
+
+    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
+
+    if (mBounds) {
+        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
+        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
+        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
+        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
+    }
+
+    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
+        issueDrawCommand();
+        mCurrentQuadIndex = 0;
+    }
+}
+
 uint32_t FontRenderer::getRemainingCacheCapacity() {
     uint32_t remainingCapacity = 0;
     float totalPixels = 0;
@@ -956,6 +1066,21 @@
     return mDrawn;
 }
 
+bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
+        uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
+        float hOffset, float vOffset, Rect* bounds) {
+    if (!mCurrentFont) {
+        ALOGE("No font set");
+        return false;
+    }
+
+    initRender(clip, bounds);
+    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
+    finishRender();
+
+    return mDrawn;
+}
+
 void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
     // Compute gaussian weights for the blur
     // e is the euler's number
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index b767be5..4fc5862 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -24,6 +24,8 @@
 
 #include <SkScalerContext.h>
 #include <SkPaint.h>
+#include <SkPathMeasure.h>
+#include <SkPoint.h>
 
 #include <GLES2/gl2.h>
 
@@ -155,6 +157,9 @@
     void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
             int numGlyphs, int x, int y, const float* positions);
 
+    void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+            int numGlyphs, SkPath* path, float hOffset, float vOffset);
+
     /**
      * Creates a new font associated with the specified font state.
      */
@@ -200,6 +205,8 @@
     void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
             uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
             Rect* bounds, const float* pos);
+    void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+            SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
 
     CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit);
 
@@ -244,6 +251,9 @@
     // bounds is an out parameter
     bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
             uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds);
+    // bounds is an out parameter
+    bool renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
+            uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds);
 
     struct DropShadow {
         DropShadow() { };
@@ -305,7 +315,7 @@
     void allocateTextureMemory(CacheTexture* cacheTexture);
     void deallocateTextureMemory(CacheTexture* cacheTexture);
     void initTextTexture();
-    CacheTexture *createCacheTexture(int width, int height, bool allocate);
+    CacheTexture* createCacheTexture(int width, int height, bool allocate);
     void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
             uint32_t *retOriginX, uint32_t *retOriginY);
 
@@ -320,10 +330,18 @@
     void precacheLatin(SkPaint* paint);
 
     void issueDrawCommand();
+    void appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
+            float x2, float y2, float u2, float v2,
+            float x3, float y3, float u3, float v3,
+            float x4, float y4, float u4, float v4, CacheTexture* texture);
     void appendMeshQuad(float x1, float y1, float u1, float v1,
             float x2, float y2, float u2, float v2,
             float x3, float y3, float u3, float v3,
             float x4, float y4, float u4, float v4, CacheTexture* texture);
+    void appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
+            float x2, float y2, float u2, float v2,
+            float x3, float y3, float u3, float v3,
+            float x4, float y4, float u4, float v4, CacheTexture* texture);
 
     uint32_t mSmallCacheWidth;
     uint32_t mSmallCacheHeight;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index ebb6d88..e3148e8 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2300,15 +2300,6 @@
         return;
     }
 
-    float x = 0.0f;
-    float y = 0.0f;
-
-    const bool pureTranslate = mSnapshot->transform->isPureTranslate();
-    if (CC_LIKELY(pureTranslate)) {
-        x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
-        y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
-    }
-
     FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint);
     fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
             paint->getTextSize());
@@ -2326,41 +2317,30 @@
     setupDrawShader();
     setupDrawBlending(true, mode);
     setupDrawProgram();
-    setupDrawModelView(x, y, x, y, pureTranslate, true);
+    setupDrawModelView(0.0f, 0.0f, 0.0f, 0.0f, false, true);
     setupDrawTexture(fontRenderer.getTexture(true));
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms();
-    setupDrawShaderUniforms(pureTranslate);
+    setupDrawShaderUniforms(false);
 
-//    mat4 pathTransform;
-//    pathTransform.loadTranslate(hOffset, vOffset, 0.0f);
-//
-//    float offset = 0.0f;
-//    SkPathMeasure pathMeasure(*path, false);
-//
-//    if (paint->getTextAlign() != SkPaint::kLeft_Align) {
-//        SkScalar pathLength = pathMeasure.getLength();
-//        if (paint->getTextAlign() == SkPaint::kCenter_Align) {
-//            pathLength = SkScalarHalf(pathLength);
-//        }
-//        offset += SkScalarToFloat(pathLength);
-//    }
+    const Rect* clip = &mSnapshot->getLocalClip();
+    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
 
-//        SkScalar x;
-//        SkPath      tmp;
-//        SkMatrix    m(scaledMatrix);
-//
-//        m.postTranslate(xpos + hOffset, 0);
-//        if (matrix) {
-//            m.postConcat(*matrix);
-//        }
-//        morphpath(&tmp, *iterPath, meas, m);
-//        if (fDevice) {
-//            fDevice->drawPath(*this, tmp, iter.getPaint(), NULL, true);
-//        } else {
-//            this->drawPath(tmp, iter.getPaint(), NULL, true);
-//        }
-//    }
+#if RENDER_LAYERS_AS_REGIONS
+    const bool hasActiveLayer = hasLayer();
+#else
+    const bool hasActiveLayer = false;
+#endif
+
+    if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
+            hOffset, vOffset, hasActiveLayer ? &bounds : NULL)) {
+#if RENDER_LAYERS_AS_REGIONS
+        if (hasActiveLayer) {
+            mSnapshot->transform->mapRect(bounds);
+            dirtyLayerUnchecked(bounds, getRegion());
+        }
+#endif
+    }
 }
 
 void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
@@ -2497,7 +2477,7 @@
 }
 
 SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) {
-    if (!mHasDrawFilter || !paint) return paint;
+    if (CC_LIKELY(!mHasDrawFilter || !paint)) return paint;
 
     uint32_t flags = paint->getFlags();
 
diff --git a/libs/rs/scriptc/rs_allocation.rsh b/libs/rs/scriptc/rs_allocation.rsh
index a2f69d9..89696b8 100644
--- a/libs/rs/scriptc/rs_allocation.rsh
+++ b/libs/rs/scriptc/rs_allocation.rsh
@@ -298,5 +298,67 @@
 extern uint32_t __attribute__((overloadable))
     rsElementGetVectorSize(rs_element e);
 
+/**
+ * Fetch allocation in a way described by the sampler
+ * @param a 1D allocation to sample from
+ * @param s sampler state
+ * @param location to sample from
+ */
+extern const float4 __attribute__((overloadable))
+    rsSample(rs_allocation a, rs_sampler s, float location);
+/**
+ * Fetch allocation in a way described by the sampler
+ * @param a 1D allocation to sample from
+ * @param s sampler state
+ * @param location to sample from
+ * @param lod mip level to sample from, for fractional values
+ *            mip levels will be interpolated if
+ *            RS_SAMPLER_LINEAR_MIP_LINEAR is used
+ */
+extern const float4 __attribute__((overloadable))
+    rsSample(rs_allocation a, rs_sampler s, float location, float lod);
+
+/**
+ * Fetch allocation in a way described by the sampler
+ * @param a 2D allocation to sample from
+ * @param s sampler state
+ * @param location to sample from
+ */
+extern const float4 __attribute__((overloadable))
+    rsSample(rs_allocation a, rs_sampler s, float2 location);
+
+/**
+ * Fetch allocation in a way described by the sampler
+ * @param a 2D allocation to sample from
+ * @param s sampler state
+ * @param location to sample from
+ * @param lod mip level to sample from, for fractional values
+ *            mip levels will be interpolated if
+ *            RS_SAMPLER_LINEAR_MIP_LINEAR is used
+ */
+extern const float4 __attribute__((overloadable))
+    rsSample(rs_allocation a, rs_sampler s, float2 location, float lod);
+
+/**
+ * Fetch allocation in a way described by the sampler
+ * @param a 3D allocation to sample from
+ * @param s sampler state
+ * @param location to sample from
+ */
+extern const float4 __attribute__((overloadable))
+    rsSample(rs_allocation a, rs_sampler s, float3 location);
+
+/**
+ * Fetch allocation in a way described by the sampler
+ * @param a 3D allocation to sample from
+ * @param s sampler state
+ * @param location to sample from
+ * @param lod mip level to sample from, for fractional values
+ *            mip levels will be interpolated if
+ *            RS_SAMPLER_LINEAR_MIP_LINEAR is used
+ */
+extern const float4 __attribute__((overloadable))
+    rsSample(rs_allocation a, rs_sampler s, float3 location, float lod);
+
 #endif
 
diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh
index 7fdebdc..e3fde828 100644
--- a/libs/rs/scriptc/rs_graphics.rsh
+++ b/libs/rs/scriptc/rs_graphics.rsh
@@ -23,65 +23,6 @@
 #ifndef __RS_GRAPHICS_RSH__
 #define __RS_GRAPHICS_RSH__
 
-// These are API 15 once it get official
-typedef enum {
-    RS_DEPTH_FUNC_ALWAYS,
-    RS_DEPTH_FUNC_LESS,
-    RS_DEPTH_FUNC_LEQUAL,
-    RS_DEPTH_FUNC_GREATER,
-    RS_DEPTH_FUNC_GEQUAL,
-    RS_DEPTH_FUNC_EQUAL,
-    RS_DEPTH_FUNC_NOTEQUAL,
-
-    RS_DEPTH_FUNC_INVALID = 100,
-} rs_depth_func;
-
-typedef enum {
-    RS_BLEND_SRC_ZERO,                  // 0
-    RS_BLEND_SRC_ONE,                   // 1
-    RS_BLEND_SRC_DST_COLOR,             // 2
-    RS_BLEND_SRC_ONE_MINUS_DST_COLOR,   // 3
-    RS_BLEND_SRC_SRC_ALPHA,             // 4
-    RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA,   // 5
-    RS_BLEND_SRC_DST_ALPHA,             // 6
-    RS_BLEND_SRC_ONE_MINUS_DST_ALPHA,   // 7
-    RS_BLEND_SRC_SRC_ALPHA_SATURATE,    // 8
-
-    RS_BLEND_SRC_INVALID = 100,
-} rs_blend_src_func;
-
-typedef enum {
-    RS_BLEND_DST_ZERO,                  // 0
-    RS_BLEND_DST_ONE,                   // 1
-    RS_BLEND_DST_SRC_COLOR,             // 2
-    RS_BLEND_DST_ONE_MINUS_SRC_COLOR,   // 3
-    RS_BLEND_DST_SRC_ALPHA,             // 4
-    RS_BLEND_DST_ONE_MINUS_SRC_ALPHA,   // 5
-    RS_BLEND_DST_DST_ALPHA,             // 6
-    RS_BLEND_DST_ONE_MINUS_DST_ALPHA,   // 7
-
-    RS_BLEND_DST_INVALID = 100,
-} rs_blend_dst_func;
-
-typedef enum {
-    RS_CULL_BACK,
-    RS_CULL_FRONT,
-    RS_CULL_NONE,
-
-    RS_CULL_INVALID = 100,
-} rs_cull_mode;
-
-typedef enum {
-    RS_SAMPLER_NEAREST,
-    RS_SAMPLER_LINEAR,
-    RS_SAMPLER_LINEAR_MIP_LINEAR,
-    RS_SAMPLER_WRAP,
-    RS_SAMPLER_CLAMP,
-    RS_SAMPLER_LINEAR_MIP_NEAREST,
-
-    RS_SAMPLER_INVALID = 100,
-} rs_sampler_value;
-
 #if (defined(RS_VERSION) && (RS_VERSION >= 14))
 /**
  * Set the color target used for all subsequent rendering calls
diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh
index 5345a48..f8c2657 100644
--- a/libs/rs/scriptc/rs_types.rsh
+++ b/libs/rs/scriptc/rs_types.rsh
@@ -407,14 +407,14 @@
  *
  **/
 typedef enum {
-    RS_PRIMITIVE_POINT,
-    RS_PRIMITIVE_LINE,
-    RS_PRIMITIVE_LINE_STRIP,
-    RS_PRIMITIVE_TRIANGLE,
-    RS_PRIMITIVE_TRIANGLE_STRIP,
-    RS_PRIMITIVE_TRIANGLE_FAN,
+    RS_PRIMITIVE_POINT              = 0,
+    RS_PRIMITIVE_LINE               = 1,
+    RS_PRIMITIVE_LINE_STRIP         = 2,
+    RS_PRIMITIVE_TRIANGLE           = 3,
+    RS_PRIMITIVE_TRIANGLE_STRIP     = 4,
+    RS_PRIMITIVE_TRIANGLE_FAN       = 5,
 
-    RS_PRIMITIVE_INVALID = 100,
+    RS_PRIMITIVE_INVALID            = 100,
 } rs_primitive;
 
 /**
@@ -436,41 +436,41 @@
  * RS_* objects.  32 bit opaque handles.
  */
 typedef enum {
-    RS_TYPE_NONE,
+    RS_TYPE_NONE             = 0,
     //RS_TYPE_FLOAT_16,
-    RS_TYPE_FLOAT_32 = 2,
-    RS_TYPE_FLOAT_64,
-    RS_TYPE_SIGNED_8,
-    RS_TYPE_SIGNED_16,
-    RS_TYPE_SIGNED_32,
-    RS_TYPE_SIGNED_64,
-    RS_TYPE_UNSIGNED_8,
-    RS_TYPE_UNSIGNED_16,
-    RS_TYPE_UNSIGNED_32,
-    RS_TYPE_UNSIGNED_64,
+    RS_TYPE_FLOAT_32         = 2,
+    RS_TYPE_FLOAT_64         = 3,
+    RS_TYPE_SIGNED_8         = 4,
+    RS_TYPE_SIGNED_16        = 5,
+    RS_TYPE_SIGNED_32        = 6,
+    RS_TYPE_SIGNED_64        = 7,
+    RS_TYPE_UNSIGNED_8       = 8,
+    RS_TYPE_UNSIGNED_16      = 9,
+    RS_TYPE_UNSIGNED_32      = 10,
+    RS_TYPE_UNSIGNED_64      = 11,
 
-    RS_TYPE_BOOLEAN,
+    RS_TYPE_BOOLEAN          = 12,
 
-    RS_TYPE_UNSIGNED_5_6_5,
-    RS_TYPE_UNSIGNED_5_5_5_1,
-    RS_TYPE_UNSIGNED_4_4_4_4,
+    RS_TYPE_UNSIGNED_5_6_5   = 13,
+    RS_TYPE_UNSIGNED_5_5_5_1 = 14,
+    RS_TYPE_UNSIGNED_4_4_4_4 = 15,
 
-    RS_TYPE_MATRIX_4X4,
-    RS_TYPE_MATRIX_3X3,
-    RS_TYPE_MATRIX_2X2,
+    RS_TYPE_MATRIX_4X4       = 16,
+    RS_TYPE_MATRIX_3X3       = 17,
+    RS_TYPE_MATRIX_2X2       = 18,
 
-    RS_TYPE_ELEMENT = 1000,
-    RS_TYPE_TYPE,
-    RS_TYPE_ALLOCATION,
-    RS_TYPE_SAMPLER,
-    RS_TYPE_SCRIPT,
-    RS_TYPE_MESH,
-    RS_TYPE_PROGRAM_FRAGMENT,
-    RS_TYPE_PROGRAM_VERTEX,
-    RS_TYPE_PROGRAM_RASTER,
-    RS_TYPE_PROGRAM_STORE,
+    RS_TYPE_ELEMENT          = 1000,
+    RS_TYPE_TYPE             = 1001,
+    RS_TYPE_ALLOCATION       = 1002,
+    RS_TYPE_SAMPLER          = 1003,
+    RS_TYPE_SCRIPT           = 1004,
+    RS_TYPE_MESH             = 1005,
+    RS_TYPE_PROGRAM_FRAGMENT = 1006,
+    RS_TYPE_PROGRAM_VERTEX   = 1007,
+    RS_TYPE_PROGRAM_RASTER   = 1008,
+    RS_TYPE_PROGRAM_STORE    = 1009,
 
-    RS_TYPE_INVALID = 10000,
+    RS_TYPE_INVALID          = 10000,
 } rs_data_type;
 
 /**
@@ -482,16 +482,74 @@
  * representing texture formats.
  */
 typedef enum {
-    RS_KIND_USER,
+    RS_KIND_USER         = 0,
 
-    RS_KIND_PIXEL_L = 7,
-    RS_KIND_PIXEL_A,
-    RS_KIND_PIXEL_LA,
-    RS_KIND_PIXEL_RGB,
-    RS_KIND_PIXEL_RGBA,
-    RS_KIND_PIXEL_DEPTH,
+    RS_KIND_PIXEL_L      = 7,
+    RS_KIND_PIXEL_A      = 8,
+    RS_KIND_PIXEL_LA     = 9,
+    RS_KIND_PIXEL_RGB    = 10,
+    RS_KIND_PIXEL_RGBA   = 11,
+    RS_KIND_PIXEL_DEPTH  = 12,
 
-    RS_KIND_INVALID = 100,
+    RS_KIND_INVALID      = 100,
 } rs_data_kind;
 
+typedef enum {
+    RS_DEPTH_FUNC_ALWAYS        = 0,
+    RS_DEPTH_FUNC_LESS          = 1,
+    RS_DEPTH_FUNC_LEQUAL        = 2,
+    RS_DEPTH_FUNC_GREATER       = 3,
+    RS_DEPTH_FUNC_GEQUAL        = 4,
+    RS_DEPTH_FUNC_EQUAL         = 5,
+    RS_DEPTH_FUNC_NOTEQUAL      = 6,
+
+    RS_DEPTH_FUNC_INVALID       = 100,
+} rs_depth_func;
+
+typedef enum {
+    RS_BLEND_SRC_ZERO                   = 0,
+    RS_BLEND_SRC_ONE                    = 1,
+    RS_BLEND_SRC_DST_COLOR              = 2,
+    RS_BLEND_SRC_ONE_MINUS_DST_COLOR    = 3,
+    RS_BLEND_SRC_SRC_ALPHA              = 4,
+    RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA    = 5,
+    RS_BLEND_SRC_DST_ALPHA              = 6,
+    RS_BLEND_SRC_ONE_MINUS_DST_ALPHA    = 7,
+    RS_BLEND_SRC_SRC_ALPHA_SATURATE     = 8,
+
+    RS_BLEND_SRC_INVALID                = 100,
+} rs_blend_src_func;
+
+typedef enum {
+    RS_BLEND_DST_ZERO                   = 0,
+    RS_BLEND_DST_ONE                    = 1,
+    RS_BLEND_DST_SRC_COLOR              = 2,
+    RS_BLEND_DST_ONE_MINUS_SRC_COLOR    = 3,
+    RS_BLEND_DST_SRC_ALPHA              = 4,
+    RS_BLEND_DST_ONE_MINUS_SRC_ALPHA    = 5,
+    RS_BLEND_DST_DST_ALPHA              = 6,
+    RS_BLEND_DST_ONE_MINUS_DST_ALPHA    = 7,
+
+    RS_BLEND_DST_INVALID                = 100,
+} rs_blend_dst_func;
+
+typedef enum {
+    RS_CULL_BACK     = 0,
+    RS_CULL_FRONT    = 1,
+    RS_CULL_NONE     = 2,
+
+    RS_CULL_INVALID  = 100,
+} rs_cull_mode;
+
+typedef enum {
+    RS_SAMPLER_NEAREST              = 0,
+    RS_SAMPLER_LINEAR               = 1,
+    RS_SAMPLER_LINEAR_MIP_LINEAR    = 2,
+    RS_SAMPLER_WRAP                 = 3,
+    RS_SAMPLER_CLAMP                = 4,
+    RS_SAMPLER_LINEAR_MIP_NEAREST   = 5,
+
+    RS_SAMPLER_INVALID              = 100,
+} rs_sampler_value;
+
 #endif
diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp
index e52cf2f..be398ee 100644
--- a/libs/utils/BlobCache.cpp
+++ b/libs/utils/BlobCache.cpp
@@ -183,7 +183,7 @@
 status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count)
         const {
     if (count != 0) {
-        ALOGE("flatten: nonzero fd count: %d", count);
+        ALOGE("flatten: nonzero fd count: %zu", count);
         return BAD_VALUE;
     }
 
@@ -234,7 +234,7 @@
     mCacheEntries.clear();
 
     if (count != 0) {
-        ALOGE("unflatten: nonzero fd count: %d", count);
+        ALOGE("unflatten: nonzero fd count: %zu", count);
         return BAD_VALUE;
     }
 
diff --git a/libs/utils/Debug.cpp b/libs/utils/Debug.cpp
index f7988ec..e8ac983 100644
--- a/libs/utils/Debug.cpp
+++ b/libs/utils/Debug.cpp
@@ -199,7 +199,7 @@
     if ((int32_t)length < 0) {
         if (singleLineBytesCutoff < 0) func(cookie, "\n");
         char buf[64];
-        sprintf(buf, "(bad length: %d)", length);
+        sprintf(buf, "(bad length: %zu)", length);
         func(cookie, buf);
         return;
     }
diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp
index bfcb2da..624e917 100644
--- a/libs/utils/Static.cpp
+++ b/libs/utils/Static.cpp
@@ -57,8 +57,8 @@
     virtual status_t writeLines(const struct iovec& vec, size_t N)
     {
         //android_writevLog(&vec, N);       <-- this is now a no-op
-        if (N != 1) ALOGI("WARNING: writeLines N=%d\n", N);
-        ALOGI("%.*s", vec.iov_len, (const char*) vec.iov_base);
+        if (N != 1) ALOGI("WARNING: writeLines N=%zu\n", N);
+        ALOGI("%.*s", (int)vec.iov_len, (const char*) vec.iov_base);
         return NO_ERROR;
     }
 };
diff --git a/libs/utils/StopWatch.cpp b/libs/utils/StopWatch.cpp
index 595aec3..b1708d62 100644
--- a/libs/utils/StopWatch.cpp
+++ b/libs/utils/StopWatch.cpp
@@ -20,6 +20,10 @@
 #include <stdlib.h>
 #include <stdio.h>
 
+/* for PRId64 */
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
 #include <utils/Log.h>
 #include <utils/Errors.h>
 #include <utils/StopWatch.h>
@@ -39,11 +43,11 @@
 {
     nsecs_t elapsed = elapsedTime();
     const int n = mNumLaps;
-    ALOGD("StopWatch %s (us): %lld ", mName, ns2us(elapsed));
+    ALOGD("StopWatch %s (us): %" PRId64 " ", mName, ns2us(elapsed));
     for (int i=0 ; i<n ; i++) {
         const nsecs_t soFar = mLaps[i].soFar;
         const nsecs_t thisLap = mLaps[i].thisLap;
-        ALOGD(" [%d: %lld, %lld]", i, ns2us(soFar), ns2us(thisLap));
+        ALOGD(" [%d: %" PRId64 ", %" PRId64, i, ns2us(soFar), ns2us(thisLap));
     }
 }
 
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 4c70e9d..e663e91 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -35,6 +35,7 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.net.InetSocketAddress;
 import java.util.Map;
 import java.util.Set;
 import java.lang.ref.WeakReference;
@@ -1486,6 +1487,52 @@
      */
     public native static int native_pullBatteryData(Parcel reply);
 
+    /**
+     * Sets the target UDP re-transmit endpoint for the low level player.
+     * Generally, the address portion of the endpoint is an IP multicast
+     * address, although a unicast address would be equally valid.  When a valid
+     * retransmit endpoint has been set, the media player will not decode and
+     * render the media presentation locally.  Instead, the player will attempt
+     * to re-multiplex its media data using the Android@Home RTP profile and
+     * re-transmit to the target endpoint.  Receiver devices (which may be
+     * either the same as the transmitting device or different devices) may
+     * instantiate, prepare, and start a receiver player using a setDataSource
+     * URL of the form...
+     *
+     * aahRX://&lt;multicastIP&gt;:&lt;port&gt;
+     *
+     * to receive, decode and render the re-transmitted content.
+     *
+     * setRetransmitEndpoint may only be called before setDataSource has been
+     * called; while the player is in the Idle state.
+     *
+     * @param endpoint the address and UDP port of the re-transmission target or
+     * null if no re-transmission is to be performed.
+     * @throws IllegalStateException if it is called in an invalid state
+     * @throws IllegalArgumentException if the retransmit endpoint is supplied,
+     * but invalid.
+     *
+     * {@hide} pending API council
+     */
+    public void setRetransmitEndpoint(InetSocketAddress endpoint)
+            throws IllegalStateException, IllegalArgumentException
+    {
+        String addrString = null;
+        int port = 0;
+
+        if (null != endpoint) {
+            addrString = endpoint.getAddress().getHostAddress();
+            port = endpoint.getPort();
+        }
+
+        int ret = native_setRetransmitEndpoint(addrString, port);
+        if (ret != 0) {
+            throw new IllegalArgumentException("Illegal re-transmit endpoint; native ret " + ret);
+        }
+    }
+
+    private native final int native_setRetransmitEndpoint(String addrString, int port);
+
     @Override
     protected void finalize() { native_finalize(); }
 
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index f3a5668..6ec5d20 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -722,6 +722,45 @@
     return service->pullBatteryData(reply);
 }
 
+static jint
+android_media_MediaPlayer_setRetransmitEndpoint(JNIEnv *env, jobject thiz,
+                                                jstring addrString, jint port) {
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return INVALID_OPERATION;
+    }
+
+    const char *cAddrString = NULL;
+
+    if (NULL != addrString) {
+        cAddrString = env->GetStringUTFChars(addrString, NULL);
+        if (cAddrString == NULL) {  // Out of memory
+            return NO_MEMORY;
+        }
+    }
+    ALOGV("setRetransmitEndpoint: %s:%d",
+            cAddrString ? cAddrString : "(null)", port);
+
+    status_t ret;
+    if (cAddrString && (port > 0xFFFF)) {
+        ret = BAD_VALUE;
+    } else {
+        ret = mp->setRetransmitEndpoint(cAddrString,
+                static_cast<uint16_t>(port));
+    }
+
+    if (NULL != addrString) {
+        env->ReleaseStringUTFChars(addrString, cAddrString);
+    }
+
+    if (ret == INVALID_OPERATION ) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+    }
+
+    return ret;
+}
+
 static jboolean
 android_media_MediaPlayer_setParameter(JNIEnv *env, jobject thiz, jint key, jobject java_request)
 {
@@ -799,6 +838,7 @@
     {"native_pullBatteryData", "(Landroid/os/Parcel;)I",        (void *)android_media_MediaPlayer_pullBatteryData},
     {"setParameter",        "(ILandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_setParameter},
     {"getParameter",        "(ILandroid/os/Parcel;)V",          (void *)android_media_MediaPlayer_getParameter},
+    {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I",  (void *)android_media_MediaPlayer_setRetransmitEndpoint},
 };
 
 static const char* const kClassPathName = "android/media/MediaPlayer";
diff --git a/media/libaah_rtp/aah_decoder_pump.cpp b/media/libaah_rtp/aah_decoder_pump.cpp
index 72fe43b..28b8c7b 100644
--- a/media/libaah_rtp/aah_decoder_pump.cpp
+++ b/media/libaah_rtp/aah_decoder_pump.cpp
@@ -159,8 +159,8 @@
 
                 res = renderer_->queueTimedBuffer(pcm_payload, ts);
                 if (res != OK) {
-                    ALOGE("Failed to queue %d byte audio track buffer with media"
-                          " PTS %lld. (res = %d)", decoded_amt, ts, res);
+                    ALOGE("Failed to queue %d byte audio track buffer with"
+                          " media PTS %lld. (res = %d)", decoded_amt, ts, res);
                 } else {
                     last_queued_pts_valid_ = true;
                     last_queued_pts_ = ts;
@@ -291,8 +291,8 @@
     // thread_status_.
     thread_status_ = decoder_->start(format_.get());
     if (OK != thread_status_) {
-        ALOGE("AAH_DecoderPump's work thread failed to start decoder (res = %d)",
-                thread_status_);
+        ALOGE("AAH_DecoderPump's work thread failed to start decoder"
+              " (res = %d)", thread_status_);
         return NULL;
     }
 
diff --git a/media/libaah_rtp/aah_rx_player.h b/media/libaah_rtp/aah_rx_player.h
index 7a1b6e3..ba5617e 100644
--- a/media/libaah_rtp/aah_rx_player.h
+++ b/media/libaah_rtp/aah_rx_player.h
@@ -217,14 +217,15 @@
         status_t getStatus() const { return status_; }
 
       protected:
-        virtual ~Substream() {
-            shutdown();
-        }
+        virtual ~Substream();
 
       private:
         void                cleanupDecoder();
         bool                shouldAbort(const char* log_tag);
         void                processCompletedBuffer();
+        bool                setupSubstreamMeta();
+        bool                setupMP3SubstreamMeta();
+        bool                setupAACSubstreamMeta();
         bool                setupSubstreamType(uint8_t substream_type,
                                                uint8_t codec_type);
 
@@ -235,12 +236,16 @@
         bool                substream_details_known_;
         uint8_t             substream_type_;
         uint8_t             codec_type_;
+        const char*         codec_mime_type_;
         sp<MetaData>        substream_meta_;
 
         MediaBuffer*        buffer_in_progress_;
         uint32_t            expected_buffer_size_;
         uint32_t            buffer_filled_;
 
+        Vector<uint8_t>     aux_data_in_progress_;
+        uint32_t            aux_data_expected_size_;
+
         sp<AAH_DecoderPump> decoder_;
 
         static int64_t      kAboutToUnderflowThreshold;
diff --git a/media/libaah_rtp/aah_rx_player_core.cpp b/media/libaah_rtp/aah_rx_player_core.cpp
index d2b3386..d6b31fd 100644
--- a/media/libaah_rtp/aah_rx_player_core.cpp
+++ b/media/libaah_rtp/aah_rx_player_core.cpp
@@ -431,8 +431,8 @@
         // Looks like a NAK packet; make sure its long enough.
 
         if (amt < static_cast<ssize_t>(sizeof(RetransRequest))) {
-            ALOGV("Dropping packet, too short to contain NAK payload (%u bytes)",
-                  static_cast<uint32_t>(amt));
+            ALOGV("Dropping packet, too short to contain NAK payload"
+                  " (%u bytes)", static_cast<uint32_t>(amt));
             goto drop_packet;
         }
 
@@ -441,7 +441,8 @@
         gap.start_seq_ = ntohs(rtr->start_seq_);
         gap.end_seq_   = ntohs(rtr->end_seq_);
 
-        ALOGV("Process NAK for gap at [%hu, %hu]", gap.start_seq_, gap.end_seq_);
+        ALOGV("Process NAK for gap at [%hu, %hu]",
+                gap.start_seq_, gap.end_seq_);
         ring_buffer_.processNAK(&gap);
 
         return true;
@@ -770,7 +771,8 @@
             ALOGE("Error when sending retransmit request (%d)", errno);
         } else {
             ALOGV("%s request for range [%hu, %hu] sent",
-                  (kGS_FastStartGap == gap_status) ? "Fast Start" : "Retransmit",
+                  (kGS_FastStartGap == gap_status) ? "Fast Start"
+                                                   : "Retransmit",
                   gap.start_seq_, gap.end_seq_);
         }
 
diff --git a/media/libaah_rtp/aah_rx_player_ring_buffer.cpp b/media/libaah_rtp/aah_rx_player_ring_buffer.cpp
index 0d8b31f..779405e 100644
--- a/media/libaah_rtp/aah_rx_player_ring_buffer.cpp
+++ b/media/libaah_rtp/aah_rx_player_ring_buffer.cpp
@@ -116,8 +116,8 @@
 
     // Check for overflow first.
     if ((!(norm_seq & 0x8000)) && (norm_seq >= (capacity_ - 1))) {
-        ALOGW("Ring buffer overflow; cap = %u, [rd, wr] = [%hu, %hu], seq = %hu",
-              capacity_, rd_seq_, norm_wr_seq + rd_seq_, seq);
+        ALOGW("Ring buffer overflow; cap = %u, [rd, wr] = [%hu, %hu],"
+              " seq = %hu", capacity_, rd_seq_, norm_wr_seq + rd_seq_, seq);
         PacketBuffer::destroy(buf);
         return false;
     }
diff --git a/media/libaah_rtp/aah_rx_player_substream.cpp b/media/libaah_rtp/aah_rx_player_substream.cpp
index 1e4c784..18b0e2b 100644
--- a/media/libaah_rtp/aah_rx_player_substream.cpp
+++ b/media/libaah_rtp/aah_rx_player_substream.cpp
@@ -27,6 +27,11 @@
 #include <media/stagefright/Utils.h>
 
 #include "aah_rx_player.h"
+#include "aah_tx_packet.h"
+
+inline uint32_t min(uint32_t a, uint32_t b) {
+    return (a < b ? a : b);
+}
 
 namespace android {
 
@@ -38,6 +43,7 @@
     substream_details_known_ = false;
     buffer_in_progress_ = NULL;
     status_ = OK;
+    codec_mime_type_ = "";
 
     decoder_ = new AAH_DecoderPump(omx);
     if (decoder_ == NULL) {
@@ -52,6 +58,9 @@
     cleanupBufferInProgress();
 }
 
+AAH_RXPlayer::Substream::~Substream() {
+    shutdown();
+}
 
 void AAH_RXPlayer::Substream::shutdown() {
     substream_meta_ = NULL;
@@ -69,6 +78,9 @@
     expected_buffer_size_ = 0;
     buffer_filled_ = 0;
     waiting_for_rap_ = true;
+
+    aux_data_in_progress_.clear();
+    aux_data_expected_size_ = 0;
 }
 
 void AAH_RXPlayer::Substream::cleanupDecoder() {
@@ -129,16 +141,16 @@
     // one that does not conflict with any previously received substream type.
     uint8_t header_type = (buf[1] >> 4) & 0xF;
     switch (header_type) {
-        case 0x01:
+        case TRTPPacket::kHeaderTypeAudio:
             // Audio, yay!  Just break.  We understand audio payloads.
             break;
-        case 0x02:
+        case TRTPPacket::kHeaderTypeVideo:
             ALOGV("RXed packet with unhandled TRTP header type (Video).");
             return;
-        case 0x03:
+        case TRTPPacket::kHeaderTypeSubpicture:
             ALOGV("RXed packet with unhandled TRTP header type (Subpicture).");
             return;
-        case 0x04:
+        case TRTPPacket::kHeaderTypeControl:
             ALOGV("RXed packet with unhandled TRTP header type (Control).");
             return;
         default:
@@ -148,15 +160,15 @@
     }
 
     if (substream_details_known_ && (header_type != substream_type_)) {
-        ALOGV("RXed TRTP Payload for SSRC=0x%08x where header type (%u) does not"
-              " match previously received header type (%u)",
+        ALOGV("RXed TRTP Payload for SSRC=0x%08x where header type (%u) does"
+              " not match previously received header type (%u)",
               ssrc_, header_type, substream_type_);
         return;
     }
 
     // Check the flags to see if there is another 32 bits of timestamp present.
     uint32_t trtp_header_len = 6;
-    bool ts_valid = buf[1] & 0x1;
+    bool ts_valid = buf[1] & TRTPPacket::kFlag_TSValid;
     if (ts_valid) {
         min_length += 4;
         trtp_header_len += 4;
@@ -168,11 +180,7 @@
     }
 
     // Extract the TRTP length field and sanity check it.
-    uint32_t trtp_len;
-    trtp_len = (static_cast<uint32_t>(buf[2]) << 24) |
-        (static_cast<uint32_t>(buf[3]) << 16) |
-        (static_cast<uint32_t>(buf[4]) <<  8) |
-        static_cast<uint32_t>(buf[5]);
+    uint32_t trtp_len = U32_AT(buf + 2);
     if (trtp_len < min_length) {
         ALOGV("TRTP length (%u) is too short to be valid.  Must be at least %u"
               " bytes.", trtp_len, min_length);
@@ -183,17 +191,14 @@
     int64_t ts = 0;
     uint32_t parse_offset = 6;
     if (ts_valid) {
-        ts = (static_cast<int64_t>(buf[parse_offset    ]) << 56) |
-            (static_cast<int64_t>(buf[parse_offset + 1]) << 48) |
-            (static_cast<int64_t>(buf[parse_offset + 2]) << 40) |
-            (static_cast<int64_t>(buf[parse_offset + 3]) << 32);
-        ts |= ts_lower;
+        uint32_t ts_upper = U32_AT(buf + parse_offset);
         parse_offset += 4;
+        ts = (static_cast<int64_t>(ts_upper) << 32) | ts_lower;
     }
 
     // Check the flags to see if there is another 24 bytes of timestamp
     // transformation present.
-    if (buf[1] & 0x2) {
+    if (buf[1] & TRTPPacket::kFlag_TSTransformPresent) {
         min_length += 24;
         parse_offset += 24;
         trtp_header_len += 24;
@@ -219,8 +224,8 @@
 
     if (amt < min_length) {
         ALOGV("TRTP porttion of RTP payload (%u bytes) too small to contain"
-              " entire TRTP header.  TRTP does not currently support fragmenting"
-              " TRTP headers across RTP payloads", amt);
+              " entire TRTP header.  TRTP does not currently support"
+              " fragmenting TRTP headers across RTP payloads", amt);
         return;
     }
 
@@ -238,16 +243,42 @@
         decoder_->setRenderVolume(volume);
     }
 
-    // TODO : move all of the constant flag and offset definitions for TRTP up
-    // into some sort of common header file.
-    if (waiting_for_rap_ && !(flags & 0x08)) {
+    if (waiting_for_rap_ && !(flags & TRTPAudioPacket::kFlag_RandomAccessPoint)) {
         ALOGV("Dropping non-RAP TRTP Audio Payload while waiting for RAP.");
         return;
     }
 
-    if (flags & 0x10) {
-        ALOGV("Dropping TRTP Audio Payload with aux codec data present (only"
-              " handle MP3 right now, and it has no aux data)");
+    // Check for the presence of codec aux data.
+    if (flags & TRTPAudioPacket::kFlag_AuxLengthPresent) {
+        min_length += 4;
+        trtp_header_len += 4;
+
+        if (trtp_len < min_length) {
+            ALOGV("TRTP length (%u) is too short to be a valid audio payload.  "
+                  "Must be at least %u bytes.", trtp_len, min_length);
+            return;
+        }
+
+        if (amt < min_length) {
+            ALOGV("TRTP porttion of RTP payload (%u bytes) too small to contain"
+                  " entire TRTP header.  TRTP does not currently support"
+                  " fragmenting TRTP headers across RTP payloads", amt);
+            return;
+        }
+
+        aux_data_expected_size_ = U32_AT(buf + parse_offset);
+        aux_data_in_progress_.clear();
+        if (aux_data_in_progress_.capacity() < aux_data_expected_size_) {
+            aux_data_in_progress_.setCapacity(aux_data_expected_size_);
+        }
+    } else {
+        aux_data_expected_size_ = 0;
+    }
+
+    if ((aux_data_expected_size_ + trtp_header_len) > trtp_len) {
+        ALOGV("Expected codec aux data length (%u) and TRTP header overhead"
+              " (%u) too large for total TRTP payload length (%u).",
+             aux_data_expected_size_, trtp_header_len, trtp_len);
         return;
     }
 
@@ -255,7 +286,9 @@
     // the buffer in progress and pack as much payload as we can into it.  If
     // the payload is finished once we are done, go ahead and send the payload
     // to the decoder.
-    expected_buffer_size_ = trtp_len - trtp_header_len;
+    expected_buffer_size_ = trtp_len
+                          - trtp_header_len
+                          - aux_data_expected_size_;
     if (!expected_buffer_size_) {
         ALOGV("Dropping TRTP Audio Payload with 0 Access Unit length");
         return;
@@ -263,9 +296,10 @@
 
     CHECK(amt >= trtp_header_len);
     uint32_t todo = amt - trtp_header_len;
-    if (expected_buffer_size_ < todo) {
+    if ((expected_buffer_size_ + aux_data_expected_size_) < todo) {
         ALOGV("Extra data (%u > %u) present in initial TRTP Audio Payload;"
-              " dropping payload.", todo, expected_buffer_size_);
+              " dropping payload.", todo,
+              expected_buffer_size_ + aux_data_expected_size_);
         return;
     }
 
@@ -287,18 +321,32 @@
         return;
     }
 
-    // TODO : set this based on the codec type indicated in the TRTP stream.
-    // Right now, we only support MP3, so the choice is obvious.
-    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+    meta->setCString(kKeyMIMEType, codec_mime_type_);
     if (ts_valid) {
         meta->setInt64(kKeyTime, ts);
     }
 
-    if (amt > 0) {
+    // Skip over the header we have already extracted.
+    amt -= trtp_header_len;
+    buf += trtp_header_len;
+
+    // Extract as much of the expected aux data as we can.
+    todo = min(aux_data_expected_size_, amt);
+    if (todo) {
+        aux_data_in_progress_.appendArray(buf, todo);
+        buf += todo;
+        amt -= todo;
+    }
+
+    // Extract as much of the expected payload as we can.
+    todo = min(expected_buffer_size_, amt);
+    if (todo > 0) {
         uint8_t* tgt =
             reinterpret_cast<uint8_t*>(buffer_in_progress_->data());
-        memcpy(tgt + buffer_filled_, buf + trtp_header_len, todo);
-        buffer_filled_ += amt;
+        memcpy(tgt, buf, todo);
+        buffer_filled_ = amt;
+        buf += todo;
+        amt -= todo;
     }
 
     if (buffer_filled_ >= expected_buffer_size_) {
@@ -318,6 +366,18 @@
         return;
     }
 
+    CHECK(aux_data_in_progress_.size() <= aux_data_expected_size_);
+    uint32_t aux_left = aux_data_expected_size_ - aux_data_in_progress_.size();
+    if (aux_left) {
+        uint32_t todo = min(aux_left, amt);
+        aux_data_in_progress_.appendArray(buf, todo);
+        amt -= todo;
+        buf += todo;
+
+        if (!amt)
+            return;
+    }
+
     CHECK(buffer_filled_ < expected_buffer_size_);
     uint32_t buffer_left = expected_buffer_size_ - buffer_filled_;
     if (amt > buffer_left) {
@@ -340,10 +400,6 @@
 }
 
 void AAH_RXPlayer::Substream::processCompletedBuffer() {
-    const uint8_t* buffer_data = NULL;
-    int sample_rate;
-    int channel_count;
-    size_t frame_size;
     status_t res;
 
     CHECK(NULL != buffer_in_progress_);
@@ -353,56 +409,10 @@
         goto bailout;
     }
 
-    buffer_data = reinterpret_cast<const uint8_t*>(buffer_in_progress_->data());
-    if (buffer_in_progress_->size() < 4) {
-        ALOGV("MP3 payload too short to contain header, dropping payload.");
+    // Make sure our metadata used to initialize the decoder has been properly
+    // set up.
+    if (!setupSubstreamMeta())
         goto bailout;
-    }
-
-    // Extract the channel count and the sample rate from the MP3 header.  The
-    // stagefright MP3 requires that these be delivered before decoing can
-    // begin.
-    if (!GetMPEGAudioFrameSize(U32_AT(buffer_data),
-                               &frame_size,
-                               &sample_rate,
-                               &channel_count,
-                               NULL,
-                               NULL)) {
-        ALOGV("Failed to parse MP3 header in payload, droping payload.");
-        goto bailout;
-    }
-
-
-    // Make sure that our substream metadata is set up properly.  If there has
-    // been a format change, be sure to reset the underlying decoder.  In
-    // stagefright, it seems like the only way to do this is to destroy and
-    // recreate the decoder.
-    if (substream_meta_ == NULL) {
-        substream_meta_ = new MetaData();
-
-        if (substream_meta_ == NULL) {
-            ALOGE("Failed to allocate MetaData structure for substream");
-            goto bailout;
-        }
-
-        substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
-        substream_meta_->setInt32  (kKeyChannelCount, channel_count);
-        substream_meta_->setInt32  (kKeySampleRate,   sample_rate);
-    } else {
-        int32_t prev_sample_rate;
-        int32_t prev_channel_count;
-        substream_meta_->findInt32(kKeySampleRate,   &prev_sample_rate);
-        substream_meta_->findInt32(kKeyChannelCount, &prev_channel_count);
-
-        if ((prev_channel_count != channel_count) ||
-            (prev_sample_rate   != sample_rate)) {
-            ALOGW("Format change detected, forcing decoder reset.");
-            cleanupDecoder();
-
-            substream_meta_->setInt32(kKeyChannelCount, channel_count);
-            substream_meta_->setInt32(kKeySampleRate,   sample_rate);
-        }
-    }
 
     // If our decoder has not be set up, do so now.
     res = decoder_->init(substream_meta_);
@@ -418,7 +428,7 @@
 
     if (res != OK) {
         ALOGD("Failed to queue payload for decode, resetting decoder pump!"
-              " (res = %d)", res);
+             " (res = %d)", res);
         status_ = res;
         cleanupDecoder();
         cleanupBufferInProgress();
@@ -454,6 +464,167 @@
     cleanupBufferInProgress();
 }
 
+bool AAH_RXPlayer::Substream::setupSubstreamMeta() {
+    switch (codec_type_) {
+        case TRTPAudioPacket::kCodecMPEG1Audio:
+            codec_mime_type_ = MEDIA_MIMETYPE_AUDIO_MPEG;
+            return setupMP3SubstreamMeta();
+
+        case TRTPAudioPacket::kCodecAACAudio:
+            codec_mime_type_ = MEDIA_MIMETYPE_AUDIO_AAC;
+            return setupAACSubstreamMeta();
+
+        default:
+            ALOGV("Failed to setup substream metadata for unsupported codec"
+                  " type (%u)", codec_type_);
+            break;
+    }
+
+    return false;
+}
+
+bool AAH_RXPlayer::Substream::setupMP3SubstreamMeta() {
+    const uint8_t* buffer_data = NULL;
+    int sample_rate;
+    int channel_count;
+    size_t frame_size;
+    status_t res;
+
+    buffer_data = reinterpret_cast<const uint8_t*>(buffer_in_progress_->data());
+    if (buffer_in_progress_->size() < 4) {
+        ALOGV("MP3 payload too short to contain header, dropping payload.");
+        return false;
+    }
+
+    // Extract the channel count and the sample rate from the MP3 header.  The
+    // stagefright MP3 requires that these be delivered before decoing can
+    // begin.
+    if (!GetMPEGAudioFrameSize(U32_AT(buffer_data),
+                               &frame_size,
+                               &sample_rate,
+                               &channel_count,
+                               NULL,
+                               NULL)) {
+        ALOGV("Failed to parse MP3 header in payload, droping payload.");
+        return false;
+    }
+
+
+    // Make sure that our substream metadata is set up properly.  If there has
+    // been a format change, be sure to reset the underlying decoder.  In
+    // stagefright, it seems like the only way to do this is to destroy and
+    // recreate the decoder.
+    if (substream_meta_ == NULL) {
+        substream_meta_ = new MetaData();
+
+        if (substream_meta_ == NULL) {
+            ALOGE("Failed to allocate MetaData structure for MP3 substream");
+            return false;
+        }
+
+        substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+        substream_meta_->setInt32  (kKeyChannelCount, channel_count);
+        substream_meta_->setInt32  (kKeySampleRate,   sample_rate);
+    } else {
+        int32_t prev_sample_rate;
+        int32_t prev_channel_count;
+        substream_meta_->findInt32(kKeySampleRate,   &prev_sample_rate);
+        substream_meta_->findInt32(kKeyChannelCount, &prev_channel_count);
+
+        if ((prev_channel_count != channel_count) ||
+            (prev_sample_rate   != sample_rate)) {
+            ALOGW("MP3 format change detected, forcing decoder reset.");
+            cleanupDecoder();
+
+            substream_meta_->setInt32(kKeyChannelCount, channel_count);
+            substream_meta_->setInt32(kKeySampleRate,   sample_rate);
+        }
+    }
+
+    return true;
+}
+
+bool AAH_RXPlayer::Substream::setupAACSubstreamMeta() {
+    int32_t sample_rate, channel_cnt;
+    static const size_t overhead = sizeof(sample_rate)
+                                 + sizeof(channel_cnt);
+
+    if (aux_data_in_progress_.size() < overhead) {
+        ALOGE("Not enough aux data (%u) to initialize AAC substream decoder",
+                aux_data_in_progress_.size());
+        return false;
+    }
+
+    const uint8_t* aux_data = aux_data_in_progress_.array();
+    size_t aux_data_size = aux_data_in_progress_.size();
+    sample_rate = U32_AT(aux_data);
+    channel_cnt = U32_AT(aux_data + sizeof(sample_rate));
+
+    const uint8_t* esds_data = NULL;
+    size_t esds_data_size = 0;
+    if (aux_data_size > overhead) {
+        esds_data = aux_data + overhead;
+        esds_data_size = aux_data_size - overhead;
+    }
+
+    // Do we already have metadata?  If so, has it changed at all?  If not, then
+    // there should be nothing else to do.  Otherwise, release our old stream
+    // metadata and make new metadata.
+    if (substream_meta_ != NULL) {
+        uint32_t type;
+        const void* data;
+        size_t size;
+        int32_t prev_sample_rate;
+        int32_t prev_channel_count;
+        bool res;
+
+        res = substream_meta_->findInt32(kKeySampleRate,   &prev_sample_rate);
+        CHECK(res);
+        res = substream_meta_->findInt32(kKeyChannelCount, &prev_channel_count);
+        CHECK(res);
+
+        // If nothing has changed about the codec aux data (esds, sample rate,
+        // channel count), then we can just do nothing and get out.  Otherwise,
+        // we will need to reset the decoder and make a new metadata object to
+        // deal with the format change.
+        bool hasData = (esds_data != NULL);
+        bool hadData = substream_meta_->findData(kKeyESDS, &type, &data, &size);
+        bool esds_change = (hadData != hasData);
+
+        if (!esds_change && hasData)
+            esds_change = ((size != esds_data_size) ||
+                           memcmp(data, esds_data, size));
+
+        if (!esds_change &&
+            (prev_sample_rate   == sample_rate) &&
+            (prev_channel_count == channel_cnt)) {
+            return true;  // no change, just get out.
+        }
+
+        ALOGW("AAC format change detected, forcing decoder reset.");
+        cleanupDecoder();
+        substream_meta_ = NULL;
+    }
+
+    CHECK(substream_meta_ == NULL);
+
+    substream_meta_ = new MetaData();
+    if (substream_meta_ == NULL) {
+        ALOGE("Failed to allocate MetaData structure for AAC substream");
+        return false;
+    }
+
+    substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+    substream_meta_->setInt32  (kKeySampleRate,   sample_rate);
+    substream_meta_->setInt32  (kKeyChannelCount, channel_cnt);
+
+    if (esds_data) {
+        substream_meta_->setData(kKeyESDS, kTypeESDS,
+                                 esds_data, esds_data_size);
+    }
+
+    return true;
+}
 
 void AAH_RXPlayer::Substream::processTSTransform(const LinearTransform& trans) {
     if (decoder_ != NULL) {
@@ -471,26 +642,34 @@
 
 bool AAH_RXPlayer::Substream::setupSubstreamType(uint8_t substream_type,
                                                  uint8_t codec_type) {
-    // Sanity check the codec type.  Right now we only support MP3.  Also check
-    // for conflicts with previously delivered codec types.
-    if (substream_details_known_ && (codec_type != codec_type_)) {
-        ALOGV("RXed TRTP Payload for SSRC=0x%08x where codec type (%u) does not"
-              " match previously received codec type (%u)",
-              ssrc_, codec_type, codec_type_);
-        return false;
+    // Sanity check the codec type.  Right now we only support MP3 and AAC.
+    // Also check for conflicts with previously delivered codec types.
+    if (substream_details_known_) {
+        if (codec_type != codec_type_) {
+            ALOGV("RXed TRTP Payload for SSRC=0x%08x where codec type (%u) does"
+                  " not match previously received codec type (%u)",
+                 ssrc_, codec_type, codec_type_);
+            return false;
+        }
+
+        return true;
     }
 
-    if (codec_type != 0x03) {
-        ALOGV("RXed TRTP Audio Payload for SSRC=0x%08x with unsupported codec"
-              " type (%u)", ssrc_, codec_type);
-        return false;
+    switch (codec_type) {
+        // MP3 and AAC are all we support right now.
+        case TRTPAudioPacket::kCodecMPEG1Audio:
+        case TRTPAudioPacket::kCodecAACAudio:
+            break;
+
+        default:
+            ALOGV("RXed TRTP Audio Payload for SSRC=0x%08x with unsupported"
+                  " codec type (%u)", ssrc_, codec_type);
+            return false;
     }
 
-    if (!substream_details_known_) {
-        substream_type_ = substream_type;
-        codec_type_ = codec_type;
-        substream_details_known_ = true;
-    }
+    substream_type_ = substream_type;
+    codec_type_ = codec_type;
+    substream_details_known_ = true;
 
     return true;
 }
diff --git a/media/libaah_rtp/aah_tx_packet.cpp b/media/libaah_rtp/aah_tx_packet.cpp
index 3f6e0e9..4cd6e471 100644
--- a/media/libaah_rtp/aah_tx_packet.cpp
+++ b/media/libaah_rtp/aah_tx_packet.cpp
@@ -142,12 +142,18 @@
     mVolume = val;
 }
 
-void TRTPAudioPacket::setAccessUnitData(void* data, int len) {
+void TRTPAudioPacket::setAccessUnitData(const void* data, size_t len) {
     CHECK(!mIsPacked);
     mAccessUnitData = data;
     mAccessUnitLen = len;
 }
 
+void TRTPAudioPacket::setAuxData(const void* data, size_t len) {
+    CHECK(!mIsPacked);
+    mAuxData = data;
+    mAuxDataLen = len;
+}
+
 /*** TRTP control packet properties ***/
 
 void TRTPControlPacket::setCommandID(TRTPCommandID val) {
@@ -232,6 +238,7 @@
     }
 
     int packetLen = kRTPHeaderLen +
+                    mAuxDataLen +
                     mAccessUnitLen +
                     TRTPHeaderLen();
 
@@ -249,16 +256,24 @@
     mPacketLen = packetLen;
 
     uint8_t* cur = mPacket;
+    bool hasAux = mAuxData && mAuxDataLen;
+    uint8_t flags = (static_cast<int>(hasAux) << 4) |
+                    (static_cast<int>(mRandomAccessPoint) << 3) |
+                    (static_cast<int>(mDropable) << 2) |
+                    (static_cast<int>(mDiscontinuity) << 1) |
+                    (static_cast<int>(mEndOfStream));
 
     writeTRTPHeader(cur, true, packetLen);
     writeU8(cur, mCodecType);
-    writeU8(cur,
-            (static_cast<int>(mRandomAccessPoint) << 3) |
-            (static_cast<int>(mDropable) << 2) |
-            (static_cast<int>(mDiscontinuity) << 1) |
-            (static_cast<int>(mEndOfStream)));
+    writeU8(cur, flags);
     writeU8(cur, mVolume);
 
+    if (hasAux) {
+        writeU32(cur, mAuxDataLen);
+        memcpy(cur, mAuxData, mAuxDataLen);
+        cur += mAuxDataLen;
+    }
+
     memcpy(cur, mAccessUnitData, mAccessUnitLen);
 
     mIsPacked = true;
@@ -293,12 +308,10 @@
     }
 
 
-    // TODO : properly compute aux data length.  Currently, nothing
-    // uses aux data, so its length is always 0.
-    int auxDataLength = 0;
+    int auxDataLenField = (NULL != mAuxData) ? sizeof(uint32_t) : 0;
     return TRTPPacket::TRTPHeaderLen() +
            3 +
-           auxDataLength +
+           auxDataLenField +
            pcmParamLength;
 }
 
diff --git a/media/libaah_rtp/aah_tx_packet.h b/media/libaah_rtp/aah_tx_packet.h
index 833803e..7f78ea0 100644
--- a/media/libaah_rtp/aah_tx_packet.h
+++ b/media/libaah_rtp/aah_tx_packet.h
@@ -25,7 +25,7 @@
 namespace android {
 
 class TRTPPacket : public RefBase {
-  protected:
+  public:
     enum TRTPHeaderType {
         kHeaderTypeAudio = 1,
         kHeaderTypeVideo = 2,
@@ -33,6 +33,12 @@
         kHeaderTypeControl = 4,
     };
 
+    enum TRTPPayloadFlags {
+        kFlag_TSTransformPresent = 0x02,
+        kFlag_TSValid = 0x01,
+    };
+
+  protected:
     TRTPPacket(TRTPHeaderType headerType)
         : mIsPacked(false)
         , mVersion(2)
@@ -121,6 +127,14 @@
 
 class TRTPAudioPacket : public TRTPPacket {
   public:
+    enum AudioPayloadFlags {
+        kFlag_AuxLengthPresent = 0x10,
+        kFlag_RandomAccessPoint = 0x08,
+        kFlag_Dropable = 0x04,
+        kFlag_Discontinuity = 0x02,
+        kFlag_EndOfStream = 0x01,
+    };
+
     TRTPAudioPacket()
         : TRTPPacket(kHeaderTypeAudio)
         , mCodecType(kCodecInvalid)
@@ -129,13 +143,17 @@
         , mDiscontinuity(false)
         , mEndOfStream(false)
         , mVolume(0)
-        , mAccessUnitData(NULL) { }
+        , mAccessUnitData(NULL)
+        , mAccessUnitLen(0)
+        , mAuxData(NULL)
+        , mAuxDataLen(0) { }
 
     enum TRTPAudioCodecType {
         kCodecInvalid = 0,
         kCodecPCMBigEndian = 1,
         kCodecPCMLittleEndian = 2,
         kCodecMPEG1Audio = 3,
+        kCodecAACAudio = 4,
     };
 
     void setCodecType(TRTPAudioCodecType val);
@@ -144,7 +162,8 @@
     void setDiscontinuity(bool val);
     void setEndOfStream(bool val);
     void setVolume(uint8_t val);
-    void setAccessUnitData(void* data, int len);
+    void setAccessUnitData(const void* data, size_t len);
+    void setAuxData(const void* data, size_t len);
 
     virtual bool pack();
 
@@ -158,8 +177,11 @@
     bool mDiscontinuity;
     bool mEndOfStream;
     uint8_t mVolume;
-    void* mAccessUnitData;
-    int mAccessUnitLen;
+
+    const void* mAccessUnitData;
+    size_t mAccessUnitLen;
+    const void* mAuxData;
+    size_t mAuxDataLen;
 
     DISALLOW_EVIL_CONSTRUCTORS(TRTPAudioPacket);
 };
diff --git a/media/libaah_rtp/aah_tx_player.cpp b/media/libaah_rtp/aah_tx_player.cpp
index a79a989..974805b 100644
--- a/media/libaah_rtp/aah_tx_player.cpp
+++ b/media/libaah_rtp/aah_tx_player.cpp
@@ -28,6 +28,7 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/FileSource.h>
 #include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
 #include <utils/Timers.h>
 
@@ -98,6 +99,8 @@
     mPumpAudioEvent = new AAH_TXEvent(this, &AAH_TXPlayer::onPumpAudio);
     mPumpAudioEventPending = false;
 
+    mAudioCodecData = NULL;
+
     reset_l();
 }
 
@@ -146,14 +149,7 @@
         const KeyedVector<String8, String8> *headers) {
     reset_l();
 
-    // the URL must consist of "aahTX://" followed by the real URL of
-    // the data source
-    const char *kAAHPrefix = "aahTX://";
-    if (strncasecmp(url, kAAHPrefix, strlen(kAAHPrefix))) {
-        return INVALID_OPERATION;
-    }
-
-    mUri.setTo(url + strlen(kAAHPrefix));
+    mUri.setTo(url);
 
     if (headers) {
         mUriHeaders = *headers;
@@ -398,7 +394,76 @@
         }
     }
 
-    mAudioSource->getFormat()->findInt64(kKeyDuration, &mDurationUs);
+    mAudioFormat = mAudioSource->getFormat();
+    if (!mAudioFormat->findInt64(kKeyDuration, &mDurationUs))
+        mDurationUs = 1;
+
+    const char* mime_type = NULL;
+    if (!mAudioFormat->findCString(kKeyMIMEType, &mime_type)) {
+        ALOGE("Failed to find audio substream MIME type during prepare.");
+        abortPrepare(BAD_VALUE);
+        return;
+    }
+
+    if (!strcmp(mime_type, MEDIA_MIMETYPE_AUDIO_MPEG)) {
+        mAudioCodec = TRTPAudioPacket::kCodecMPEG1Audio;
+    } else
+    if (!strcmp(mime_type, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        mAudioCodec = TRTPAudioPacket::kCodecAACAudio;
+
+        uint32_t type;
+        int32_t  sample_rate;
+        int32_t  channel_count;
+        const void* esds_data;
+        size_t esds_len;
+
+        if (!mAudioFormat->findInt32(kKeySampleRate, &sample_rate)) {
+            ALOGE("Failed to find sample rate for AAC substream.");
+            abortPrepare(BAD_VALUE);
+            return;
+        }
+
+        if (!mAudioFormat->findInt32(kKeyChannelCount, &channel_count)) {
+            ALOGE("Failed to find channel count for AAC substream.");
+            abortPrepare(BAD_VALUE);
+            return;
+        }
+
+        if (!mAudioFormat->findData(kKeyESDS, &type, &esds_data, &esds_len)) {
+            ALOGE("Failed to find codec init data for AAC substream.");
+            abortPrepare(BAD_VALUE);
+            return;
+        }
+
+        CHECK(NULL == mAudioCodecData);
+        mAudioCodecDataSize = esds_len
+                            + sizeof(sample_rate)
+                            + sizeof(channel_count);
+        mAudioCodecData = new uint8_t[mAudioCodecDataSize];
+        if (NULL == mAudioCodecData) {
+            ALOGE("Failed to allocate %u bytes for AAC substream codec aux"
+                  " data.", mAudioCodecDataSize);
+            mAudioCodecDataSize = 0;
+            abortPrepare(BAD_VALUE);
+            return;
+        }
+
+        uint8_t* tmp = mAudioCodecData;
+        tmp[0] = static_cast<uint8_t>((sample_rate   >> 24) & 0xFF);
+        tmp[1] = static_cast<uint8_t>((sample_rate   >> 16) & 0xFF);
+        tmp[2] = static_cast<uint8_t>((sample_rate   >>  8) & 0xFF);
+        tmp[3] = static_cast<uint8_t>((sample_rate        ) & 0xFF);
+        tmp[4] = static_cast<uint8_t>((channel_count >> 24) & 0xFF);
+        tmp[5] = static_cast<uint8_t>((channel_count >> 16) & 0xFF);
+        tmp[6] = static_cast<uint8_t>((channel_count >>  8) & 0xFF);
+        tmp[7] = static_cast<uint8_t>((channel_count      ) & 0xFF);
+
+        memcpy(tmp + 8, esds_data, esds_len);
+    } else {
+        ALOGE("Unsupported MIME type \"%s\" in audio substream", mime_type);
+        abortPrepare(BAD_VALUE);
+        return;
+    }
 
     status_t err = mAudioSource->start();
     if (err != OK) {
@@ -666,6 +731,11 @@
         mAudioSource->stop();
     }
     mAudioSource.clear();
+    mAudioCodec = TRTPAudioPacket::kCodecInvalid;
+    mAudioFormat = NULL;
+    delete[] mAudioCodecData;
+    mAudioCodecData = NULL;
+    mAudioCodecDataSize = 0;
 
     mFlags = 0;
     mExtractorFlags = 0;
@@ -717,67 +787,7 @@
 }
 
 status_t AAH_TXPlayer::invoke(const Parcel& request, Parcel *reply) {
-    if (!reply) {
-        return BAD_VALUE;
-    }
-
-    int32_t methodID;
-    status_t err = request.readInt32(&methodID);
-    if (err != android::OK) {
-        return err;
-    }
-
-    switch (methodID) {
-        case kInvokeSetAAHDstIPPort:
-        case kInvokeSetAAHConfigBlob: {
-            if (mEndpointValid) {
-                return INVALID_OPERATION;
-            }
-
-            String8 addr;
-            uint16_t port;
-
-            if (methodID == kInvokeSetAAHDstIPPort) {
-                addr = String8(request.readString16());
-
-                int32_t port32;
-                err = request.readInt32(&port32);
-                if (err != android::OK) {
-                    return err;
-                }
-                port = static_cast<uint16_t>(port32);
-            } else {
-                String8 blob(request.readString16());
-
-                char addr_buf[101];
-                if (sscanf(blob.string(), "V1:%100s %" SCNu16,
-                           addr_buf, &port) != 2) {
-                    return BAD_VALUE;
-                }
-                if (addr.setTo(addr_buf) != OK) {
-                    return NO_MEMORY;
-                }
-            }
-
-            struct hostent* ent = gethostbyname(addr.string());
-            if (ent == NULL) {
-                return ERROR_UNKNOWN_HOST;
-            }
-            if (!(ent->h_addrtype == AF_INET && ent->h_length == 4)) {
-                return BAD_VALUE;
-            }
-
-            Mutex::Autolock lock(mEndpointLock);
-            mEndpoint = AAH_TXSender::Endpoint(
-                        reinterpret_cast<struct in_addr*>(ent->h_addr)->s_addr,
-                        port);
-            mEndpointValid = true;
-            return OK;
-        };
-
-        default:
-            return INVALID_OPERATION;
-    }
+    return INVALID_OPERATION;
 }
 
 status_t AAH_TXPlayer::getMetadata(const media::Metadata::Filter& ids,
@@ -812,6 +822,24 @@
     return OK;
 }
 
+status_t AAH_TXPlayer::setRetransmitEndpoint(
+        const struct sockaddr_in* endpoint) {
+    Mutex::Autolock lock(mLock);
+
+    if (NULL == endpoint)
+        return BAD_VALUE;
+
+    // Once the endpoint has been registered, it may not be changed.
+    if (mEndpointRegistered)
+        return INVALID_OPERATION;
+
+    mEndpoint.addr = endpoint->sin_addr.s_addr;
+    mEndpoint.port = endpoint->sin_port;
+    mEndpointValid = true;
+
+    return OK;
+}
+
 void AAH_TXPlayer::notifyListener_l(int msg, int ext1, int ext2) {
     sendEvent(msg, ext1, ext2);
 }
@@ -1078,14 +1106,24 @@
         packet->setPTS(mediaTimeUs);
         packet->setSubstreamID(1);
 
-        packet->setCodecType(TRTPAudioPacket::kCodecMPEG1Audio);
+        packet->setCodecType(mAudioCodec);
         packet->setVolume(mTRTPVolume);
         // TODO : introduce a throttle for this so we can control the
         // frequency with which transforms get sent.
         packet->setClockTransform(mCurrentClockTransform);
         packet->setAccessUnitData(data, mediaBuffer->range_length());
+
+        // TODO : while its pretty much universally true that audio ES payloads
+        // are all RAPs across all codecs, it might be a good idea to throttle
+        // the frequency with which we send codec out of band data to the RXers.
+        // If/when we do, we need to flag only those payloads which have
+        // required out of band data attached to them as RAPs.
         packet->setRandomAccessPoint(true);
 
+        if (mAudioCodecData && mAudioCodecDataSize) {
+            packet->setAuxData(mAudioCodecData, mAudioCodecDataSize);
+        }
+
         queuePacketToSender_l(packet);
         mediaBuffer->release();
 
diff --git a/media/libaah_rtp/aah_tx_player.h b/media/libaah_rtp/aah_tx_player.h
index 64cf5dc1..2e4b1f7 100644
--- a/media/libaah_rtp/aah_tx_player.h
+++ b/media/libaah_rtp/aah_tx_player.h
@@ -63,16 +63,8 @@
                                     Parcel* records);
     virtual status_t    setVolume(float leftVolume, float rightVolume);
     virtual status_t    setAudioStreamType(audio_stream_type_t streamType);
-
-    // invoke method IDs
-    enum {
-        // set the IP address and port of the A@H receiver
-        kInvokeSetAAHDstIPPort = 1,
-
-        // set the destination IP address and port (and perhaps any additional
-        // parameters added in the future) packaged in one string
-        kInvokeSetAAHConfigBlob,
-    };
+    virtual status_t    setRetransmitEndpoint(
+                            const struct sockaddr_in* endpoint);
 
     static const int64_t kAAHRetryKeepAroundTimeNs;
 
@@ -153,6 +145,11 @@
     sp<NuCachedSource2> mCachedSource;
 
     sp<MediaSource> mAudioSource;
+    TRTPAudioPacket::TRTPAudioCodecType mAudioCodec;
+    sp<MetaData> mAudioFormat;
+    uint8_t* mAudioCodecData;
+    size_t mAudioCodecDataSize;
+
     int64_t mDurationUs;
     int64_t mBitrate;
 
diff --git a/media/libaah_rtp/aah_tx_sender.cpp b/media/libaah_rtp/aah_tx_sender.cpp
index d991ea7..08e32d2 100644
--- a/media/libaah_rtp/aah_tx_sender.cpp
+++ b/media/libaah_rtp/aah_tx_sender.cpp
@@ -243,7 +243,7 @@
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
     addr.sin_addr.s_addr = endpoint.addr;
-    addr.sin_port = htons(endpoint.port);
+    addr.sin_port = endpoint.port;
 
     ssize_t result = sendto(mSocket,
                             packet->getPacket(),
@@ -283,7 +283,8 @@
     // remove the state for any endpoints that are no longer in use
     for (size_t i = 0; i < endpointsToRemove.size(); i++) {
         Endpoint& e = endpointsToRemove.editItemAt(i);
-        ALOGD("*** %s removing endpoint addr=%08x", __PRETTY_FUNCTION__, e.addr);
+        ALOGD("*** %s removing endpoint addr=%08x",
+                __PRETTY_FUNCTION__, e.addr);
         size_t index = mEndpointMap.indexOfKey(e);
         delete mEndpointMap.valueAt(index);
         mEndpointMap.removeItemsAt(index);
@@ -411,7 +412,7 @@
         return;
     }
 
-    Endpoint endpoint(request.endpointIP, ntohs(request.endpointPort));
+    Endpoint endpoint(request.endpointIP, request.endpointPort);
 
     Mutex::Autolock lock(mSender->mEndpointLock);
 
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index a4068ff..943f3af 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -307,7 +307,7 @@
         pid_t tid;
         if (t != 0) {
             mReadyToRun = WOULD_BLOCK;
-            t->run("ClientRecordThread", ANDROID_PRIORITY_AUDIO);
+            t->run("AudioRecord", ANDROID_PRIORITY_AUDIO);
             tid = t->getTid();  // pid_t is unknown until run()
             ALOGV("getTid=%d", tid);
             if (tid == -1) {
@@ -386,7 +386,7 @@
     return !mActive;
 }
 
-uint32_t AudioRecord::getSampleRate()
+uint32_t AudioRecord::getSampleRate() const
 {
     AutoMutex lock(mLock);
     return mCblk->sampleRate;
@@ -402,7 +402,7 @@
     return NO_ERROR;
 }
 
-status_t AudioRecord::getMarkerPosition(uint32_t *marker)
+status_t AudioRecord::getMarkerPosition(uint32_t *marker) const
 {
     if (marker == NULL) return BAD_VALUE;
 
@@ -423,7 +423,7 @@
     return NO_ERROR;
 }
 
-status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod)
+status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod) const
 {
     if (updatePeriod == NULL) return BAD_VALUE;
 
@@ -432,7 +432,7 @@
     return NO_ERROR;
 }
 
-status_t AudioRecord::getPosition(uint32_t *position)
+status_t AudioRecord::getPosition(uint32_t *position) const
 {
     if (position == NULL) return BAD_VALUE;
 
@@ -442,7 +442,7 @@
     return NO_ERROR;
 }
 
-unsigned int AudioRecord::getInputFramesLost()
+unsigned int AudioRecord::getInputFramesLost() const
 {
     if (mActive)
         return AudioSystem::getInputFramesLost(mInput);
@@ -597,7 +597,7 @@
     mCblk->stepUser(audioBuffer->frameCount);
 }
 
-audio_io_handle_t AudioRecord::getInput()
+audio_io_handle_t AudioRecord::getInput() const
 {
     AutoMutex lock(mLock);
     return mInput;
@@ -615,7 +615,7 @@
     return mInput;
 }
 
-int AudioRecord::getSessionId()
+int AudioRecord::getSessionId() const
 {
     return mSessionId;
 }
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 74c97ed..4890f05 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -370,7 +370,7 @@
         android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags);
         pid_t tid;
         if (t != 0) {
-            t->run("AudioTrackThread", ANDROID_PRIORITY_AUDIO);
+            t->run("AudioTrack", ANDROID_PRIORITY_AUDIO);
             tid = t->getTid();  // pid_t is unknown until run()
             ALOGV("getTid=%d", tid);
             if (tid == -1) {
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index 86d65db..c47fa41 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -15,6 +15,7 @@
 ** limitations under the License.
 */
 
+#include <arpa/inet.h>
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -53,6 +54,7 @@
     SET_VIDEO_SURFACETEXTURE,
     SET_PARAMETER,
     GET_PARAMETER,
+    SET_RETRANSMIT_ENDPOINT,
 };
 
 class BpMediaPlayer: public BpInterface<IMediaPlayer>
@@ -289,6 +291,25 @@
         return remote()->transact(GET_PARAMETER, data, reply);
     }
 
+    status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint) {
+        Parcel data, reply;
+        status_t err;
+
+        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+        if (NULL != endpoint) {
+            data.writeInt32(sizeof(*endpoint));
+            data.write(endpoint, sizeof(*endpoint));
+        } else {
+            data.writeInt32(0);
+        }
+
+        err = remote()->transact(SET_RETRANSMIT_ENDPOINT, data, &reply);
+        if (OK != err) {
+            return err;
+        }
+
+        return reply.readInt32();
+    }
 };
 
 IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer");
@@ -457,6 +478,20 @@
             CHECK_INTERFACE(IMediaPlayer, data, reply);
             return getParameter(data.readInt32(), reply);
         } break;
+        case SET_RETRANSMIT_ENDPOINT: {
+            CHECK_INTERFACE(IMediaPlayer, data, reply);
+
+            struct sockaddr_in endpoint;
+            int amt = data.readInt32();
+            if (amt == sizeof(endpoint)) {
+                data.read(&endpoint, sizeof(struct sockaddr_in));
+                reply->writeInt32(setRetransmitEndpoint(&endpoint));
+            } else {
+                reply->writeInt32(setRetransmitEndpoint(NULL));
+            }
+
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 9d45907..4ff1862 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -61,6 +61,7 @@
     mAudioSessionId = AudioSystem::newAudioSessionId();
     AudioSystem::acquireAudioSessionId(mAudioSessionId);
     mSendLevel = 0;
+    mRetransmitEndpointValid = false;
 }
 
 MediaPlayer::~MediaPlayer()
@@ -93,6 +94,7 @@
     mCurrentPosition = -1;
     mSeekPosition = -1;
     mVideoWidth = mVideoHeight = 0;
+    mRetransmitEndpointValid = false;
 }
 
 status_t MediaPlayer::setListener(const sp<MediaPlayerListener>& listener)
@@ -144,7 +146,8 @@
         const sp<IMediaPlayerService>& service(getMediaPlayerService());
         if (service != 0) {
             sp<IMediaPlayer> player(service->create(getpid(), this, mAudioSessionId));
-            if (NO_ERROR != player->setDataSource(url, headers)) {
+            if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
+                (NO_ERROR != player->setDataSource(url, headers))) {
                 player.clear();
             }
             err = attachNewPlayer(player);
@@ -160,7 +163,8 @@
     const sp<IMediaPlayerService>& service(getMediaPlayerService());
     if (service != 0) {
         sp<IMediaPlayer> player(service->create(getpid(), this, mAudioSessionId));
-        if (NO_ERROR != player->setDataSource(fd, offset, length)) {
+        if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
+            (NO_ERROR != player->setDataSource(fd, offset, length))) {
             player.clear();
         }
         err = attachNewPlayer(player);
@@ -175,7 +179,8 @@
     const sp<IMediaPlayerService>& service(getMediaPlayerService());
     if (service != 0) {
         sp<IMediaPlayer> player(service->create(getpid(), this, mAudioSessionId));
-        if (NO_ERROR != player->setDataSource(source)) {
+        if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
+            (NO_ERROR != player->setDataSource(source))) {
             player.clear();
         }
         err = attachNewPlayer(player);
@@ -469,6 +474,20 @@
     return NO_ERROR;
 }
 
+status_t MediaPlayer::doSetRetransmitEndpoint(const sp<IMediaPlayer>& player) {
+    Mutex::Autolock _l(mLock);
+
+    if (player == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    if (mRetransmitEndpointValid) {
+        return player->setRetransmitEndpoint(&mRetransmitEndpoint);
+    }
+
+    return OK;
+}
+
 status_t MediaPlayer::reset()
 {
     ALOGV("reset");
@@ -597,6 +616,34 @@
     return INVALID_OPERATION;
 }
 
+status_t MediaPlayer::setRetransmitEndpoint(const char* addrString,
+                                            uint16_t port) {
+    ALOGV("MediaPlayer::setRetransmitEndpoint(%s:%hu)",
+            addrString ? addrString : "(null)", port);
+
+    Mutex::Autolock _l(mLock);
+    if ((mPlayer != NULL) || (mCurrentState != MEDIA_PLAYER_IDLE))
+        return INVALID_OPERATION;
+
+    if (NULL == addrString) {
+        mRetransmitEndpointValid = false;
+        return OK;
+    }
+
+    struct in_addr saddr;
+    if(!inet_aton(addrString, &saddr)) {
+        return BAD_VALUE;
+    }
+
+    memset(&mRetransmitEndpoint, 0, sizeof(&mRetransmitEndpoint));
+    mRetransmitEndpoint.sin_family = AF_INET;
+    mRetransmitEndpoint.sin_addr   = saddr;
+    mRetransmitEndpoint.sin_port   = htons(port);
+    mRetransmitEndpointValid       = true;
+
+    return OK;
+}
+
 void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
 {
     ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 764eddc..1e2abf0 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -492,6 +492,7 @@
     mStatus = NO_INIT;
     mAudioSessionId = audioSessionId;
     mUID = uid;
+    mRetransmitEndpointValid = false;
 
 #if CALLBACK_ANTAGONIZER
     ALOGD("create Antagonizer");
@@ -602,10 +603,6 @@
         return AAH_RX_PLAYER;
     }
 
-    if (!strncasecmp("aahTX://", url, 8)) {
-        return AAH_TX_PLAYER;
-    }
-
     // use MidiFile for MIDI extensions
     int lenURL = strlen(url);
     for (int i = 0; i < NELEM(FILE_EXTS); ++i) {
@@ -621,6 +618,44 @@
     return getDefaultPlayerType();
 }
 
+player_type MediaPlayerService::Client::getPlayerType(int fd,
+                                                      int64_t offset,
+                                                      int64_t length)
+{
+    // Until re-transmit functionality is added to the existing core android
+    // players, we use the special AAH TX player whenever we were configured
+    // for retransmission.
+    if (mRetransmitEndpointValid) {
+        return AAH_TX_PLAYER;
+    }
+
+    return android::getPlayerType(fd, offset, length);
+}
+
+player_type MediaPlayerService::Client::getPlayerType(const char* url)
+{
+    // Until re-transmit functionality is added to the existing core android
+    // players, we use the special AAH TX player whenever we were configured
+    // for retransmission.
+    if (mRetransmitEndpointValid) {
+        return AAH_TX_PLAYER;
+    }
+
+    return android::getPlayerType(url);
+}
+
+player_type MediaPlayerService::Client::getPlayerType(
+        const sp<IStreamSource> &source) {
+    // Until re-transmit functionality is added to the existing core android
+    // players, we use the special AAH TX player whenever we were configured
+    // for retransmission.
+    if (mRetransmitEndpointValid) {
+        return AAH_TX_PLAYER;
+    }
+
+    return NU_PLAYER;
+}
+
 static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
         notify_callback_f notifyFunc)
 {
@@ -686,6 +721,49 @@
     return p;
 }
 
+sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
+        player_type playerType)
+{
+    ALOGV("player type = %d", playerType);
+
+    // create the right type of player
+    sp<MediaPlayerBase> p = createPlayer(playerType);
+    if (p == NULL) {
+        return p;
+    }
+
+    if (!p->hardwareOutput()) {
+        mAudioOutput = new AudioOutput(mAudioSessionId);
+        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
+    }
+
+    return p;
+}
+
+void MediaPlayerService::Client::setDataSource_post(
+        const sp<MediaPlayerBase>& p,
+        status_t status)
+{
+    ALOGV(" setDataSource");
+    mStatus = status;
+    if (mStatus != OK) {
+        ALOGE("  error: %d", mStatus);
+        return;
+    }
+
+    // Set the re-transmission endpoint if one was chosen.
+    if (mRetransmitEndpointValid) {
+        mStatus = p->setRetransmitEndpoint(&mRetransmitEndpoint);
+        if (mStatus != NO_ERROR) {
+            ALOGE("setRetransmitEndpoint error: %d", mStatus);
+        }
+    }
+
+    if (mStatus == OK) {
+        mPlayer = p;
+    }
+}
+
 status_t MediaPlayerService::Client::setDataSource(
         const char *url, const KeyedVector<String8, String8> *headers)
 {
@@ -717,25 +795,12 @@
         return mStatus;
     } else {
         player_type playerType = getPlayerType(url);
-        ALOGV("player type = %d", playerType);
-
-        // create the right type of player
-        sp<MediaPlayerBase> p = createPlayer(playerType);
-        if (p == NULL) return NO_INIT;
-
-        if (!p->hardwareOutput()) {
-            mAudioOutput = new AudioOutput(mAudioSessionId);
-            static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
+        sp<MediaPlayerBase> p = setDataSource_pre(playerType);
+        if (p == NULL) {
+            return NO_INIT;
         }
 
-        // now set data source
-        ALOGV(" setDataSource");
-        mStatus = p->setDataSource(url, headers);
-        if (mStatus == NO_ERROR) {
-            mPlayer = p;
-        } else {
-            ALOGE("  error: %d", mStatus);
-        }
+        setDataSource_post(p, p->setDataSource(url, headers));
         return mStatus;
     }
 }
@@ -766,46 +831,34 @@
         ALOGV("calculated length = %lld", length);
     }
 
+    // Until re-transmit functionality is added to the existing core android
+    // players, we use the special AAH TX player whenever we were configured for
+    // retransmission.
     player_type playerType = getPlayerType(fd, offset, length);
-    ALOGV("player type = %d", playerType);
-
-    // create the right type of player
-    sp<MediaPlayerBase> p = createPlayer(playerType);
-    if (p == NULL) return NO_INIT;
-
-    if (!p->hardwareOutput()) {
-        mAudioOutput = new AudioOutput(mAudioSessionId);
-        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
+    sp<MediaPlayerBase> p = setDataSource_pre(playerType);
+    if (p == NULL) {
+        return NO_INIT;
     }
 
     // now set data source
-    mStatus = p->setDataSource(fd, offset, length);
-    if (mStatus == NO_ERROR) mPlayer = p;
-
+    setDataSource_post(p, p->setDataSource(fd, offset, length));
     return mStatus;
 }
 
 status_t MediaPlayerService::Client::setDataSource(
         const sp<IStreamSource> &source) {
     // create the right type of player
-    sp<MediaPlayerBase> p = createPlayer(NU_PLAYER);
-
+    // Until re-transmit functionality is added to the existing core android
+    // players, we use the special AAH TX player whenever we were configured for
+    // retransmission.
+    player_type playerType = getPlayerType(source);
+    sp<MediaPlayerBase> p = setDataSource_pre(playerType);
     if (p == NULL) {
         return NO_INIT;
     }
 
-    if (!p->hardwareOutput()) {
-        mAudioOutput = new AudioOutput(mAudioSessionId);
-        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
-    }
-
     // now set data source
-    mStatus = p->setDataSource(source);
-
-    if (mStatus == OK) {
-        mPlayer = p;
-    }
-
+    setDataSource_post(p, p->setDataSource(source));
     return mStatus;
 }
 
@@ -1026,6 +1079,7 @@
 status_t MediaPlayerService::Client::reset()
 {
     ALOGV("[%d] reset", mConnId);
+    mRetransmitEndpointValid = false;
     sp<MediaPlayerBase> p = getPlayer();
     if (p == 0) return UNKNOWN_ERROR;
     return p->reset();
@@ -1100,6 +1154,36 @@
     return p->getParameter(key, reply);
 }
 
+status_t MediaPlayerService::Client::setRetransmitEndpoint(
+        const struct sockaddr_in* endpoint) {
+
+    if (NULL != endpoint) {
+        uint32_t a = ntohl(endpoint->sin_addr.s_addr);
+        uint16_t p = ntohs(endpoint->sin_port);
+        ALOGV("[%d] setRetransmitEndpoint(%u.%u.%u.%u:%hu)", mConnId,
+                (a >> 24), (a >> 16) & 0xFF, (a >> 8) & 0xFF, (a & 0xFF), p);
+    } else {
+        ALOGV("[%d] setRetransmitEndpoint = <none>", mConnId);
+    }
+
+    sp<MediaPlayerBase> p = getPlayer();
+
+    // Right now, the only valid time to set a retransmit endpoint is before
+    // player selection has been made (since the presence or absence of a
+    // retransmit endpoint is going to determine which player is selected during
+    // setDataSource).
+    if (p != 0) return INVALID_OPERATION;
+
+    if (NULL != endpoint) {
+        mRetransmitEndpoint = *endpoint;
+        mRetransmitEndpointValid = true;
+    } else {
+        mRetransmitEndpointValid = false;
+    }
+
+    return NO_ERROR;
+}
+
 void MediaPlayerService::Client::notify(
         void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
 {
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 52af64d..53847ed 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -18,6 +18,8 @@
 #ifndef ANDROID_MEDIAPLAYERSERVICE_H
 #define ANDROID_MEDIAPLAYERSERVICE_H
 
+#include <arpa/inet.h>
+
 #include <utils/Log.h>
 #include <utils/threads.h>
 #include <utils/List.h>
@@ -276,6 +278,7 @@
         virtual status_t        attachAuxEffect(int effectId);
         virtual status_t        setParameter(int key, const Parcel &request);
         virtual status_t        getParameter(int key, Parcel *reply);
+        virtual status_t        setRetransmitEndpoint(const struct sockaddr_in* endpoint);
 
         sp<MediaPlayerBase>     createPlayer(player_type playerType);
 
@@ -287,6 +290,14 @@
 
         virtual status_t        setDataSource(const sp<IStreamSource> &source);
 
+        sp<MediaPlayerBase>     setDataSource_pre(player_type playerType);
+        void                    setDataSource_post(const sp<MediaPlayerBase>& p,
+                                                   status_t status);
+
+        player_type             getPlayerType(int fd, int64_t offset, int64_t length);
+        player_type             getPlayerType(const char* url);
+        player_type             getPlayerType(const sp<IStreamSource> &source);
+
         static  void            notify(void* cookie, int msg,
                                        int ext1, int ext2, const Parcel *obj);
 
@@ -338,6 +349,8 @@
                     uid_t                       mUID;
                     sp<ANativeWindow>           mConnectedWindow;
                     sp<IBinder>                 mConnectedWindowBinder;
+                    struct sockaddr_in          mRetransmitEndpoint;
+                    bool                        mRetransmitEndpointValid;
 
         // Metadata filters.
         media::Metadata::Filter mMetadataAllow;  // protected by mLock
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 9a9d094..09e4e45 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -26,6 +26,7 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 
+#include <media/stagefright/MediaCodecList.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/NativeWindowWrapper.h>
 #include <media/stagefright/OMXClient.h>
@@ -328,7 +329,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 ACodec::ACodec()
-    : mNode(NULL),
+    : mQuirks(0),
+      mNode(NULL),
       mSentFormat(false),
       mIsEncoder(false),
       mShutdownInProgress(false) {
@@ -427,16 +429,12 @@
 
                 IOMX::buffer_id buffer;
 
-                if (!strncasecmp(
-                            mComponentName.c_str(), "OMX.TI.DUCATI1.VIDEO.", 21)) {
-                    if (portIndex == kPortIndexInput && i == 0) {
-                        // Only log this warning once per allocation round.
+                uint32_t requiresAllocateBufferBit =
+                    (portIndex == kPortIndexInput)
+                        ? OMXCodec::kRequiresAllocateBufferOnInputPorts
+                        : OMXCodec::kRequiresAllocateBufferOnOutputPorts;
 
-                        ALOGW("OMX.TI.DUCATI1.VIDEO.* require the use of "
-                             "OMX_AllocateBuffer instead of the preferred "
-                             "OMX_UseBuffer. Vendor must fix this.");
-                    }
-
+                if (mQuirks & requiresAllocateBufferBit) {
                     err = mOMX->allocateBufferWithBackup(
                             mNode, portIndex, mem, &buffer);
                 } else {
@@ -2588,12 +2586,19 @@
     sp<IOMX> omx = client.interface();
 
     Vector<String8> matchingCodecs;
+    Vector<uint32_t> matchingCodecQuirks;
 
     AString mime;
 
     AString componentName;
+    uint32_t quirks;
     if (msg->findString("componentName", &componentName)) {
         matchingCodecs.push_back(String8(componentName.c_str()));
+
+        if (!OMXCodec::findCodecQuirks(componentName.c_str(), &quirks)) {
+            quirks = 0;
+        }
+        matchingCodecQuirks.push_back(quirks);
     } else {
         CHECK(msg->findString("mime", &mime));
 
@@ -2607,7 +2612,8 @@
                 encoder, // createEncoder
                 NULL,  // matchComponentName
                 0,     // flags
-                &matchingCodecs);
+                &matchingCodecs,
+                &matchingCodecQuirks);
     }
 
     sp<CodecObserver> observer = new CodecObserver;
@@ -2616,6 +2622,7 @@
     for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
             ++matchIndex) {
         componentName = matchingCodecs.itemAt(matchIndex).string();
+        quirks = matchingCodecQuirks.itemAt(matchIndex);
 
         pid_t tid = androidGetTid();
         int prevPriority = androidGetThreadPriority(tid);
@@ -2646,6 +2653,7 @@
     observer->setNotificationMessage(notify);
 
     mCodec->mComponentName = componentName;
+    mCodec->mQuirks = quirks;
     mCodec->mOMX = omx;
     mCodec->mNode = node;
 
@@ -2692,6 +2700,7 @@
         mCodec->mNativeWindow.clear();
         mCodec->mNode = NULL;
         mCodec->mOMX.clear();
+        mCodec->mQuirks = 0;
         mCodec->mComponentName.clear();
 
         mCodec->changeState(mCodec->mUninitializedState);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 95bcada..21d6866 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -30,6 +30,7 @@
         MediaBuffer.cpp                   \
         MediaBufferGroup.cpp              \
         MediaCodec.cpp                    \
+        MediaCodecList.cpp                \
         MediaDefs.cpp                     \
         MediaExtractor.cpp                \
         MediaSource.cpp                   \
@@ -59,31 +60,33 @@
 	$(JNI_H_INCLUDE) \
         $(TOP)/frameworks/base/include/media/stagefright/openmax \
         $(TOP)/frameworks/base/include/media/stagefright/timedtext \
+        $(TOP)/external/expat/lib \
         $(TOP)/external/flac/include \
         $(TOP)/external/tremolo \
         $(TOP)/external/openssl/include \
 
 LOCAL_SHARED_LIBRARIES := \
         libbinder \
-        libmedia \
-        libutils \
-        libcutils \
-        libui \
-        libsonivox \
-        libvorbisidec \
-        libstagefright_yuv \
         libcamera_client \
-        libdrmframework \
-        libcrypto \
-        libssl \
-        libgui \
-        libstagefright_omx \
-        liblog \
-        libicuuc \
-        libicui18n \
-        libz \
-        libdl \
         libchromium_net \
+        libcrypto \
+        libcutils \
+        libdl \
+        libdrmframework \
+        libexpat \
+        libgui \
+        libicui18n \
+        libicuuc \
+        liblog \
+        libmedia \
+        libsonivox \
+        libssl \
+        libstagefright_omx \
+        libstagefright_yuv \
+        libui \
+        libutils \
+        libvorbisidec \
+        libz \
 
 LOCAL_STATIC_LIBRARIES := \
         libstagefright_color_conversion \
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
new file mode 100644
index 0000000..6b64e21
--- /dev/null
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -0,0 +1,475 @@
+/*
+ * Copyright 2012, 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 "MediaCodecList"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaCodecList.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
+
+#include <expat.h>
+
+namespace android {
+
+static Mutex sInitMutex;
+
+// static
+MediaCodecList *MediaCodecList::sCodecList;
+
+// static
+const MediaCodecList *MediaCodecList::getInstance() {
+    Mutex::Autolock autoLock(sInitMutex);
+
+    if (sCodecList == NULL) {
+        sCodecList = new MediaCodecList;
+    }
+
+    return sCodecList->initCheck() == OK ? sCodecList : NULL;
+}
+
+MediaCodecList::MediaCodecList()
+    : mInitCheck(NO_INIT) {
+    FILE *file = fopen("/etc/media_codecs.xml", "r");
+
+    if (file == NULL) {
+        ALOGW("unable to open media codecs configuration xml file.");
+        return;
+    }
+
+    parseXMLFile(file);
+
+    if (mInitCheck == OK) {
+        // These are currently still used by the video editing suite.
+
+        addMediaCodec(true /* encoder */, "AACEncoder", "audio/mp4a-latm");
+        addMediaCodec(true /* encoder */, "AVCEncoder", "video/avc");
+
+        addMediaCodec(true /* encoder */, "M4vH263Encoder");
+        addType("video/3gpp");
+        addType("video/mp4v-es");
+    }
+
+#if 0
+    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
+        const CodecInfo &info = mCodecInfos.itemAt(i);
+
+        AString line = info.mName;
+        line.append(" supports ");
+        for (size_t j = 0; j < mTypes.size(); ++j) {
+            uint32_t value = mTypes.valueAt(j);
+
+            if (info.mTypes & (1ul << value)) {
+                line.append(mTypes.keyAt(j));
+                line.append(" ");
+            }
+        }
+
+        ALOGI("%s", line.c_str());
+    }
+#endif
+
+    fclose(file);
+    file = NULL;
+}
+
+MediaCodecList::~MediaCodecList() {
+}
+
+status_t MediaCodecList::initCheck() const {
+    return mInitCheck;
+}
+
+void MediaCodecList::parseXMLFile(FILE *file) {
+    mInitCheck = OK;
+    mCurrentSection = SECTION_TOPLEVEL;
+    mDepth = 0;
+
+    XML_Parser parser = ::XML_ParserCreate(NULL);
+    CHECK(parser != NULL);
+
+    ::XML_SetUserData(parser, this);
+    ::XML_SetElementHandler(
+            parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
+
+    const int BUFF_SIZE = 512;
+    while (mInitCheck == OK) {
+        void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
+        if (buff == NULL) {
+            ALOGE("failed to in call to XML_GetBuffer()");
+            mInitCheck = UNKNOWN_ERROR;
+            break;
+        }
+
+        int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
+        if (bytes_read < 0) {
+            ALOGE("failed in call to read");
+            mInitCheck = ERROR_IO;
+            break;
+        }
+
+        if (::XML_ParseBuffer(parser, bytes_read, bytes_read == 0)
+                != XML_STATUS_OK) {
+            mInitCheck = ERROR_MALFORMED;
+            break;
+        }
+
+        if (bytes_read == 0) {
+            break;
+        }
+    }
+
+    ::XML_ParserFree(parser);
+
+    if (mInitCheck == OK) {
+        for (size_t i = mCodecInfos.size(); i-- > 0;) {
+            CodecInfo *info = &mCodecInfos.editItemAt(i);
+
+            if (info->mTypes == 0) {
+                // No types supported by this component???
+
+                ALOGW("Component %s does not support any type of media?",
+                      info->mName.c_str());
+
+                mCodecInfos.removeAt(i);
+            }
+        }
+    }
+
+    if (mInitCheck != OK) {
+        mCodecInfos.clear();
+        mCodecQuirks.clear();
+    }
+}
+
+// static
+void MediaCodecList::StartElementHandlerWrapper(
+        void *me, const char *name, const char **attrs) {
+    static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs);
+}
+
+// static
+void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) {
+    static_cast<MediaCodecList *>(me)->endElementHandler(name);
+}
+
+void MediaCodecList::startElementHandler(
+        const char *name, const char **attrs) {
+    if (mInitCheck != OK) {
+        return;
+    }
+
+    switch (mCurrentSection) {
+        case SECTION_TOPLEVEL:
+        {
+            if (!strcmp(name, "Decoders")) {
+                mCurrentSection = SECTION_DECODERS;
+            } else if (!strcmp(name, "Encoders")) {
+                mCurrentSection = SECTION_ENCODERS;
+            }
+            break;
+        }
+
+        case SECTION_DECODERS:
+        {
+            if (!strcmp(name, "MediaCodec")) {
+                mInitCheck =
+                    addMediaCodecFromAttributes(false /* encoder */, attrs);
+
+                mCurrentSection = SECTION_DECODER;
+            }
+            break;
+        }
+
+        case SECTION_ENCODERS:
+        {
+            if (!strcmp(name, "MediaCodec")) {
+                mInitCheck =
+                    addMediaCodecFromAttributes(true /* encoder */, attrs);
+
+                mCurrentSection = SECTION_ENCODER;
+            }
+            break;
+        }
+
+        case SECTION_DECODER:
+        case SECTION_ENCODER:
+        {
+            if (!strcmp(name, "Quirk")) {
+                mInitCheck = addQuirk(attrs);
+            } else if (!strcmp(name, "Type")) {
+                mInitCheck = addTypeFromAttributes(attrs);
+            }
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    ++mDepth;
+}
+
+void MediaCodecList::endElementHandler(const char *name) {
+    if (mInitCheck != OK) {
+        return;
+    }
+
+    switch (mCurrentSection) {
+        case SECTION_DECODERS:
+        {
+            if (!strcmp(name, "Decoders")) {
+                mCurrentSection = SECTION_TOPLEVEL;
+            }
+            break;
+        }
+
+        case SECTION_ENCODERS:
+        {
+            if (!strcmp(name, "Encoders")) {
+                mCurrentSection = SECTION_TOPLEVEL;
+            }
+            break;
+        }
+
+        case SECTION_DECODER:
+        {
+            if (!strcmp(name, "MediaCodec")) {
+                mCurrentSection = SECTION_DECODERS;
+            }
+            break;
+        }
+
+        case SECTION_ENCODER:
+        {
+            if (!strcmp(name, "MediaCodec")) {
+                mCurrentSection = SECTION_ENCODERS;
+            }
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    --mDepth;
+}
+
+status_t MediaCodecList::addMediaCodecFromAttributes(
+        bool encoder, const char **attrs) {
+    const char *name = NULL;
+    const char *type = NULL;
+
+    size_t i = 0;
+    while (attrs[i] != NULL) {
+        if (!strcmp(attrs[i], "name")) {
+            if (attrs[i + 1] == NULL) {
+                return -EINVAL;
+            }
+            name = attrs[i + 1];
+            ++i;
+        } else if (!strcmp(attrs[i], "type")) {
+            if (attrs[i + 1] == NULL) {
+                return -EINVAL;
+            }
+            type = attrs[i + 1];
+            ++i;
+        } else {
+            return -EINVAL;
+        }
+
+        ++i;
+    }
+
+    if (name == NULL) {
+        return -EINVAL;
+    }
+
+    addMediaCodec(encoder, name, type);
+
+    return OK;
+}
+
+void MediaCodecList::addMediaCodec(
+        bool encoder, const char *name, const char *type) {
+    mCodecInfos.push();
+    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+    info->mName = name;
+    info->mIsEncoder = encoder;
+    info->mTypes = 0;
+    info->mQuirks = 0;
+
+    if (type != NULL) {
+        addType(type);
+    }
+}
+
+status_t MediaCodecList::addQuirk(const char **attrs) {
+    const char *name = NULL;
+
+    size_t i = 0;
+    while (attrs[i] != NULL) {
+        if (!strcmp(attrs[i], "name")) {
+            if (attrs[i + 1] == NULL) {
+                return -EINVAL;
+            }
+            name = attrs[i + 1];
+            ++i;
+        } else {
+            return -EINVAL;
+        }
+
+        ++i;
+    }
+
+    if (name == NULL) {
+        return -EINVAL;
+    }
+
+    uint32_t bit;
+    ssize_t index = mCodecQuirks.indexOfKey(name);
+    if (index < 0) {
+        bit = mCodecQuirks.size();
+
+        if (bit == 32) {
+            ALOGW("Too many distinct quirk names in configuration.");
+            return OK;
+        }
+
+        mCodecQuirks.add(name, bit);
+    } else {
+        bit = mCodecQuirks.valueAt(index);
+    }
+
+    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+    info->mQuirks |= 1ul << bit;
+
+    return OK;
+}
+
+status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
+    const char *name = NULL;
+
+    size_t i = 0;
+    while (attrs[i] != NULL) {
+        if (!strcmp(attrs[i], "name")) {
+            if (attrs[i + 1] == NULL) {
+                return -EINVAL;
+            }
+            name = attrs[i + 1];
+            ++i;
+        } else {
+            return -EINVAL;
+        }
+
+        ++i;
+    }
+
+    if (name == NULL) {
+        return -EINVAL;
+    }
+
+    addType(name);
+
+    return OK;
+}
+
+void MediaCodecList::addType(const char *name) {
+    uint32_t bit;
+    ssize_t index = mTypes.indexOfKey(name);
+    if (index < 0) {
+        bit = mTypes.size();
+
+        if (bit == 32) {
+            ALOGW("Too many distinct type names in configuration.");
+            return;
+        }
+
+        mTypes.add(name, bit);
+    } else {
+        bit = mTypes.valueAt(index);
+    }
+
+    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+    info->mTypes |= 1ul << bit;
+}
+
+ssize_t MediaCodecList::findCodecByType(
+        const char *type, bool encoder, size_t startIndex) const {
+    ssize_t typeIndex = mTypes.indexOfKey(type);
+
+    if (typeIndex < 0) {
+        return -ENOENT;
+    }
+
+    uint32_t typeMask = 1ul << mTypes.valueAt(typeIndex);
+
+    while (startIndex < mCodecInfos.size()) {
+        const CodecInfo &info = mCodecInfos.itemAt(startIndex);
+
+        if (info.mIsEncoder == encoder && (info.mTypes & typeMask)) {
+            return startIndex;
+        }
+
+        ++startIndex;
+    }
+
+    return -ENOENT;
+}
+
+ssize_t MediaCodecList::findCodecByName(const char *name) const {
+    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
+        const CodecInfo &info = mCodecInfos.itemAt(i);
+
+        if (info.mName == name) {
+            return i;
+        }
+    }
+
+    return -ENOENT;
+}
+
+const char *MediaCodecList::getCodecName(size_t index) const {
+    if (index >= mCodecInfos.size()) {
+        return NULL;
+    }
+
+    const CodecInfo &info = mCodecInfos.itemAt(index);
+    return info.mName.c_str();
+}
+
+bool MediaCodecList::codecHasQuirk(
+        size_t index, const char *quirkName) const {
+    if (index >= mCodecInfos.size()) {
+        return NULL;
+    }
+
+    const CodecInfo &info = mCodecInfos.itemAt(index);
+
+    if (info.mQuirks != 0) {
+        ssize_t index = mCodecQuirks.indexOfKey(quirkName);
+        if (index >= 0 && info.mQuirks & (1ul << mCodecQuirks.valueAt(index))) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 1325462..966416e 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -33,6 +33,7 @@
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaCodecList.h>
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/OMXCodec.h>
@@ -57,11 +58,6 @@
 // component in question is buggy or not.
 const static uint32_t kMaxColorFormatSupported = 1000;
 
-struct CodecInfo {
-    const char *mime;
-    const char *codec;
-};
-
 #define FACTORY_CREATE_ENCODER(name) \
 static sp<MediaSource> Make##name(const sp<MediaSource> &source, const sp<MetaData> &meta) { \
     return new name(source, meta); \
@@ -96,83 +92,8 @@
     return NULL;
 }
 
+#undef FACTORY_CREATE_ENCODER
 #undef FACTORY_REF
-#undef FACTORY_CREATE
-
-static const CodecInfo kDecoderInfo[] = {
-    { MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },
-//    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },
-    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.google.mp3.decoder" },
-    { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II, "OMX.Nvidia.mp2.decoder" },
-//    { 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, "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_AAC, "OMX.Nvidia.aac.decoder" },
-    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
-    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.decoder" },
-    { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "OMX.google.g711.alaw.decoder" },
-    { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "OMX.google.g711.mlaw.decoder" },
-    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.DUCATI1.VIDEO.DECODER" },
-    { 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_H263, "OMX.TI.DUCATI1.VIDEO.DECODER" },
-    { 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_AVC, "OMX.TI.DUCATI1.VIDEO.DECODER" },
-    { 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.h264.decoder" },
-    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.google.avc.decoder" },
-    { MEDIA_MIMETYPE_AUDIO_VORBIS, "OMX.google.vorbis.decoder" },
-    { MEDIA_MIMETYPE_VIDEO_VPX, "OMX.google.vpx.decoder" },
-    { MEDIA_MIMETYPE_VIDEO_MPEG2, "OMX.Nvidia.mpeg2v.decode" },
-};
-
-static const CodecInfo kEncoderInfo[] = {
-    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.encode" },
-    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.google.amrnb.encoder" },
-    { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.encode" },
-    { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.google.amrwb.encoder" },
-    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
-    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.encoder" },
-    { MEDIA_MIMETYPE_AUDIO_AAC, "AACEncoder" },
-    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.DUCATI1.VIDEO.MPEG4E" },
-    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.encoder.mpeg4" },
-    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.encoder.mpeg4" },
-    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.encoder" },
-    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.Nvidia.mp4.encoder" },
-    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Encoder" },
-    { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Encoder" },
-    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.DUCATI1.VIDEO.MPEG4E" },
-    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.encoder.h263" },
-    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.encoder.h263" },
-    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.encoder" },
-    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.Nvidia.h263.encoder" },
-    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Encoder" },
-    { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Encoder" },
-    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.DUCATI1.VIDEO.H264E" },
-    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.encoder.avc" },
-    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.encoder.avc" },
-    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.encoder" },
-    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.Nvidia.h264.encoder" },
-    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.SEC.AVC.Encoder" },
-    { MEDIA_MIMETYPE_VIDEO_AVC, "AVCEncoder" },
-};
-
-#undef OPTIONAL
 
 #define CODEC_LOGI(x, ...) ALOGI("[%s] "x, mComponentName, ##__VA_ARGS__)
 #define CODEC_LOGV(x, ...) ALOGV("[%s] "x, mComponentName, ##__VA_ARGS__)
@@ -207,22 +128,6 @@
     OMXCodecObserver &operator=(const OMXCodecObserver &);
 };
 
-static const char *GetCodec(const CodecInfo *info, size_t numInfos,
-                            const char *mime, int index) {
-    CHECK(index >= 0);
-    for(size_t i = 0; i < numInfos; ++i) {
-        if (!strcasecmp(mime, info[i].mime)) {
-            if (index == 0) {
-                return info[i].codec;
-            }
-
-            --index;
-        }
-    }
-
-    return NULL;
-}
-
 template<class T>
 static void InitOMXParams(T *params) {
     params->nSize = sizeof(T);
@@ -278,119 +183,36 @@
 }
 
 // static
-uint32_t OMXCodec::getComponentQuirks(
-        const char *componentName, bool isEncoder) {
-    uint32_t quirks = 0;
-
-    if (!strcmp(componentName, "OMX.Nvidia.amr.decoder") ||
-         !strcmp(componentName, "OMX.Nvidia.amrwb.decoder") ||
-         !strcmp(componentName, "OMX.Nvidia.aac.decoder") ||
-         !strcmp(componentName, "OMX.Nvidia.mp3.decoder")) {
-        quirks |= kDecoderLiesAboutNumberOfChannels;
-    }
-
-    if (!strcmp(componentName, "OMX.TI.MP3.decode")) {
-        quirks |= kNeedsFlushBeforeDisable;
-        quirks |= kDecoderLiesAboutNumberOfChannels;
-    }
-    if (!strcmp(componentName, "OMX.TI.AAC.decode")) {
-        quirks |= kNeedsFlushBeforeDisable;
-        quirks |= kRequiresFlushCompleteEmulation;
-        quirks |= kSupportsMultipleFramesPerInputBuffer;
-    }
-    if (!strncmp(componentName, "OMX.qcom.video.encoder.", 23)) {
-        quirks |= kRequiresLoadedToIdleAfterAllocation;
-        quirks |= kRequiresAllocateBufferOnInputPorts;
-        quirks |= kRequiresAllocateBufferOnOutputPorts;
-        if (!strncmp(componentName, "OMX.qcom.video.encoder.avc", 26)) {
-
-            // The AVC encoder advertises the size of output buffers
-            // based on the input video resolution and assumes
-            // the worst/least compression ratio is 0.5. It is found that
-            // sometimes, the output buffer size is larger than
-            // size advertised by the encoder.
-            quirks |= kRequiresLargerEncoderOutputBuffer;
-        }
-    }
-    if (!strncmp(componentName, "OMX.qcom.7x30.video.encoder.", 28)) {
-    }
-    if (!strncmp(componentName, "OMX.qcom.video.decoder.", 23)) {
-        quirks |= kRequiresAllocateBufferOnOutputPorts;
-        quirks |= kDefersOutputBufferAllocation;
-    }
-    if (!strncmp(componentName, "OMX.qcom.7x30.video.decoder.", 28)) {
-        quirks |= kRequiresAllocateBufferOnInputPorts;
-        quirks |= kRequiresAllocateBufferOnOutputPorts;
-        quirks |= kDefersOutputBufferAllocation;
-    }
-
-    if (!strcmp(componentName, "OMX.TI.DUCATI1.VIDEO.DECODER")) {
-        quirks |= kRequiresAllocateBufferOnInputPorts;
-        quirks |= kRequiresAllocateBufferOnOutputPorts;
-    }
-
-    // FIXME:
-    // Remove the quirks after the work is done.
-    else if (!strcmp(componentName, "OMX.TI.DUCATI1.VIDEO.MPEG4E") ||
-             !strcmp(componentName, "OMX.TI.DUCATI1.VIDEO.H264E")) {
-
-        quirks |= kRequiresAllocateBufferOnInputPorts;
-        quirks |= kRequiresAllocateBufferOnOutputPorts;
-    }
-    else if (!strncmp(componentName, "OMX.TI.", 7)) {
-        // Apparently I must not use OMX_UseBuffer on either input or
-        // output ports on any of the TI components or quote:
-        // "(I) may have unexpected problem (sic) which can be timing related
-        //  and hard to reproduce."
-
-        quirks |= kRequiresAllocateBufferOnInputPorts;
-        quirks |= kRequiresAllocateBufferOnOutputPorts;
-        if (!strncmp(componentName, "OMX.TI.Video.encoder", 20)) {
-            quirks |= kAvoidMemcopyInputRecordingFrames;
-        }
-    }
-
-    if (!strcmp(componentName, "OMX.TI.Video.Decoder")) {
-        quirks |= kInputBufferSizesAreBogus;
-    }
-
-    if (!strncmp(componentName, "OMX.SEC.", 8) && !isEncoder) {
-        // These output buffers contain no video data, just some
-        // opaque information that allows the overlay to display their
-        // contents.
-        quirks |= kOutputBuffersAreUnreadable;
-    }
-
-    return quirks;
-}
-
-// static
 void OMXCodec::findMatchingCodecs(
         const char *mime,
         bool createEncoder, const char *matchComponentName,
         uint32_t flags,
-        Vector<String8> *matchingCodecs) {
+        Vector<String8> *matchingCodecs,
+        Vector<uint32_t> *matchingCodecQuirks) {
     matchingCodecs->clear();
 
-    for (int index = 0;; ++index) {
-        const char *componentName;
+    if (matchingCodecQuirks) {
+        matchingCodecQuirks->clear();
+    }
 
-        if (createEncoder) {
-            componentName = GetCodec(
-                    kEncoderInfo,
-                    sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]),
-                    mime, index);
-        } else {
-            componentName = GetCodec(
-                    kDecoderInfo,
-                    sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),
-                    mime, index);
-        }
+    const MediaCodecList *list = MediaCodecList::getInstance();
+    if (list == NULL) {
+        return;
+    }
 
-        if (!componentName) {
+    size_t index = 0;
+    for (;;) {
+        ssize_t matchIndex =
+            list->findCodecByType(mime, createEncoder, index);
+
+        if (matchIndex < 0) {
             break;
         }
 
+        index = matchIndex + 1;
+
+        const char *componentName = list->getCodecName(matchIndex);
+
         // If a specific codec is requested, skip the non-matching ones.
         if (matchComponentName && strcmp(componentName, matchComponentName)) {
             continue;
@@ -405,6 +227,10 @@
             (!(flags & (kSoftwareCodecsOnly | kHardwareCodecsOnly)))) {
 
             matchingCodecs->push(String8(componentName));
+
+            if (matchingCodecQuirks) {
+                matchingCodecQuirks->push(getComponentQuirks(list, matchIndex));
+            }
         }
     }
 
@@ -414,6 +240,45 @@
 }
 
 // static
+uint32_t OMXCodec::getComponentQuirks(
+        const MediaCodecList *list, size_t index) {
+    uint32_t quirks = 0;
+    if (list->codecHasQuirk(
+                index, "requires-allocate-on-input-ports")) {
+        quirks |= kRequiresAllocateBufferOnInputPorts;
+    }
+    if (list->codecHasQuirk(
+                index, "requires-allocate-on-output-ports")) {
+        quirks |= kRequiresAllocateBufferOnOutputPorts;
+    }
+    if (list->codecHasQuirk(
+                index, "output-buffers-are-unreadable")) {
+        quirks |= kOutputBuffersAreUnreadable;
+    }
+
+    return quirks;
+}
+
+// static
+bool OMXCodec::findCodecQuirks(const char *componentName, uint32_t *quirks) {
+    const MediaCodecList *list = MediaCodecList::getInstance();
+
+    if (list == NULL) {
+        return false;
+    }
+
+    ssize_t index = list->findCodecByName(componentName);
+
+    if (index < 0) {
+        return false;
+    }
+
+    *quirks = getComponentQuirks(list, index);
+
+    return true;
+}
+
+// static
 sp<MediaSource> OMXCodec::Create(
         const sp<IOMX> &omx,
         const sp<MetaData> &meta, bool createEncoder,
@@ -435,8 +300,10 @@
     CHECK(success);
 
     Vector<String8> matchingCodecs;
+    Vector<uint32_t> matchingCodecQuirks;
     findMatchingCodecs(
-            mime, createEncoder, matchComponentName, flags, &matchingCodecs);
+            mime, createEncoder, matchComponentName, flags,
+            &matchingCodecs, &matchingCodecQuirks);
 
     if (matchingCodecs.isEmpty()) {
         return NULL;
@@ -447,6 +314,7 @@
 
     for (size_t i = 0; i < matchingCodecs.size(); ++i) {
         const char *componentNameBase = matchingCodecs[i].string();
+        uint32_t quirks = matchingCodecQuirks[i];
         const char *componentName = componentNameBase;
 
         AString tmp;
@@ -470,8 +338,6 @@
 
         ALOGV("Attempting to allocate OMX node '%s'", componentName);
 
-        uint32_t quirks = getComponentQuirks(componentNameBase, createEncoder);
-
         if (!createEncoder
                 && (quirks & kOutputBuffersAreUnreadable)
                 && (flags & kClientNeedsFramebuffer)) {
@@ -627,16 +493,6 @@
             CODEC_LOGI(
                     "AVC profile = %u (%s), level = %u",
                     profile, AVCProfileToString(profile), level);
-
-            if (!strcmp(mComponentName, "OMX.TI.Video.Decoder")
-                && (profile != kAVCProfileBaseline || level > 30)) {
-                // This stream exceeds the decoder's capabilities. The decoder
-                // does not handle this gracefully and would clobber the heap
-                // and wreak havoc instead...
-
-                ALOGE("Profile and/or level exceed the decoder's capabilities.");
-                return ERROR_UNSUPPORTED;
-            }
         } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
             addCodecSpecificData(data, size);
 
@@ -692,40 +548,11 @@
         }
     }
 
-    if (!strcasecmp(mMIME, MEDIA_MIMETYPE_IMAGE_JPEG)
-        && !strcmp(mComponentName, "OMX.TI.JPEG.decode")) {
-        OMX_COLOR_FORMATTYPE format =
-            OMX_COLOR_Format32bitARGB8888;
-            // OMX_COLOR_FormatYUV420PackedPlanar;
-            // OMX_COLOR_FormatCbYCrY;
-            // OMX_COLOR_FormatYUV411Planar;
-
-        int32_t width, height;
-        bool success = meta->findInt32(kKeyWidth, &width);
-        success = success && meta->findInt32(kKeyHeight, &height);
-
-        int32_t compressedSize;
-        success = success && meta->findInt32(
-                kKeyMaxInputSize, &compressedSize);
-
-        CHECK(success);
-        CHECK(compressedSize > 0);
-
-        setImageOutputFormat(format, width, height);
-        setJPEGInputFormat(width, height, (OMX_U32)compressedSize);
-    }
-
     int32_t maxInputSize;
     if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
         setMinBufferSize(kPortIndexInput, (OMX_U32)maxInputSize);
     }
 
-    if (!strcmp(mComponentName, "OMX.TI.AMR.encode")
-        || !strcmp(mComponentName, "OMX.TI.WBAMR.encode")
-        || !strcmp(mComponentName, "OMX.TI.AAC.encode")) {
-        setMinBufferSize(kPortIndexOutput, 8192);  // XXX
-    }
-
     initOutputFormat(meta);
 
     if ((mFlags & kClientNeedsFramebuffer)
@@ -829,21 +656,6 @@
              index, format.eCompressionFormat, format.eColorFormat);
 #endif
 
-        if (!strcmp("OMX.TI.Video.encoder", mComponentName)) {
-            if (portIndex == kPortIndexInput
-                    && colorFormat == format.eColorFormat) {
-                // eCompressionFormat does not seem right.
-                found = true;
-                break;
-            }
-            if (portIndex == kPortIndexOutput
-                    && compressionFormat == format.eCompressionFormat) {
-                // eColorFormat does not seem right.
-                found = true;
-                break;
-            }
-        }
-
         if (format.eCompressionFormat == compressionFormat
                 && format.eColorFormat == colorFormat) {
             found = true;
@@ -906,13 +718,8 @@
     int32_t targetColorFormat;
     if (meta->findInt32(kKeyColorFormat, &targetColorFormat)) {
         *colorFormat = (OMX_COLOR_FORMATTYPE) targetColorFormat;
-    } else {
-        if (!strcasecmp("OMX.TI.Video.encoder", mComponentName)) {
-            *colorFormat = OMX_COLOR_FormatYCbYCr;
-        }
     }
 
-
     // Check whether the target color format is supported.
     return isColorFormatSupported(*colorFormat, kPortIndexInput);
 }
@@ -3326,13 +3133,6 @@
 
     info->mStatus = OWNED_BY_COMPONENT;
 
-    // This component does not ever signal the EOS flag on output buffers,
-    // Thanks for nothing.
-    if (mSignalledEOS && !strcmp(mComponentName, "OMX.TI.Video.encoder")) {
-        mNoMoreOutputData = true;
-        mBufferFilled.signal();
-    }
-
     return true;
 }
 
diff --git a/media/libstagefright/codecs/aacenc/Android.mk b/media/libstagefright/codecs/aacenc/Android.mk
index 34a2796..509193c 100644
--- a/media/libstagefright/codecs/aacenc/Android.mk
+++ b/media/libstagefright/codecs/aacenc/Android.mk
@@ -79,7 +79,7 @@
 endif
 
 ifeq ($(VOTT), v7)
-LOCAL_CFLAGS += -DARMV5E -DARMV7Neon -DARM_INASM -DARMV5_INASM
+LOCAL_CFLAGS += -DARMV5E -DARMV7Neon -DARM_INASM -DARMV5_INASM -DARMV6_INASM
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/asm/ARMV5E
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/asm/ARMV7
 endif
diff --git a/media/libstagefright/codecs/aacenc/basic_op/basic_op.h b/media/libstagefright/codecs/aacenc/basic_op/basic_op.h
index e878bba..5cd7e5f 100644
--- a/media/libstagefright/codecs/aacenc/basic_op/basic_op.h
+++ b/media/libstagefright/codecs/aacenc/basic_op/basic_op.h
@@ -227,13 +227,7 @@
 #if ARMV4_INASM
 __inline Word32 ASM_L_shr(Word32 L_var1, Word16 var2)
 {
-	Word32 result;
-	asm (
-		"MOV %[result], %[L_var1], ASR %[var2] \n"
-		:[result]"=r"(result)
-		:[L_var1]"r"(L_var1), [var2]"r"(var2)
-		);
-	return result;
+	return L_var1 >> var2;
 }
 
 __inline Word32 ASM_L_shl(Word32 L_var1, Word16 var2)
@@ -264,6 +258,18 @@
 
 __inline Word32 ASM_shl(Word32 L_var1, Word16 var2)
 {
+#if ARMV6_SAT
+	Word32 result;
+	asm (
+		"CMP	%[var2], #16\n"
+		"MOVLT  %[result], %[L_var1], ASL %[var2]\n"
+		"MOVGE  %[result], %[L_var1], ASL #16\n"
+		"SSAT   %[result], #16, %[result]\n"
+		:[result]"=r"(result)
+		:[L_var1]"r"(L_var1), [var2]"r"(var2)
+		);
+	return result;
+#else
 	Word32 result;
 	Word32 tmp;
 	asm (
@@ -277,6 +283,7 @@
 		:[L_var1]"r"(L_var1), [var2]"r"(var2), [mask]"r"(0x7fff)
 		);
 	return result;
+#endif
 }
 #endif
 
@@ -288,7 +295,15 @@
 #if (SATRUATE_IS_INLINE)
 __inline Word16 saturate(Word32 L_var1)
 {
-#if ARMV5TE_SAT
+#if ARMV6_SAT
+    Word16 result;
+	asm (
+		"SSAT %[result], #16, %[L_var1]"
+		: [result]"=r"(result)
+		: [L_var1]"r"(L_var1)
+		);
+	return result;
+#elif ARMV5TE_SAT
 	Word16 result;
 	Word32 tmp;
 	asm volatile (
@@ -445,8 +460,7 @@
 	Word32 result;
 	asm (
 		"SMULBB %[result], %[var1], %[var2] \n"
-		"QADD %[result], %[result], %[result] \n"
-		"QSUB %[result], %[L_var3], %[result]\n"
+		"QDSUB %[result], %[L_var3], %[result]\n"
 		:[result]"=&r"(result)
 		:[L_var3]"r"(L_var3), [var1]"r"(var1), [var2]"r"(var2)
 		);
@@ -671,7 +685,16 @@
 #if (MULT_IS_INLINE)
 __inline Word16 mult (Word16 var1, Word16 var2)
 {
-#if ARMV5TE_MULT
+#if ARMV5TE_MULT && ARMV6_SAT
+	Word32 result;
+	asm (
+		"SMULBB %[result], %[var1], %[var2] \n"
+		"SSAT   %[result], #16, %[result], ASR #15 \n"
+		:[result]"=r"(result)
+		:[var1]"r"(var1), [var2]"r"(var2)
+		);
+	return result;
+#elif ARMV5TE_MULT
 	Word32 result, tmp;
 	asm (
 		"SMULBB %[tmp], %[var1], %[var2] \n"
@@ -990,8 +1013,7 @@
 	Word32 result;
 	asm (
 		"SMULBB %[result], %[var1], %[var2]\n"
-		"QADD	%[result], %[result], %[result]\n"
-		"QADD   %[result], %[result], %[L_var3]\n"
+		"QDADD  %[result], %[L_var3], %[result]\n"
 		:[result]"=&r"(result)
 		: [L_var3]"r"(L_var3), [var1]"r"(var1), [var2]"r"(var2)
 		);
diff --git a/media/libstagefright/codecs/aacenc/basic_op/typedefs.h b/media/libstagefright/codecs/aacenc/basic_op/typedefs.h
index 8ef43e2..6059237 100644
--- a/media/libstagefright/codecs/aacenc/basic_op/typedefs.h
+++ b/media/libstagefright/codecs/aacenc/basic_op/typedefs.h
@@ -128,6 +128,13 @@
     #define ARMV5TE_NORM_L        1
 	#define ARMV5TE_L_MPY_LS	  1
 #endif
+#if ARMV6_INASM
+    #undef  ARMV5TE_ADD
+    #define ARMV5TE_ADD           0
+    #undef  ARMV5TE_SUB
+    #define ARMV5TE_SUB           0
+    #define ARMV6_SAT             1
+#endif
 
 //basic operation functions optimization flags
 #define SATRUATE_IS_INLINE              1   //define saturate as inline function
diff --git a/media/libstagefright/codecs/aacenc/src/bitbuffer.c b/media/libstagefright/codecs/aacenc/src/bitbuffer.c
index a706893..0ce93d3 100644
--- a/media/libstagefright/codecs/aacenc/src/bitbuffer.c
+++ b/media/libstagefright/codecs/aacenc/src/bitbuffer.c
@@ -152,6 +152,7 @@
 
   wBitPos = hBitBuf->wBitPos;
   wBitPos += noBitsToWrite;
+  writeValue &= ~(0xffffffff << noBitsToWrite); // Mask out everything except the lowest noBitsToWrite bits
   writeValue <<= 32 - wBitPos;
   writeValue |= hBitBuf->cache;
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
index 73ee4dd..5e649e0 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
@@ -365,6 +365,7 @@
                 mRecorder.setVideoSize(video_width, video_height);
                 mRecorder.setVideoEncoder(video_encoder);
                 mRecorder.setAudioEncoder(audio_encoder);
+                mRecorder.setVideoEncodingBitRate(bit_rate);
                 Log.v(TAG, "mediaRecorder setPreview");
                 mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
                 mRecorder.prepare();
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 14ce266..276ca21 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -53,6 +53,7 @@
                                                  // where fade starts
     static final float ALPHA_FADE_END = 0.5f; // fraction of thumbnail width
                                               // beyond which alpha->0
+    private float mMinAlpha = 0f;
 
     private float mPagingTouchSlop;
     private Callback mCallback;
@@ -120,6 +121,10 @@
                 v.getMeasuredHeight();
     }
 
+    public void setMinAlpha(float minAlpha) {
+        mMinAlpha = minAlpha;
+    }
+
     private float getAlphaForOffset(View view) {
         float viewSize = getSize(view);
         final float fadeSize = ALPHA_FADE_END * viewSize;
@@ -130,10 +135,7 @@
         } else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) {
             result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
         }
-        // Make .03 alpha the minimum so you always see the item a bit-- slightly below
-        // .03, the item disappears entirely (as if alpha = 0) and that discontinuity looks
-        // a bit jarring
-        return Math.max(0.03f, result);
+        return Math.max(mMinAlpha, result);
     }
 
     // invalidate the view's own bounds all the way up the view hierarchy
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 76e6ee8..97c9553 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -44,7 +44,7 @@
 import java.util.ArrayList;
 
 public class RecentsHorizontalScrollView extends HorizontalScrollView
-    implements SwipeHelper.Callback {
+        implements SwipeHelper.Callback, RecentsPanelView.RecentsScrollView {
     private static final String TAG = RecentsPanelView.TAG;
     private static final boolean DEBUG = RecentsPanelView.DEBUG;
     private LinearLayout mLinearLayout;
@@ -65,6 +65,10 @@
         mRecycledViews = new ArrayList<View>();
     }
 
+    public void setMinSwipeAlpha(float minAlpha) {
+        mSwipeHelper.setMinAlpha(minAlpha);
+    }
+
     private int scrollPositionOfMostRecent() {
         return mLinearLayout.getWidth() - getWidth();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 18132ff..61aaa43 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -92,6 +92,13 @@
         public void onRecentsPanelVisibilityChanged(boolean visible);
     }
 
+    public static interface RecentsScrollView {
+        public int numItemsInOneScreenful();
+        public void setAdapter(TaskDescriptionAdapter adapter);
+        public void setCallback(RecentsCallback callback);
+        public void setMinSwipeAlpha(float minAlpha);
+    }
+
     private final class OnLongClickDelegate implements View.OnLongClickListener {
         View mOtherView;
         OnLongClickDelegate(View other) {
@@ -196,16 +203,11 @@
     }
 
     public int numItemsInOneScreenful() {
-        if (mRecentsContainer instanceof RecentsHorizontalScrollView){
-            RecentsHorizontalScrollView scrollView
-                    = (RecentsHorizontalScrollView) mRecentsContainer;
+        if (mRecentsContainer instanceof RecentsScrollView){
+            RecentsScrollView scrollView
+                    = (RecentsScrollView) mRecentsContainer;
             return scrollView.numItemsInOneScreenful();
-        } else if (mRecentsContainer instanceof RecentsVerticalScrollView){
-            RecentsVerticalScrollView scrollView
-                    = (RecentsVerticalScrollView) mRecentsContainer;
-            return scrollView.numItemsInOneScreenful();
-        }
-        else {
+        }  else {
             throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
         }
     }
@@ -427,18 +429,12 @@
         mRecentsContainer = (ViewGroup) findViewById(R.id.recents_container);
         mStatusBarTouchProxy = (StatusBarTouchProxy) findViewById(R.id.status_bar_touch_proxy);
         mListAdapter = new TaskDescriptionAdapter(mContext);
-        if (mRecentsContainer instanceof RecentsHorizontalScrollView){
-            RecentsHorizontalScrollView scrollView
-                    = (RecentsHorizontalScrollView) mRecentsContainer;
+        if (mRecentsContainer instanceof RecentsScrollView){
+            RecentsScrollView scrollView
+                    = (RecentsScrollView) mRecentsContainer;
             scrollView.setAdapter(mListAdapter);
             scrollView.setCallback(this);
-        } else if (mRecentsContainer instanceof RecentsVerticalScrollView){
-            RecentsVerticalScrollView scrollView
-                    = (RecentsVerticalScrollView) mRecentsContainer;
-            scrollView.setAdapter(mListAdapter);
-            scrollView.setCallback(this);
-        }
-        else {
+        } else {
             throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
         }
 
@@ -467,6 +463,14 @@
         };
     }
 
+    public void setMinSwipeAlpha(float minAlpha) {
+        if (mRecentsContainer instanceof RecentsScrollView){
+            RecentsScrollView scrollView
+                = (RecentsScrollView) mRecentsContainer;
+            scrollView.setMinSwipeAlpha(minAlpha);
+        }
+    }
+
     private void createCustomAnimations(LayoutTransition transitioner) {
         transitioner.setDuration(200);
         transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
@@ -523,8 +527,7 @@
         synchronized (td) {
             if (mRecentsContainer != null) {
                 ViewGroup container = mRecentsContainer;
-                if (container instanceof HorizontalScrollView
-                        || container instanceof ScrollView) {
+                if (container instanceof RecentsScrollView) {
                     container = (ViewGroup) container.findViewById(
                             R.id.recents_linear_layout);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index 1bfd000..f4e516c 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -43,7 +43,8 @@
 
 import java.util.ArrayList;
 
-public class RecentsVerticalScrollView extends ScrollView implements SwipeHelper.Callback {
+public class RecentsVerticalScrollView extends ScrollView
+        implements SwipeHelper.Callback, RecentsPanelView.RecentsScrollView {
     private static final String TAG = RecentsPanelView.TAG;
     private static final boolean DEBUG = RecentsPanelView.DEBUG;
     private LinearLayout mLinearLayout;
@@ -65,6 +66,10 @@
         mRecycledViews = new ArrayList<View>();
     }
 
+    public void setMinSwipeAlpha(float minAlpha) {
+        mSwipeHelper.setMinAlpha(minAlpha);
+    }
+
     private int scrollPositionOfMostRecent() {
         return mLinearLayout.getHeight() - getHeight();
     }
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 b2d9e64..2e1f120 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -432,6 +432,11 @@
         mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL,
                 mRecentsPanel));
         mRecentsPanel.setVisibility(View.GONE);
+
+        // Make .03 alpha the minimum so you always see the item a bit-- slightly below
+        // .03, the item disappears entirely (as if alpha = 0) and that discontinuity looks
+        // a bit jarring
+        mRecentsPanel.setMinSwipeAlpha(0.03f);
         WindowManager.LayoutParams lp = getRecentsLayoutParams(mRecentsPanel.getLayoutParams());
 
         WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index c59290c..95704f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -203,7 +203,7 @@
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
         Handler handler = new WifiHandler();
         mWifiChannel = new AsyncChannel();
-        Messenger wifiMessenger = mWifiManager.getMessenger();
+        Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger();
         if (wifiMessenger != null) {
             mWifiChannel.connect(mContext, handler, wifiMessenger);
         }
@@ -767,17 +767,10 @@
             } else if (!mWifiConnected) {
                 mWifiSsid = null;
             }
-            // Apparently the wifi level is not stable at this point even if we've just connected to
-            // the network; we need to wait for an RSSI_CHANGED_ACTION for that. So let's just set
-            // it to 0 for now
-            mWifiLevel = 0;
-            mWifiRssi = -200;
         } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
-            if (mWifiConnected) {
-                mWifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
-                mWifiLevel = WifiManager.calculateSignalLevel(
-                        mWifiRssi, WifiIcons.WIFI_LEVEL_COUNT);
-            }
+            mWifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
+            mWifiLevel = WifiManager.calculateSignalLevel(
+                    mWifiRssi, WifiIcons.WIFI_LEVEL_COUNT);
         }
 
         updateWifiIcons();
diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
index 3384661..edf5199 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/LockScreen.java
@@ -231,7 +231,8 @@
                 if (!mCameraDisabled) {
                     // Start the Camera
                     Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
-                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                            | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                     try {
                         ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
                     } catch (RemoteException e) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 462c2fa..8f7b35c 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1464,7 +1464,7 @@
         mMasterVolume(audioFlinger->masterVolumeSW_l()),
         mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false)
 {
-    snprintf(mName, kNameLength, "AudioOut_%d", id);
+    snprintf(mName, kNameLength, "AudioOut_%X", id);
 
     readOutputParameters();
 
@@ -1993,9 +1993,12 @@
 
 bool AudioFlinger::MixerThread::threadLoop()
 {
+    // DirectOutputThread has single trackToRemove instead of Vector
     Vector< sp<Track> > tracksToRemove;
+    // DirectOutputThread has activeTrack here
     nsecs_t standbyTime = systemTime();
     size_t mixBufferSize = mFrameCount * mFrameSize;
+
     // FIXME: Relaxed timing because of a certain device that can't meet latency
     // Should be reduced to 2x after the vendor fixes the driver issue
     // increase threshold again due to low power audio mode. The way this warning threshold is
@@ -2003,18 +2006,26 @@
     nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 15;
     nsecs_t lastWarning = 0;
     bool longStandbyExit = false;
+
     uint32_t activeSleepTime = activeSleepTimeUs();
     uint32_t idleSleepTime = idleSleepTimeUs();
     uint32_t sleepTime = idleSleepTime;
+
     uint32_t sleepTimeShift = 0;
-    Vector< sp<EffectChain> > effectChains;
     CpuStats cpuStats;
 
+    // DirectOutputThread has shorter standbyDelay
+
     acquireWakeLock();
 
     while (!exitPending())
     {
         cpuStats.sample();
+
+        // DirectOutputThread has rampVolume, leftVol, rightVol
+
+        Vector< sp<EffectChain> > effectChains;
+
         processConfigEvents();
 
         mixer_state mixerStatus = MIXER_IDLE;
@@ -2024,28 +2035,29 @@
 
             if (checkForNewParameters_l()) {
                 mixBufferSize = mFrameCount * mFrameSize;
+
                 // FIXME: Relaxed timing because of a certain device that can't meet latency
                 // Should be reduced to 2x after the vendor fixes the driver issue
                 // increase threshold again due to low power audio mode. The way this warning
                 // threshold is calculated and its usefulness should be reconsidered anyway.
                 maxPeriod = seconds(mFrameCount) / mSampleRate * 15;
+
                 activeSleepTime = activeSleepTimeUs();
                 idleSleepTime = idleSleepTimeUs();
+                // DirectOutputThread updates standbyDelay also
             }
 
-            const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
-
             // put audio hardware into standby after short delay
-            if (CC_UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) ||
-                        mSuspended)) {
+            if (CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
+                        mSuspended > 0)) {
                 if (!mStandby) {
-                    ALOGV("Audio hardware entering standby, mixer %p, mSuspended %d", this, mSuspended);
+                    ALOGV("Audio hardware entering standby, mixer %p, suspend count %u", this, mSuspended);
                     mOutput->stream->common.standby(&mOutput->stream->common);
                     mStandby = true;
                     mBytesWritten = 0;
                 }
 
-                if (!activeTracks.size() && mConfigEvents.isEmpty()) {
+                if (!mActiveTracks.size() && mConfigEvents.isEmpty()) {
                     // we're about to wait, flush the binder command buffer
                     IPCThreadState::self()->flushCommands();
 
@@ -2068,7 +2080,7 @@
                 }
             }
 
-            mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);
+            mixerStatus = prepareTracks_l(&tracksToRemove);
 
             // prevent any changes in effect chain list and in each effect chain
             // during mixing and effect process as the audio buffers could be deleted
@@ -2130,12 +2142,15 @@
             // TODO add standby time extension fct of effect tail
         }
 
-        if (mSuspended) {
+        if (mSuspended > 0) {
             sleepTime = suspendSleepTimeUs();
         }
 
         // only process effects if we're going to write
         if (sleepTime == 0) {
+
+            // DirectOutputThread adds applyVolume here
+
             for (size_t i = 0; i < effectChains.size(); i ++) {
                 effectChains[i]->process_l();
             }
@@ -2146,14 +2161,16 @@
 
         // sleepTime == 0 means we must write to audio hardware
         if (sleepTime == 0) {
+            // FIXME Only in MixerThread, and rewrite to reduce number of system calls
             mLastWriteTime = systemTime();
             mInWrite = true;
             mBytesWritten += mixBufferSize;
-
             int bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
             if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
             mNumWrites++;
             mInWrite = false;
+
+            // Only in MixerThread: start of write blocked detection
             nsecs_t now = systemTime();
             nsecs_t delta = now - mLastWriteTime;
             if (!mStandby && delta > maxPeriod) {
@@ -2167,12 +2184,14 @@
                     longStandbyExit = true;
                 }
             }
+            // end of write blocked detection
+
             mStandby = false;
         } else {
             usleep(sleepTime);
         }
 
-        // finally let go of all our tracks, without the lock held
+        // finally let go of removed track(s), without the lock held
         // since we can't guarantee the destructors won't acquire that
         // same lock.
         tracksToRemove.clear();
@@ -2180,8 +2199,12 @@
         // Effect chains will be actually deleted here if they were removed from
         // mEffectChains list during mixing or effects processing
         effectChains.clear();
+
+        // FIXME Note that the above .clear() is no longer necessary since effectChains
+        // is now local to this block, but will keep it for now (at least until merge done).
     }
 
+    // put output stream into standby mode
     if (!mStandby) {
         mOutput->stream->common.standby(&mOutput->stream->common);
     }
@@ -2194,12 +2217,12 @@
 
 // prepareTracks_l() must be called with ThreadBase::mLock held
 AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
-        const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove)
+        Vector< sp<Track> > *tracksToRemove)
 {
 
     mixer_state mixerStatus = MIXER_IDLE;
     // find out which tracks need to be processed
-    size_t count = activeTracks.size();
+    size_t count = mActiveTracks.size();
     size_t mixedTracks = 0;
     size_t tracksWithEffect = 0;
 
@@ -2219,7 +2242,7 @@
     }
 
     for (size_t i=0 ; i<count ; i++) {
-        sp<Track> t = activeTracks[i].promote();
+        sp<Track> t = mActiveTracks[i].promote();
         if (t == 0) continue;
 
         // this const just means the local variable doesn't change
@@ -2706,13 +2729,20 @@
 
 bool AudioFlinger::DirectOutputThread::threadLoop()
 {
+    // MixerThread has Vector instead of single trackToRemove
     sp<Track> trackToRemove;
-    sp<Track> activeTrack;
+
     nsecs_t standbyTime = systemTime();
-    size_t mixBufferSize = mFrameCount*mFrameSize;
+    size_t mixBufferSize = mFrameCount * mFrameSize;
+
+    // MixerThread has relaxed timing: maxPeriod, lastWarning, longStandbyExit
+
     uint32_t activeSleepTime = activeSleepTimeUs();
     uint32_t idleSleepTime = idleSleepTimeUs();
     uint32_t sleepTime = idleSleepTime;
+
+    // MixerThread has sleepTimeShift and cpuStats
+
     // use shorter standby delay as on normal output to release
     // hardware resources as soon as possible
     nsecs_t standbyDelay = microseconds(activeSleepTime*2);
@@ -2721,20 +2751,30 @@
 
     while (!exitPending())
     {
+        // MixerThread has cpuStats.sample()
+
         bool rampVolume;
         uint16_t leftVol;
         uint16_t rightVol;
+
         Vector< sp<EffectChain> > effectChains;
 
         processConfigEvents();
 
+        // MixerThread does not have activeTrack here
+        sp<Track> activeTrack;
+
         mixer_state mixerStatus = MIXER_IDLE;
         { // scope for the mLock
 
             Mutex::Autolock _l(mLock);
 
             if (checkForNewParameters_l()) {
-                mixBufferSize = mFrameCount*mFrameSize;
+                mixBufferSize = mFrameCount * mFrameSize;
+
+                // different calculations here
+                standbyDelay = microseconds(activeSleepTime*2);
+
                 activeSleepTime = activeSleepTimeUs();
                 idleSleepTime = idleSleepTimeUs();
                 standbyDelay = microseconds(activeSleepTime*2);
@@ -2742,10 +2782,9 @@
 
             // put audio hardware into standby after short delay
             if (CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
-                        mSuspended)) {
-                // wait until we have something to do...
+                        mSuspended > 0)) {
                 if (!mStandby) {
-                    ALOGV("Audio hardware entering standby, mixer %p, mSuspended %d", this, mSuspended);
+                    ALOGV("Audio hardware entering standby, mixer %p, suspend count %u", this, mSuspended);
                     mOutput->stream->common.standby(&mOutput->stream->common);
                     mStandby = true;
                     mBytesWritten = 0;
@@ -2758,19 +2797,27 @@
                     if (exitPending()) break;
 
                     releaseWakeLock_l();
+                    // wait until we have something to do...
                     ALOGV("Thread %p type %d TID %d going to sleep", this, mType, gettid());
                     mWaitWorkCV.wait(mLock);
                     ALOGV("Thread %p type %d TID %d waking up", this, mType, gettid());
                     acquireWakeLock_l();
 
+                    // MixerThread has "mPrevMixerStatus = MIXER_IDLE"
                     checkSilentMode_l();
 
+                    // MixerThread has different standbyDelay
                     standbyTime = systemTime() + standbyDelay;
                     sleepTime = idleSleepTime;
+                    // MixerThread has "sleepTimeShift = 0"
                     continue;
                 }
             }
 
+            // MixerThread has "mixerStatus = prepareTracks_l(...)"
+
+            // equivalent to MixerThread's lockEffectChains_l, but without the lock
+            // FIXME - is it OK to omit the lock here?
             effectChains = mEffectChains;
 
             // find out which tracks need to be processed
@@ -2901,6 +2948,7 @@
             lockEffectChains_l(effectChains);
        }
 
+        // For DirectOutputThread, this test is equivalent to "activeTrack != 0"
         if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
             AudioBufferProvider::Buffer buffer;
             size_t frameCount = mFrameCount;
@@ -2933,15 +2981,18 @@
             }
         }
 
-        if (mSuspended) {
+        if (mSuspended > 0) {
             sleepTime = suspendSleepTimeUs();
         }
 
         // only process effects if we're going to write
         if (sleepTime == 0) {
+
+            // MixerThread does not have applyVolume
             if (mixerStatus == MIXER_TRACKS_READY) {
                 applyVolume(leftVol, rightVol, rampVolume);
             }
+
             for (size_t i = 0; i < effectChains.size(); i ++) {
                 effectChains[i]->process_l();
             }
@@ -2959,12 +3010,15 @@
             if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
             mNumWrites++;
             mInWrite = false;
+
+            // MixerThread has write blocked detection here
+
             mStandby = false;
         } else {
             usleep(sleepTime);
         }
 
-        // finally let go of removed track, without the lock held
+        // finally let go of removed track(s), without the lock held
         // since we can't guarantee the destructors won't acquire that
         // same lock.
         trackToRemove.clear();
@@ -2973,8 +3027,12 @@
         // Effect chains will be actually deleted here if they were removed from
         // mEffectChains list during mixing or effects processing
         effectChains.clear();
+
+        // FIXME Note that the above .clear() is no longer necessary since effectChains
+        // is now local to this block, but will keep it for now (at least until merge done).
     }
 
+    // put output stream into standby mode
     if (!mStandby) {
         mOutput->stream->common.standby(&mOutput->stream->common);
     }
@@ -3099,18 +3157,24 @@
 {
     Vector< sp<Track> > tracksToRemove;
     nsecs_t standbyTime = systemTime();
-    size_t mixBufferSize = mFrameCount*mFrameSize;
+    size_t mixBufferSize = mFrameCount * mFrameSize;
+
+    // Only in DuplicatingThread
     SortedVector< sp<OutputTrack> > outputTracks;
     uint32_t writeFrames = 0;
+
     uint32_t activeSleepTime = activeSleepTimeUs();
     uint32_t idleSleepTime = idleSleepTimeUs();
     uint32_t sleepTime = idleSleepTime;
-    Vector< sp<EffectChain> > effectChains;
 
     acquireWakeLock();
 
     while (!exitPending())
     {
+        // MixerThread has cpuStats.sample
+
+        Vector< sp<EffectChain> > effectChains;
+
         processConfigEvents();
 
         mixer_state mixerStatus = MIXER_IDLE;
@@ -3119,22 +3183,25 @@
             Mutex::Autolock _l(mLock);
 
             if (checkForNewParameters_l()) {
-                mixBufferSize = mFrameCount*mFrameSize;
+                mixBufferSize = mFrameCount * mFrameSize;
+
+                // Only in DuplicatingThread
                 updateWaitTime();
+
                 activeSleepTime = activeSleepTimeUs();
                 idleSleepTime = idleSleepTimeUs();
             }
 
-            const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
-
+            // Only in DuplicatingThread
             for (size_t i = 0; i < mOutputTracks.size(); i++) {
                 outputTracks.add(mOutputTracks[i]);
             }
 
             // put audio hardware into standby after short delay
-            if (CC_UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) ||
-                         mSuspended)) {
+            if (CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
+                         mSuspended > 0)) {
                 if (!mStandby) {
+                    // DuplicatingThread implements standby by stopping all tracks
                     for (size_t i = 0; i < outputTracks.size(); i++) {
                         outputTracks[i]->stop();
                     }
@@ -3142,7 +3209,7 @@
                     mBytesWritten = 0;
                 }
 
-                if (!activeTracks.size() && mConfigEvents.isEmpty()) {
+                if (!mActiveTracks.size() && mConfigEvents.isEmpty()) {
                     // we're about to wait, flush the binder command buffer
                     IPCThreadState::self()->flushCommands();
                     outputTracks.clear();
@@ -3156,15 +3223,17 @@
                     ALOGV("Thread %p type %d TID %d waking up", this, mType, gettid());
                     acquireWakeLock_l();
 
+                    // MixerThread has "mPrevMixerStatus = MIXER_IDLE"
                     checkSilentMode_l();
 
                     standbyTime = systemTime() + mStandbyTimeInNsecs;
                     sleepTime = idleSleepTime;
+                    // MixerThread has sleepTimeShift
                     continue;
                 }
             }
 
-            mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);
+            mixerStatus = prepareTracks_l(&tracksToRemove);
 
             // prevent any changes in effect chain list and in each effect chain
             // during mixing and effect process as the audio buffers could be deleted
@@ -3172,6 +3241,7 @@
             lockEffectChains_l(effectChains);
         }
 
+        // Duplicating Thread is completely different here
         if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
             // mix buffers...
             if (outputsReady(outputTracks)) {
@@ -3201,7 +3271,7 @@
             }
         }
 
-        if (mSuspended) {
+        if (mSuspended > 0) {
             sleepTime = suspendSleepTimeUs();
         }
 
@@ -3223,11 +3293,14 @@
             }
             mStandby = false;
             mBytesWritten += mixBufferSize;
+
+            // MixerThread has write blocked detection here
+
         } else {
             usleep(sleepTime);
         }
 
-        // finally let go of all our tracks, without the lock held
+        // finally let go of removed track(s), without the lock held
         // since we can't guarantee the destructors won't acquire that
         // same lock.
         tracksToRemove.clear();
@@ -3236,8 +3309,14 @@
         // Effect chains will be actually deleted here if they were removed from
         // mEffectChains list during mixing or effects processing
         effectChains.clear();
+
+        // FIXME Note that the above .clear() is no longer necessary since effectChains
+        // is now local to this block, but will keep it for now (at least until merge done).
     }
 
+    // MixerThread and DirectOutpuThread have standby here,
+    // but for DuplicatingThread this is handled by the outputTracks
+
     releaseWakeLock();
 
     ALOGV("Thread %p type %d exiting", this, mType);
@@ -4791,7 +4870,7 @@
     // mBytesRead is only meaningful while active, and so is cleared in start()
     // (but might be better to also clear here for dump?)
 {
-    snprintf(mName, kNameLength, "AudioIn_%d", id);
+    snprintf(mName, kNameLength, "AudioIn_%X", id);
 
     readInputParameters();
 }
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index e908d3f..bdaf97c 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -455,9 +455,10 @@
         virtual     status_t addEffectChain_l(const sp<EffectChain>& chain) = 0;
                     // remove an effect chain from the chain list (mEffectChains)
         virtual     size_t removeEffectChain_l(const sp<EffectChain>& chain) = 0;
-                    // lock mall effect chains Mutexes. Must be called before releasing the
+                    // lock all effect chains Mutexes. Must be called before releasing the
                     // ThreadBase mutex before processing the mixer and effects. This guarantees the
                     // integrity of the chains during the process.
+                    // Also sets the parameter 'effectChains' to current value of mEffectChains.
                     void lockEffectChains_l(Vector<sp <EffectChain> >& effectChains);
                     // unlock effect chains after process
                     void unlockEffectChains(const Vector<sp<EffectChain> >& effectChains);
@@ -548,7 +549,7 @@
                     Vector< sp<EffectChain> > mEffectChains;
                     uint32_t                mDevice;    // output device for PlaybackThread
                                                         // input + output devices for RecordThread
-                    static const int        kNameLength = 32;
+                    static const int        kNameLength = 16;   // prctl(PR_SET_NAME) limit
                     char                    mName[kNameLength];
                     sp<IPowerManager>       mPowerManager;
                     sp<IBinder>             mWakeLockToken;
@@ -575,9 +576,11 @@
     public:
 
         enum mixer_state {
-            MIXER_IDLE,
-            MIXER_TRACKS_ENABLED,
-            MIXER_TRACKS_READY
+            MIXER_IDLE,             // no active tracks
+            MIXER_TRACKS_ENABLED,   // at least one active track, but no track has any data ready
+            MIXER_TRACKS_READY      // at least one active track, and at least one track has data
+            // standby mode does not have an enum value
+            // suspend by audio policy manager is orthogonal to mixer state
         };
 
         // playback track
@@ -821,8 +824,8 @@
                     virtual audio_stream_t* stream();
 
                     void        suspend() { mSuspended++; }
-                    void        restore() { if (mSuspended) mSuspended--; }
-                    bool        isSuspended() const { return (mSuspended != 0); }
+                    void        restore() { if (mSuspended > 0) mSuspended--; }
+                    bool        isSuspended() const { return (mSuspended > 0); }
         virtual     String8     getParameters(const String8& keys);
         virtual     void        audioConfigChanged_l(int event, int param = 0);
         virtual     status_t    getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
@@ -843,7 +846,7 @@
 
     protected:
         int16_t*                        mMixBuffer;
-        int                             mSuspended;
+        uint32_t                        mSuspended;     // suspend count, > 0 means suspended
         int                             mBytesWritten;
     private:
         // mMasterMute is in both PlaybackThread and in AudioFlinger.  When a
@@ -913,8 +916,11 @@
         virtual     status_t    dumpInternals(int fd, const Vector<String16>& args);
 
     protected:
-                    mixer_state prepareTracks_l(const SortedVector< wp<Track> >& activeTracks,
-                                                Vector< sp<Track> > *tracksToRemove);
+                    // prepareTracks_l reads and writes mActiveTracks, and also returns the
+                    // pending set of tracks to remove via Vector 'tracksToRemove'.  The caller is
+                    // responsible for clearing or destroying this Vector later on, when it
+                    // is safe to do so. That will drop the final ref count and destroy the tracks.
+                    mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
         virtual     int         getTrackName_l();
         virtual     void        deleteTrackName_l(int name);
         virtual     uint32_t    idleSleepTimeUs();
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 987b039..753b1d2 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -69,7 +69,7 @@
     // start tone playback thread
     mTonePlaybackThread = new AudioCommandThread(String8(""));
     // start audio commands thread
-    mAudioCommandThread = new AudioCommandThread(String8("ApmCommandThread"));
+    mAudioCommandThread = new AudioCommandThread(String8("ApmCommand"));
 
     /* instantiate the audio policy manager */
     rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
@@ -638,7 +638,7 @@
     if (mName != "") {
         run(mName.string(), ANDROID_PRIORITY_AUDIO);
     } else {
-        run("AudioCommandThread", ANDROID_PRIORITY_AUDIO);
+        run("AudioCommand", ANDROID_PRIORITY_AUDIO);
     }
 }
 
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index 679fd30..962c917 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -66,7 +66,7 @@
                                         audio_format_t format = AUDIO_FORMAT_DEFAULT,
                                         uint32_t channels = 0,
                                         audio_policy_output_flags_t flags =
-                                            AUDIO_POLICY_OUTPUT_FLAG_INDIRECT);
+                                                AUDIO_POLICY_OUTPUT_FLAG_NONE);
     virtual status_t startOutput(audio_io_handle_t output,
                                  audio_stream_type_t stream,
                                  int session = 0);
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 7b4372f..c8f49ee 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -45,6 +45,7 @@
 import android.os.IBinder;
 import android.os.IPowerManager;
 import android.os.LocalPowerManager;
+import android.os.Message;
 import android.os.Power;
 import android.os.PowerManager;
 import android.os.Process;
@@ -57,6 +58,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.view.WindowManagerPolicy;
+import static android.view.WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR;
 import static android.provider.Settings.System.DIM_SCREEN;
 import static android.provider.Settings.System.SCREEN_BRIGHTNESS;
 import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE;
@@ -76,6 +78,7 @@
 
 public class PowerManagerService extends IPowerManager.Stub
         implements LocalPowerManager, Watchdog.Monitor {
+    private static final int NOMINAL_FRAME_TIME_MS = 1000/60;
 
     private static final String TAG = "PowerManagerService";
     static final String PARTIAL_NAME = "PowerManagerService";
@@ -131,6 +134,7 @@
     private static final int DEFAULT_SCREEN_BRIGHTNESS = 192;
 
     // flags for setPowerState
+    private static final int ALL_LIGHTS_OFF         = 0x00000000;
     private static final int SCREEN_ON_BIT          = 0x00000001;
     private static final int SCREEN_BRIGHT_BIT      = 0x00000002;
     private static final int BUTTON_BRIGHT_BIT      = 0x00000004;
@@ -157,11 +161,12 @@
     // used for noChangeLights in setPowerState()
     private static final int LIGHTS_MASK        = SCREEN_BRIGHT_BIT | BUTTON_BRIGHT_BIT | KEYBOARD_BRIGHT_BIT;
 
+    // animate screen lights in PowerManager (as opposed to SurfaceFlinger)
     boolean mAnimateScreenLights = true;
 
-    static final int ANIM_STEPS = 60/4;
+    static final int ANIM_STEPS = 60; // nominal # of frames at 60Hz
     // Slower animation for autobrightness changes
-    static final int AUTOBRIGHTNESS_ANIM_STEPS = 60;
+    static final int AUTOBRIGHTNESS_ANIM_STEPS = 2 * ANIM_STEPS;
     // Number of steps when performing a more immediate brightness change.
     static final int IMMEDIATE_ANIM_STEPS = 4;
 
@@ -221,12 +226,11 @@
     private UnsynchronizedWakeLock mPreventScreenOnPartialLock;
     private UnsynchronizedWakeLock mProximityPartialLock;
     private HandlerThread mHandlerThread;
-    private HandlerThread mScreenOffThread;
     private Handler mScreenOffHandler;
+    private Handler mScreenBrightnessHandler;
     private Handler mHandler;
     private final TimeoutTask mTimeoutTask = new TimeoutTask();
-    private final BrightnessState mScreenBrightness
-            = new BrightnessState(SCREEN_BRIGHT_BIT);
+    private ScreenBrightnessAnimator mScreenBrightnessAnimator;
     private boolean mStillNeedSleepNotification;
     private boolean mIsPowered = false;
     private IActivityManager mActivityService;
@@ -271,6 +275,7 @@
     private int mWarningSpewThrottleCount;
     private long mWarningSpewThrottleTime;
     private int mAnimationSetting = ANIM_SETTING_OFF;
+    private float mWindowScaleAnimation;
 
     // Must match with the ISurfaceComposer constants in C++.
     private static final int ANIM_SETTING_ON = 0x01;
@@ -285,7 +290,8 @@
     private static final boolean mSpew = false;
     private static final boolean mDebugProximitySensor = (false || mSpew);
     private static final boolean mDebugLightSensor = (false || mSpew);
-    
+    private static final boolean mDebugLightAnimation = (false || mSpew);
+
     private native void nativeInit();
     private native void nativeSetPowerState(boolean screenOn, boolean screenBright);
     private native void nativeStartSurfaceFlingerAnimation(int mode);
@@ -487,10 +493,10 @@
                 // recalculate everything
                 setScreenOffTimeoutsLocked();
 
-                final float windowScale = getFloat(WINDOW_ANIMATION_SCALE, 1.0f);
+                mWindowScaleAnimation = getFloat(WINDOW_ANIMATION_SCALE, 1.0f);
                 final float transitionScale = getFloat(TRANSITION_ANIMATION_SCALE, 1.0f);
                 mAnimationSetting = 0;
-                if (windowScale > 0.5f) {
+                if (mWindowScaleAnimation > 0.5f) {
                     mAnimationSetting |= ANIM_SETTING_OFF;
                 }
                 if (transitionScale > 0.5f) {
@@ -540,28 +546,20 @@
         }
 
         mInitComplete = false;
-        mScreenOffThread = new HandlerThread("PowerManagerService.mScreenOffThread") {
-            @Override
-            protected void onLooperPrepared() {
-                mScreenOffHandler = new Handler();
-                synchronized (mScreenOffThread) {
-                    mInitComplete = true;
-                    mScreenOffThread.notifyAll();
-                }
-            }
-        };
-        mScreenOffThread.start();
+        mScreenBrightnessAnimator = new ScreenBrightnessAnimator("mScreenBrightnessUpdaterThread",
+                Process.THREAD_PRIORITY_DISPLAY);
+        mScreenBrightnessAnimator.start();
 
-        synchronized (mScreenOffThread) {
+        synchronized (mScreenBrightnessAnimator) {
             while (!mInitComplete) {
                 try {
-                    mScreenOffThread.wait();
+                    mScreenBrightnessAnimator.wait();
                 } catch (InterruptedException e) {
                     // Ignore
                 }
             }
         }
-        
+
         mInitComplete = false;
         mHandlerThread = new HandlerThread("PowerManagerService") {
             @Override
@@ -581,7 +579,7 @@
                 }
             }
         }
-        
+
         nativeInit();
         Power.powerInitNative();
         synchronized (mLocks) {
@@ -1079,7 +1077,6 @@
 
             int oldPokey = mPokey;
             int cumulative = 0;
-            boolean oldAwakeOnSet = mPokeAwakeOnSet;
             boolean awakeOnSet = false;
             for (PokeLock p: mPokeLocks.values()) {
                 cumulative |= p.pokey;
@@ -1199,7 +1196,7 @@
                     + " mLightSensorKeyboardBrightness=" + mLightSensorKeyboardBrightness);
             pw.println("  mUseSoftwareAutoBrightness=" + mUseSoftwareAutoBrightness);
             pw.println("  mAutoBrightessEnabled=" + mAutoBrightessEnabled);
-            mScreenBrightness.dump(pw, "  mScreenBrightness: ");
+            mScreenBrightnessAnimator.dump(pw, "  mScreenBrightnessAnimator: ");
 
             int N = mLocks.size();
             pw.println();
@@ -1431,7 +1428,7 @@
 
     private WindowManagerPolicy.ScreenOnListener mScreenOnListener =
             new WindowManagerPolicy.ScreenOnListener() {
-                @Override public void onScreenOn() {
+                public void onScreenOn() {
                     synchronized (mLocks) {
                         if (mPreparingForScreenOn) {
                             mPreparingForScreenOn = false;
@@ -1720,7 +1717,8 @@
                             + Integer.toHexString(mPowerState)
                             + " mSkippedScreenOn=" + mSkippedScreenOn);
                 }
-                mScreenBrightness.forceValueLocked(Power.BRIGHTNESS_OFF);
+                mScreenBrightnessHandler.removeMessages(ScreenBrightnessAnimator.ANIMATE_LIGHTS);
+                mScreenBrightnessAnimator.animateTo(Power.BRIGHTNESS_OFF, SCREEN_BRIGHT_BIT, 0);
             }
         }
         int err = Power.setScreenState(on);
@@ -1879,7 +1877,7 @@
                     }
                     mPowerState &= ~SCREEN_ON_BIT;
                     mScreenOffReason = reason;
-                    if (!mScreenBrightness.animating) {
+                    if (!mScreenBrightnessAnimator.isAnimating()) {
                         err = screenOffFinishedAnimatingLocked(reason);
                     } else {
                         err = 0;
@@ -1953,11 +1951,11 @@
 
         // If the screen is not currently on, we will want to delay actually
         // turning the lights on if we are still getting the UI put up.
-        if ((oldState&SCREEN_ON_BIT) == 0 || mSkippedScreenOn) {
+        if ((oldState & SCREEN_ON_BIT) == 0 || mSkippedScreenOn) {
             // Don't turn screen on until we know we are really ready to.
             // This is to avoid letting the screen go on before things like the
             // lock screen have been displayed.
-            if ((mSkippedScreenOn=shouldDeferScreenOnLocked())) {
+            if ((mSkippedScreenOn = shouldDeferScreenOnLocked())) {
                 newState &= ~(SCREEN_ON_BIT|SCREEN_BRIGHT_BIT);
             }
         }
@@ -2017,7 +2015,7 @@
                     case SCREEN_BRIGHT_BIT:
                     default:
                         // not possible
-                        nominalCurrentValue = (int)mScreenBrightness.curValue;
+                        nominalCurrentValue = (int)mScreenBrightnessAnimator.getCurrentBrightness();
                         break;
                 }
             }
@@ -2067,8 +2065,8 @@
                 Binder.restoreCallingIdentity(identity);
             }
             if (!mSkippedScreenOn) {
-                mScreenBrightness.setTargetLocked(brightness, steps,
-                        INITIAL_SCREEN_BRIGHTNESS, nominalCurrentValue);
+                int dt = steps * NOMINAL_FRAME_TIME_MS;
+                mScreenBrightnessAnimator.animateTo(brightness, SCREEN_BRIGHT_BIT, dt);
                 if (DEBUG_SCREEN_ON) {
                     RuntimeException e = new RuntimeException("here");
                     e.fillInStackTrace();
@@ -2111,152 +2109,162 @@
         }
     }
 
-    private void setLightBrightness(int mask, int value) {
-        int brightnessMode = (mAutoBrightessEnabled
+    /**
+     * Note: by design this class does not hold mLocks while calling native methods.
+     * Nor should it. Ever.
+     */
+    class ScreenBrightnessAnimator extends HandlerThread {
+        static final int ANIMATE_LIGHTS = 10;
+        static final int ANIMATE_POWER_OFF = 11;
+        volatile int startValue;
+        volatile int endValue;
+        volatile int currentValue;
+        private int currentMask;
+        private int duration;
+        private long startTimeMillis;
+        private final String prefix;
+
+        public ScreenBrightnessAnimator(String name, int priority) {
+            super(name, priority);
+            prefix = name;
+        }
+
+        @Override
+        protected void onLooperPrepared() {
+            mScreenBrightnessHandler = new Handler() {
+                public void handleMessage(Message msg) {
+                    int brightnessMode = (mAutoBrightessEnabled && !mInitialAnimation
                             ? LightsService.BRIGHTNESS_MODE_SENSOR
                             : LightsService.BRIGHTNESS_MODE_USER);
-        if ((mask & SCREEN_BRIGHT_BIT) != 0) {
-            if (DEBUG_SCREEN_ON) {
-                RuntimeException e = new RuntimeException("here");
-                e.fillInStackTrace();
-                Slog.i(TAG, "Set LCD brightness: " + value, e);
+                    if (msg.what == ANIMATE_LIGHTS) {
+                        final int mask = msg.arg1;
+                        int value = msg.arg2;
+                        long tStart = SystemClock.uptimeMillis();
+                        if ((mask & SCREEN_BRIGHT_BIT) != 0) {
+                            if (mDebugLightAnimation) Log.v(TAG, "Set brightness: " + value);
+                            mLcdLight.setBrightness(value, brightnessMode);
+                        }
+                        long elapsed = SystemClock.uptimeMillis() - tStart;
+                        if ((mask & BUTTON_BRIGHT_BIT) != 0) {
+                            mButtonLight.setBrightness(value);
+                        }
+                        if ((mask & KEYBOARD_BRIGHT_BIT) != 0) {
+                            mKeyboardLight.setBrightness(value);
+                        }
+
+                        if (elapsed > 100) {
+                            Log.e(TAG, "Excessive delay setting brightness: " + elapsed
+                                    + "ms, mask=" + mask);
+                        }
+
+                        // Throttle brightness updates to frame refresh rate
+                        int delay = elapsed < NOMINAL_FRAME_TIME_MS ? NOMINAL_FRAME_TIME_MS : 0;
+                        synchronized(this) {
+                            currentValue = value;
+                        }
+                        animateInternal(mask, false, delay);
+                    } else if (msg.what == ANIMATE_POWER_OFF) {
+                        int mode = msg.arg1;
+                        nativeStartSurfaceFlingerAnimation(mode);
+                    }
+                }
+            };
+            synchronized (this) {
+                mInitComplete = true;
+                notifyAll();
             }
-            mLcdLight.setBrightness(value, brightnessMode);
         }
-        if ((mask & BUTTON_BRIGHT_BIT) != 0) {
-            mButtonLight.setBrightness(value);
+
+        private void animateInternal(int mask, boolean turningOff, int delay) {
+            synchronized (this) {
+                if (currentValue != endValue) {
+                    final long now = SystemClock.elapsedRealtime();
+                    final int elapsed = (int) (now - startTimeMillis);
+                    int newValue;
+                    if (elapsed < duration) {
+                        int delta = endValue - startValue;
+                        newValue = startValue + delta * elapsed / duration;
+                        newValue = Math.max(Power.BRIGHTNESS_OFF, newValue);
+                        newValue = Math.min(Power.BRIGHTNESS_ON, newValue);
+                    } else {
+                        newValue = endValue;
+                        mInitialAnimation = false;
+                    }
+
+                    if (mDebugLightAnimation) {
+                        Log.v(TAG, "Animating light: " + "start:" + startValue
+                                + ", end:" + endValue + ", elapsed:" + elapsed
+                                + ", duration:" + duration + ", current:" + currentValue
+                                + ", delay:" + delay);
+                    }
+
+                    if (turningOff && !mHeadless && !mAnimateScreenLights) {
+                        int mode = mScreenOffReason == OFF_BECAUSE_OF_PROX_SENSOR
+                                ? 0 : mAnimationSetting;
+                        if (mDebugLightAnimation) Log.v(TAG, "Doing power-off anim, mode=" + mode);
+                        mScreenBrightnessHandler.obtainMessage(ANIMATE_POWER_OFF, mode, 0)
+                                .sendToTarget();
+                    }
+                    Message msg = mScreenBrightnessHandler
+                            .obtainMessage(ANIMATE_LIGHTS, mask, newValue);
+                    mScreenBrightnessHandler.sendMessageDelayed(msg, delay);
+                }
+            }
         }
-        if ((mask & KEYBOARD_BRIGHT_BIT) != 0) {
-            mKeyboardLight.setBrightness(value);
+
+        public void dump(PrintWriter pw, String string) {
+            pw.println(prefix + "animating: " + "start:" + startValue + ", end:" + endValue
+                    + ", duration:" + duration + ", current:" + currentValue);
+        }
+
+        public void animateTo(int target, int mask, int animationDuration) {
+            synchronized(this) {
+                startValue = currentValue;
+                endValue = target;
+                currentMask = mask;
+                duration = (int) (mWindowScaleAnimation * animationDuration);
+                startTimeMillis = SystemClock.elapsedRealtime();
+                mInitialAnimation = currentValue == 0 && target > 0;
+
+                if (mDebugLightAnimation) {
+                    Log.v(TAG, "animateTo(target=" + target + ", mask=" + mask
+                            + ", duration=" + animationDuration +")"
+                            + ", currentValue=" + currentValue
+                            + ", startTime=" + startTimeMillis);
+                }
+
+                if (target != currentValue) {
+                    final boolean turningOff = endValue == Power.BRIGHTNESS_OFF;
+                    if (turningOff) {
+                        // Cancel all pending animations since we're turning off
+                        mScreenBrightnessHandler.removeCallbacksAndMessages(null);
+                        screenOffFinishedAnimatingLocked(mScreenOffReason);
+                        duration = 200; // TODO: how long should this be?
+                    }
+                    animateInternal(mask, turningOff, 0);
+                }
+            }
+        }
+
+        public int getCurrentBrightness() {
+            synchronized (this) {
+                return currentValue;
+            }
+        }
+
+        public boolean isAnimating() {
+            synchronized (this) {
+                return currentValue != endValue;
+            }
+        }
+
+        public void cancelAnimation() {
+            animateTo(endValue, currentMask, 0);
         }
     }
 
-    class BrightnessState implements Runnable {
-        final int mask;
-
-        boolean initialized;
-        int targetValue;
-        float curValue;
-        float delta;
-        boolean animating;
-
-        BrightnessState(int m) {
-            mask = m;
-        }
-
-        public void dump(PrintWriter pw, String prefix) {
-            pw.println(prefix + "animating=" + animating
-                    + " targetValue=" + targetValue
-                    + " curValue=" + curValue
-                    + " delta=" + delta);
-        }
-
-        void forceValueLocked(int value) {
-            targetValue = -1;
-            curValue = value;
-            setLightBrightness(mask, value);
-            if (animating) {
-                finishAnimationLocked(false, value);
-            }
-        }
-
-        void setTargetLocked(int target, int stepsToTarget, int initialValue,
-                int nominalCurrentValue) {
-            if (!initialized) {
-                initialized = true;
-                curValue = (float)initialValue;
-            } else if (targetValue == target) {
-                return;
-            }
-            targetValue = target;
-            delta = (targetValue -
-                    (nominalCurrentValue >= 0 ? nominalCurrentValue : curValue))
-                    / stepsToTarget;
-            if (mSpew || DEBUG_SCREEN_ON) {
-                String noticeMe = nominalCurrentValue == curValue ? "" : "  ******************";
-                Slog.i(TAG, "setTargetLocked mask=" + mask + " curValue=" + curValue
-                        + " target=" + target + " targetValue=" + targetValue + " delta=" + delta
-                        + " nominalCurrentValue=" + nominalCurrentValue
-                        + noticeMe);
-            }
-            animating = true;
-
-            if (mSpew) {
-                Slog.i(TAG, "scheduling light animator");
-            }
-            mScreenOffHandler.removeCallbacks(this);
-            mScreenOffHandler.post(this);
-        }
-
-        boolean stepLocked() {
-            if (!animating) return false;
-            if (false && mSpew) {
-                Slog.i(TAG, "Step target " + mask + ": cur=" + curValue
-                        + " target=" + targetValue + " delta=" + delta);
-            }
-            curValue += delta;
-            int curIntValue = (int)curValue;
-            boolean more = true;
-            if (delta == 0) {
-                curValue = curIntValue = targetValue;
-                more = false;
-            } else if (delta > 0) {
-                if (curIntValue >= targetValue) {
-                    curValue = curIntValue = targetValue;
-                    more = false;
-                }
-            } else {
-                if (curIntValue <= targetValue) {
-                    curValue = curIntValue = targetValue;
-                    more = false;
-                }
-            }
-            if (mSpew) Slog.d(TAG, "Animating curIntValue=" + curIntValue + ": " + mask);
-            setLightBrightness(mask, curIntValue);
-            finishAnimationLocked(more, curIntValue);
-            return more;
-        }
-
-        void jumpToTargetLocked() {
-            if (mSpew) Slog.d(TAG, "jumpToTargetLocked targetValue=" + targetValue + ": " + mask);
-            setLightBrightness(mask, targetValue);
-            final int tv = targetValue;
-            curValue = tv;
-            targetValue = -1;
-            finishAnimationLocked(false, tv);
-        }
-
-        private void finishAnimationLocked(boolean more, int curIntValue) {
-            animating = more;
-            if (!more) {
-                if (mask == SCREEN_BRIGHT_BIT && curIntValue == Power.BRIGHTNESS_OFF) {
-                    screenOffFinishedAnimatingLocked(mScreenOffReason);
-                }
-            }
-        }
-
-        public void run() {
-            synchronized (mLocks) {
-                // we're turning off
-                final boolean turningOff = animating && targetValue == Power.BRIGHTNESS_OFF;
-                if (mAnimateScreenLights || !turningOff) {
-                    long now = SystemClock.uptimeMillis();
-                    boolean more = mScreenBrightness.stepLocked();
-                    if (more) {
-                        mScreenOffHandler.postAtTime(this, now+(1000/60));
-                    }
-                } else {
-                    if (!mHeadless) {
-                        // It's pretty scary to hold mLocks for this long, and we should
-                        // redesign this, but it works for now.
-                        nativeStartSurfaceFlingerAnimation(
-                                mScreenOffReason == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR
-                                ? 0 : mAnimationSetting);
-                    }
-                    mScreenBrightness.jumpToTargetLocked();
-                }
-            }
-        }
+    private void setLightBrightness(int mask, int value) {
+        mScreenBrightnessAnimator.animateTo(value, mask, 0);
     }
 
     private int getPreferredBrightness() {
@@ -2326,7 +2334,8 @@
     }
 
     private boolean isScreenTurningOffLocked() {
-        return (mScreenBrightness.animating && mScreenBrightness.targetValue == 0);
+        return (mScreenBrightnessAnimator.isAnimating()
+                && mScreenBrightnessAnimator.endValue == Power.BRIGHTNESS_OFF);
     }
 
     private boolean shouldLog(long time) {
@@ -2347,7 +2356,7 @@
     private void forceUserActivityLocked() {
         if (isScreenTurningOffLocked()) {
             // cancel animation so userActivity will succeed
-            mScreenBrightness.animating = false;
+            mScreenBrightnessAnimator.cancelAnimation();
         }
         boolean savedActivityAllowed = mUserActivityAllowed;
         mUserActivityAllowed = true;
@@ -2526,6 +2535,8 @@
         }
     };
 
+    private boolean mInitialAnimation; // used to prevent lightsensor changes while turning on
+
     private void dockStateChanged(int state) {
         synchronized (mLocks) {
             mIsDocked = (state != Intent.EXTRA_DOCK_STATE_UNDOCKED);
@@ -2587,10 +2598,11 @@
                 }
 
                 if (mAutoBrightessEnabled && mScreenBrightnessOverride < 0) {
-                    if (!mSkippedScreenOn) {
-                        mScreenBrightness.setTargetLocked(lcdValue,
-                                immediate ? IMMEDIATE_ANIM_STEPS : AUTOBRIGHTNESS_ANIM_STEPS,
-                                INITIAL_SCREEN_BRIGHTNESS, (int)mScreenBrightness.curValue);
+                    if (!mSkippedScreenOn && !mInitialAnimation) {
+                        int steps = immediate ? IMMEDIATE_ANIM_STEPS : AUTOBRIGHTNESS_ANIM_STEPS;
+                        mScreenBrightnessAnimator.cancelAnimation();
+                        mScreenBrightnessAnimator.animateTo(lcdValue,
+                                SCREEN_BRIGHT_BIT, steps * NOMINAL_FRAME_TIME_MS);
                     }
                 }
                 if (mButtonBrightnessOverride < 0) {
@@ -2642,7 +2654,7 @@
                 synchronized (this) {
                     ShutdownThread.reboot(mContext, finalReason, false);
                 }
-                
+
             }
         };
         // ShutdownThread must run on a looper capable of displaying the UI.
@@ -2996,9 +3008,7 @@
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
-
-            mScreenBrightness.targetValue = brightness;
-            mScreenBrightness.jumpToTargetLocked();
+            mScreenBrightnessAnimator.animateTo(brightness, SCREEN_BRIGHT_BIT, 0);
         }
     }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c9b5997..423dad6 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -402,6 +402,14 @@
                 reportWtf("starting ThrottleService", e);
             }
 
+            try {
+                Slog.i(TAG, "UpdateLock Service");
+                ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
+                        new UpdateLockService(context));
+            } catch (Throwable e) {
+                reportWtf("starting UpdateLockService", e);
+            }
+
             if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
                 try {
                     /*
diff --git a/services/java/com/android/server/UpdateLockService.java b/services/java/com/android/server/UpdateLockService.java
new file mode 100644
index 0000000..1ffd196
--- /dev/null
+++ b/services/java/com/android/server/UpdateLockService.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IUpdateLock;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.TokenWatcher;
+import android.os.UpdateLock;
+import android.util.Slog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public class UpdateLockService extends IUpdateLock.Stub {
+    static final boolean DEBUG = false;
+    static final String TAG = "UpdateLockService";
+
+    // signatureOrSystem required to use update locks
+    static final String PERMISSION = "android.permission.UPDATE_LOCK";
+
+    Context mContext;
+    LockWatcher mLocks;
+
+    class LockWatcher extends TokenWatcher {
+        LockWatcher(Handler h, String tag) {
+            super(h, tag);
+        }
+
+        public void acquired() {
+            if (DEBUG) {
+                Slog.d(TAG, "first acquire; broadcasting convenient=false");
+            }
+            sendLockChangedBroadcast(false);
+        }
+        public void released() {
+            if (DEBUG) {
+                Slog.d(TAG, "last release; broadcasting convenient=true");
+            }
+            sendLockChangedBroadcast(true);
+        }
+    }
+
+    UpdateLockService(Context context) {
+        mContext = context;
+        mLocks = new LockWatcher(new Handler(), "UpdateLocks");
+
+        // Consider just-booting to be a reasonable time to allow
+        // interruptions for update installation etc.
+        sendLockChangedBroadcast(true);
+    }
+
+    void sendLockChangedBroadcast(boolean state) {
+        // Safe early during boot because this broadcast only goes to registered receivers.
+        long oldIdent = Binder.clearCallingIdentity();
+        try {
+            Intent intent = new Intent(UpdateLock.UPDATE_LOCK_CHANGED)
+                    .putExtra(UpdateLock.NOW_IS_CONVENIENT, state)
+                    .putExtra(UpdateLock.TIMESTAMP, System.currentTimeMillis())
+                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            mContext.sendStickyBroadcast(intent);
+        } finally {
+            Binder.restoreCallingIdentity(oldIdent);
+        }
+    }
+
+    @Override
+    public void acquireUpdateLock(IBinder token, String tag) throws RemoteException {
+        if (DEBUG) {
+            Slog.d(TAG, "acquire(" + token + ") by " + makeTag(tag));
+        }
+
+        mContext.enforceCallingOrSelfPermission(PERMISSION, "acquireUpdateLock");
+        mLocks.acquire(token, makeTag(tag));
+    }
+
+    @Override
+    public void releaseUpdateLock(IBinder token) throws RemoteException {
+        if (DEBUG) {
+            Slog.d(TAG, "release(" + token + ')');
+        }
+
+        mContext.enforceCallingOrSelfPermission(PERMISSION, "releaseUpdateLock");
+        mLocks.release(token);
+    };
+
+    private String makeTag(String tag) {
+        return "{tag=" + tag
+                + " uid=" + Binder.getCallingUid()
+                + " pid=" + Binder.getCallingPid() + '}';
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump update lock service from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+
+        mLocks.dump(pw);
+    }
+}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 5208785..6b4c895 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -263,47 +263,42 @@
                     ac.connect(mContext, this, msg.replyTo);
                     break;
                 }
-                case WifiManager.CMD_ENABLE_TRAFFIC_STATS_POLL: {
+                case WifiManager.ENABLE_TRAFFIC_STATS_POLL: {
                     mEnableTrafficStatsPoll = (msg.arg1 == 1);
                     mTrafficStatsPollToken++;
                     if (mEnableTrafficStatsPoll) {
                         notifyOnDataActivity();
-                        sendMessageDelayed(Message.obtain(this, WifiManager.CMD_TRAFFIC_STATS_POLL,
+                        sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL,
                                 mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
                     }
                     break;
                 }
-                case WifiManager.CMD_TRAFFIC_STATS_POLL: {
+                case WifiManager.TRAFFIC_STATS_POLL: {
                     if (msg.arg1 == mTrafficStatsPollToken) {
                         notifyOnDataActivity();
-                        sendMessageDelayed(Message.obtain(this, WifiManager.CMD_TRAFFIC_STATS_POLL,
+                        sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL,
                                 mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
                     }
                     break;
                 }
-                case WifiManager.CMD_CONNECT_NETWORK: {
-                    if (msg.obj != null) {
-                        mWifiStateMachine.connectNetwork((WifiConfiguration)msg.obj);
-                    } else {
-                        mWifiStateMachine.connectNetwork(msg.arg1);
-                    }
+                case WifiManager.CONNECT_NETWORK: {
+                    mWifiStateMachine.sendMessage(Message.obtain(msg));
                     break;
                 }
-                case WifiManager.CMD_SAVE_NETWORK: {
-                    mWifiStateMachine.saveNetwork((WifiConfiguration)msg.obj);
+                case WifiManager.SAVE_NETWORK: {
+                    mWifiStateMachine.sendMessage(Message.obtain(msg));
                     break;
                 }
-                case WifiManager.CMD_FORGET_NETWORK: {
-                    mWifiStateMachine.forgetNetwork(msg.arg1);
+                case WifiManager.FORGET_NETWORK: {
+                    mWifiStateMachine.sendMessage(Message.obtain(msg));
                     break;
                 }
-                case WifiManager.CMD_START_WPS: {
-                    //replyTo has the original source
-                    mWifiStateMachine.startWps(msg.replyTo, (WpsInfo)msg.obj);
+                case WifiManager.START_WPS: {
+                    mWifiStateMachine.sendMessage(Message.obtain(msg));
                     break;
                 }
-                case WifiManager.CMD_DISABLE_NETWORK: {
-                    mWifiStateMachine.disableNetwork(msg.replyTo, msg.arg1, msg.arg2);
+                case WifiManager.DISABLE_NETWORK: {
+                    mWifiStateMachine.sendMessage(Message.obtain(msg));
                     break;
                 }
                 default: {
@@ -914,7 +909,7 @@
      * Get a reference to handler. This is used by a client to establish
      * an AsyncChannel communication with WifiService
      */
-    public Messenger getMessenger() {
+    public Messenger getWifiServiceMessenger() {
         /* Enforce the highest permissions
            TODO: when we consider exposing the asynchronous API, think about
                  how to provide both access and change permissions seperately
@@ -924,6 +919,13 @@
         return new Messenger(mAsyncServiceHandler);
     }
 
+    /** Get a reference to WifiStateMachine handler for AsyncChannel communication */
+    public Messenger getWifiStateMachineMessenger() {
+        enforceAccessPermission();
+        enforceChangePermission();
+        return mWifiStateMachine.getMessenger();
+    }
+
     /**
      * Get the IP and proxy configuration file
      */
@@ -1587,10 +1589,10 @@
         Message msg;
         if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED && !mScreenOff) {
             msg = Message.obtain(mAsyncServiceHandler,
-                    WifiManager.CMD_ENABLE_TRAFFIC_STATS_POLL, 1, 0);
+                    WifiManager.ENABLE_TRAFFIC_STATS_POLL, 1, 0);
         } else {
             msg = Message.obtain(mAsyncServiceHandler,
-                    WifiManager.CMD_ENABLE_TRAFFIC_STATS_POLL, 0, 0);
+                    WifiManager.ENABLE_TRAFFIC_STATS_POLL, 0, 0);
         }
         msg.sendToTarget();
     }
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 9573fda..88a0ccb 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -1215,6 +1215,8 @@
                 return retValue;
             }
             protected boolean turnOffUpstreamMobileConnection() {
+                // ignore pending renewal requests
+                ++mCurrentConnectionSequence;
                 if (mMobileApnReserved != ConnectivityManager.TYPE_NONE) {
                     try {
                         mConnService.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
@@ -1304,6 +1306,14 @@
                 if (upType == ConnectivityManager.TYPE_MOBILE_DUN ||
                         upType == ConnectivityManager.TYPE_MOBILE_HIPRI) {
                     turnOnUpstreamMobileConnection(upType);
+                } else if (upType != ConnectivityManager.TYPE_NONE) {
+                    /* If we've found an active upstream connection that's not DUN/HIPRI
+                     * we should stop any outstanding DUN/HIPRI start requests.
+                     *
+                     * If we found NONE we don't want to do this as we want any previous
+                     * requests to keep trying to bring up something we can use.
+                     */
+                    turnOffUpstreamMobileConnection();
                 }
 
                 if (upType == ConnectivityManager.TYPE_NONE) {
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 5afc1f3..cf96ab2 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -2566,13 +2566,20 @@
                 result[0] = ret;
                 result[1] = Long.valueOf(nitzReceiveTime);
 
-                if (mNITZTimeRegistrant != null) {
+                boolean ignoreNitz = SystemProperties.getBoolean(
+                        TelephonyProperties.PROPERTY_IGNORE_NITZ, false);
 
-                    mNITZTimeRegistrant
-                        .notifyRegistrant(new AsyncResult (null, result, null));
+                if (ignoreNitz) {
+                    if (RILJ_LOGD) riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED");
                 } else {
-                    // in case NITZ time registrant isnt registered yet
-                    mLastNITZTimeInfo = result;
+                    if (mNITZTimeRegistrant != null) {
+
+                        mNITZTimeRegistrant
+                            .notifyRegistrant(new AsyncResult (null, result, null));
+                    } else {
+                        // in case NITZ time registrant isnt registered yet
+                        mLastNITZTimeInfo = result;
+                    }
                 }
             break;
 
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index abb4523..f95e081 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -182,4 +182,9 @@
      * in commercial configuration.
      */
     static final String PROPERTY_TEST_CSIM = "persist.radio.test-csim";
+
+    /**
+     * Ignore RIL_UNSOL_NITZ_TIME_RECEIVED completely, used for debugging/testing.
+     */
+    static final String PROPERTY_IGNORE_NITZ = "telephony.test.ignore.nitz";
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index a4fb1d8..148b139 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -59,9 +59,12 @@
 import android.util.Log;
 import android.util.TimeUtils;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
+import java.util.Collection;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.TimeZone;
 
 /**
@@ -112,6 +115,9 @@
     private boolean mGotCountryCode = false;
     private ContentResolver cr;
 
+    /** Boolean is true is setTimeFromNITZString was called */
+    private boolean mNitzUpdatedTime = false;
+
     String mSavedTimeZone;
     long mSavedTime;
     long mSavedAtTime;
@@ -698,6 +704,7 @@
                 newCellLoc.setStateInvalid();
                 setSignalStrengthDefaultValues();
                 mGotCountryCode = false;
+                mNitzUpdatedTime = false;
                 pollStateDone();
             break;
 
@@ -706,6 +713,7 @@
                 newCellLoc.setStateInvalid();
                 setSignalStrengthDefaultValues();
                 mGotCountryCode = false;
+                mNitzUpdatedTime = false;
                 pollStateDone();
             break;
 
@@ -826,6 +834,12 @@
 
         if (hasRegistered) {
             mNetworkAttachedRegistrants.notifyRegistrants();
+
+            if (DBG) {
+                log("pollStateDone: registering current mNitzUpdatedTime=" +
+                        mNitzUpdatedTime + " changing to false");
+            }
+            mNitzUpdatedTime = false;
         }
 
         if (hasChanged) {
@@ -840,28 +854,71 @@
             phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric);
 
             if (operatorNumeric == null) {
+                if (DBG) {
+                    log("pollStateDone: operatorNumeric is null:" +
+                            " clear PROPERTY_OPERATOR_ISO_COUNTRY");
+                }
                 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
                 mGotCountryCode = false;
+                mNitzUpdatedTime = false;
             } else {
                 String iso = "";
+                String mcc = operatorNumeric.substring(0, 3);
                 try{
-                    iso = MccTable.countryCodeForMcc(Integer.parseInt(
-                            operatorNumeric.substring(0,3)));
+                    iso = MccTable.countryCodeForMcc(Integer.parseInt(mcc));
                 } catch ( NumberFormatException ex){
-                    loge("countryCodeForMcc error" + ex);
+                    loge("pollStateDone: countryCodeForMcc error" + ex);
                 } catch ( StringIndexOutOfBoundsException ex) {
-                    loge("countryCodeForMcc error" + ex);
+                    loge("pollStateDone: countryCodeForMcc error" + ex);
+                }
+                if (DBG) {
+                    log("pollStateDone: operatorNumeric=" + operatorNumeric +
+                            " mcc=" + mcc + " iso=" + iso);
                 }
 
                 phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, iso);
                 mGotCountryCode = true;
 
+                TimeZone zone = null;
+
+                if (!mNitzUpdatedTime && !mcc.equals("000") && !TextUtils.isEmpty(iso) &&
+                        getAutoTimeZone()) {
+
+                    // Test both paths if ignore nitz is true
+                    boolean testOneUniqueOffsetPath = SystemProperties.getBoolean(
+                                TelephonyProperties.PROPERTY_IGNORE_NITZ, false) &&
+                                    ((SystemClock.uptimeMillis() & 1) == 0);
+
+                    ArrayList<TimeZone> uniqueZones = TimeUtils.getTimeZonesWithUniqueOffsets(iso);
+                    if ((uniqueZones.size() == 1) || testOneUniqueOffsetPath) {
+                        zone = uniqueZones.get(0);
+                        if (DBG) {
+                           log("pollStateDone: no nitz but one TZ for iso=" + iso +
+                                   " with zone.getID=" + zone.getID() +
+                                   " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath);
+                        }
+                        setAndBroadcastNetworkSetTimeZone(zone.getID());
+                    } else {
+                        if (DBG) {
+                            log("pollStateDone: there are " + uniqueZones.size() +
+                                " unique offsets for iso='" + iso +
+                                " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath +
+                                "', do nothing");
+                        }
+                    }
+                }
+
                 if (mNeedFixZone) {
-                    TimeZone zone = null;
                     // If the offset is (0, false) and the timezone property
                     // is set, use the timezone property rather than
                     // GMT.
                     String zoneName = SystemProperties.get(TIMEZONE_PROPERTY);
+                    if (DBG) {
+                        log("pollStateDone: mNeedFixZone==true zoneName='" + zoneName +
+                            "' mZoneOffset=" + mZoneOffset + " mZoneDst=" + mZoneDst +
+                            " iso='" + iso +
+                            "' iso-cc-idx=" + Arrays.binarySearch(GMT_COUNTRY_CODES, iso));
+                    }
                     if ((mZoneOffset == 0) && (mZoneDst == false) &&
                         (zoneName != null) && (zoneName.length() > 0) &&
                         (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)) {
@@ -876,22 +933,36 @@
                             // Adjust the saved NITZ time to account for tzOffset.
                             mSavedTime = mSavedTime - tzOffset;
                         }
+                        if (DBG) log("pollStateDone: using default TimeZone");
                     } else if (iso.equals("")){
                         // Country code not found.  This is likely a test network.
                         // Get a TimeZone based only on the NITZ parameters (best guess).
                         zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime);
+                        if (DBG) log("pollStateDone: using NITZ TimeZone");
                     } else {
-                        zone = TimeUtils.getTimeZone(mZoneOffset,
-                            mZoneDst, mZoneTime, iso);
+                        zone = TimeUtils.getTimeZone(mZoneOffset, mZoneDst, mZoneTime, iso);
+                        if (DBG) log("pollStateDone: using getTimeZone(off, dst, time, iso)");
                     }
 
                     mNeedFixZone = false;
 
                     if (zone != null) {
+                        log("pollStateDone: zone != null zone.getID=" + zone.getID());
                         if (getAutoTimeZone()) {
                             setAndBroadcastNetworkSetTimeZone(zone.getID());
                         }
                         saveNitzTimeZone(zone.getID());
+                    } else {
+                        log("pollStateDone: zone == null");
+                    }
+                } else {
+                    if (DBG) {
+                        String zoneName = SystemProperties.get(TIMEZONE_PROPERTY);
+                        zone = TimeZone.getDefault();
+                        log("pollStateDone: mNeedFixZone==false zoneName='" + zoneName +
+                                "' mZoneOffset=" + mZoneOffset + " mZoneDst=" + mZoneDst +
+                                " iso='" + iso +
+                                "' iso-cc-idx=" + Arrays.binarySearch(GMT_COUNTRY_CODES, iso));
                     }
                 }
             }
@@ -1440,6 +1511,7 @@
                     long end = SystemClock.elapsedRealtime();
                     log("NITZ: end=" + end + " dur=" + (end - start));
                 }
+                mNitzUpdatedTime = true;
             } finally {
                 mWakeLock.release();
             }
@@ -1489,6 +1561,10 @@
         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         intent.putExtra("time-zone", zoneId);
         phone.getContext().sendStickyBroadcast(intent);
+        if (DBG) {
+            log("setAndBroadcastNetworkSetTimeZone: call alarm.setTimeZone and broadcast zoneId=" +
+                zoneId);
+        }
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index 5b0f251..1fb99e3 100755
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -167,8 +167,10 @@
         "405841", "405842", "405843", "405844", "405845", "405846", "405847", "405848",
         "405849", "405850", "405851", "405852", "405853", "405875", "405876", "405877",
         "405878", "405879", "405880", "405881", "405882", "405883", "405884", "405885",
-        "405886", "405908", "405909", "405910", "405911", "405925", "405926", "405927",
-        "405928", "405929", "405932"
+        "405886", "405908", "405909", "405910", "405911", "405912", "405913", "405914",
+        "405915", "405916", "405917", "405918", "405919", "405920", "405921", "405922",
+        "405923", "405924", "405925", "405926", "405927", "405928", "405929", "405930",
+        "405931", "405932"
     };
 
     // ***** Constructor
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java
index b798ee7..9849e3c 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java
@@ -21,18 +21,21 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Path;
+import android.graphics.PathMeasure;
 import android.os.Bundle;
 import android.view.View;
 
 @SuppressWarnings({"UnusedDeclaration"})
 public class TextOnPathActivity extends Activity {
     private Path mPath;
+    private Path mStraightPath;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         mPath = makePath();
+        mStraightPath = makeStraightPath();
 
         final TextOnPathView view = new TextOnPathView(this);
         setContentView(view);
@@ -51,11 +54,28 @@
         path.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f);
     }
 
+    private Path makeStraightPath() {
+        Path path = new Path();
+        buildStraightPath(path);
+        return path;
+    }
+
+    private void buildStraightPath(Path path) {
+        path.moveTo(0.0f, 0.0f);
+        path.lineTo(400.0f, 0.0f);
+    }
+
     public class TextOnPathView extends View {
         private static final String TEST_STRING = "Hello OpenGL renderer, text on path! ";
 
         private final Paint mPaint;
+        private final Paint mPathPaint;
         private final String mText;
+        private final PathMeasure mMeasure;
+        private final float mLength;
+        private final float[] mLines;
+        private final float[] mPos;
+        private final float[] mTan;
 
         public TextOnPathView(Context c) {
             super(c);
@@ -64,11 +84,23 @@
             mPaint.setAntiAlias(true);
             mPaint.setColor(0xff000000);
 
+            mPathPaint = new Paint();
+            mPathPaint.setAntiAlias(true);
+            mPathPaint.setStyle(Paint.Style.STROKE);
+            mPathPaint.setColor(0xff000099);
+
             StringBuilder builder = new StringBuilder(TEST_STRING.length() * 2);
             for (int i = 0; i < 2; i++) {
                 builder.append(TEST_STRING);
             }
             mText = builder.toString();
+
+            mMeasure = new PathMeasure(mPath, false);
+            mLength = mMeasure.getLength();
+            
+            mLines = new float[100 * 4];
+            mPos = new float[2];
+            mTan = new float[2];
         }
 
         @Override
@@ -81,19 +113,40 @@
             canvas.translate(400.0f, 350.0f);
             mPaint.setTextAlign(Paint.Align.LEFT);
             canvas.drawTextOnPath(mText + mText, mPath, 0.0f, 0.0f, mPaint);
+            canvas.drawPath(mPath, mPathPaint);
+            
+            for (int i = 0; i < 100; i++) {
+                mMeasure.getPosTan(i * mLength / 100.0f, mPos, mTan);
+                mLines[i * 4    ] = mPos[0];
+                mLines[i * 4 + 1] = mPos[1];
+                mLines[i * 4 + 2] = mPos[0] + mTan[1] * 15;
+                mLines[i * 4 + 3] = mPos[1] - mTan[0] * 15;
+            }
+            canvas.drawLines(mLines, mPathPaint);
+            
+            canvas.translate(200.0f, 0.0f);
+            canvas.drawTextOnPath(mText + mText, mStraightPath, 0.0f, 0.0f, mPaint);
+            canvas.drawPath(mStraightPath, mPathPaint);
+
             canvas.restore();
 
             canvas.save();
             canvas.translate(150.0f, 60.0f);
-            canvas.drawTextOnPath(mText, mPath, 0.0f, 0.0f, mPaint);
+            canvas.drawTextOnPath(mText, mPath, 0.0f, 10.0f, mPaint);
+            mMeasure.getPosTan(5.0f, mPos, mTan);
+            canvas.drawLine(mPos[0], mPos[1], mPos[0] + mTan[1] * 10, mPos[1] - mTan[0] * 10,
+                    mPathPaint);
+            canvas.drawPath(mPath, mPathPaint);
 
             canvas.translate(250.0f, 0.0f);
             mPaint.setTextAlign(Paint.Align.CENTER);
             canvas.drawTextOnPath(mText, mPath, 0.0f, 0.0f, mPaint);
+            canvas.drawPath(mPath, mPathPaint);
 
             canvas.translate(250.0f, 0.0f);
             mPaint.setTextAlign(Paint.Align.RIGHT);
             canvas.drawTextOnPath(mText, mPath, 0.0f, 0.0f, mPaint);
+            canvas.drawPath(mPath, mPathPaint);
             canvas.restore();
         }
     }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 61dfebf..6b08074 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -101,7 +101,9 @@
 
     void clearBlacklist();
 
-    Messenger getMessenger();
+    Messenger getWifiServiceMessenger();
+
+    Messenger getWifiStateMachineMessenger();
 
     String getConfigFile();
 }
diff --git a/wifi/java/android/net/wifi/SupplicantState.java b/wifi/java/android/net/wifi/SupplicantState.java
index 509b02c..4a2037d 100644
--- a/wifi/java/android/net/wifi/SupplicantState.java
+++ b/wifi/java/android/net/wifi/SupplicantState.java
@@ -171,8 +171,8 @@
     }
 
 
-    /* Supplicant associating or authenticating is considered a handshake state */
-    static boolean isHandshakeState(SupplicantState state) {
+    /** Supplicant associating or authenticating is considered a handshake state {@hide} */
+    public static boolean isHandshakeState(SupplicantState state) {
         switch(state) {
             case AUTHENTICATING:
             case ASSOCIATING:
diff --git a/wifi/java/android/net/wifi/SupplicantStateTracker.java b/wifi/java/android/net/wifi/SupplicantStateTracker.java
index 104a02d..6aeac5f 100644
--- a/wifi/java/android/net/wifi/SupplicantStateTracker.java
+++ b/wifi/java/android/net/wifi/SupplicantStateTracker.java
@@ -175,7 +175,7 @@
                 case WifiStateMachine.CMD_RESET_SUPPLICANT_STATE:
                     transitionTo(mUninitializedState);
                     break;
-                case WifiStateMachine.CMD_CONNECT_NETWORK:
+                case WifiManager.CONNECT_NETWORK:
                     mNetworksDisabledDuringConnect = true;
                     break;
                 default:
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 5dffa60..8305714 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -261,6 +261,7 @@
      * Add/update the specified configuration and save config
      *
      * @param config WifiConfiguration to be saved
+     * @return network update result
      */
     NetworkUpdateResult saveNetwork(WifiConfiguration config) {
         boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
@@ -298,8 +299,9 @@
      * Forget the specified network and save config
      *
      * @param netId network to forget
+     * @return {@code true} if it succeeds, {@code false} otherwise
      */
-    void forgetNetwork(int netId) {
+    boolean forgetNetwork(int netId) {
         if (mWifiNative.removeNetwork(netId)) {
             mWifiNative.saveConfig();
             WifiConfiguration config = mConfiguredNetworks.get(netId);
@@ -309,8 +311,10 @@
             }
             writeIpAndProxyConfigurations();
             sendConfiguredNetworksChangedBroadcast();
+            return true;
         } else {
             loge("Failed to remove network " + netId);
+            return false;
         }
     }
 
@@ -321,6 +325,7 @@
      * state machine
      *
      * @param config wifi configuration to add/update
+     * @return network Id
      */
     int addOrUpdateNetwork(WifiConfiguration config) {
         NetworkUpdateResult result = addOrUpdateNetworkNative(config);
@@ -335,6 +340,7 @@
      * state machine for network removal
      *
      * @param netId network to be removed
+     * @return {@code true} if it succeeds, {@code false} otherwise
      */
     boolean removeNetwork(int netId) {
         boolean ret = mWifiNative.removeNetwork(netId);
@@ -356,6 +362,7 @@
      * state machine for connecting to a network
      *
      * @param netId network to be removed
+     * @return {@code true} if it succeeds, {@code false} otherwise
      */
     boolean enableNetwork(int netId, boolean disableOthers) {
         boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
@@ -378,6 +385,7 @@
     /**
      * Disable a network. Note that there is no saveConfig operation.
      * @param netId network to be disabled
+     * @return {@code true} if it succeeds, {@code false} otherwise
      */
     boolean disableNetwork(int netId) {
         return disableNetwork(netId, WifiConfiguration.DISABLED_UNKNOWN_REASON);
@@ -387,6 +395,7 @@
      * Disable a network. Note that there is no saveConfig operation.
      * @param netId network to be disabled
      * @param reason reason code network was disabled
+     * @return {@code true} if it succeeds, {@code false} otherwise
      */
     boolean disableNetwork(int netId, int reason) {
         boolean ret = mWifiNative.disableNetwork(netId);
@@ -402,6 +411,7 @@
 
     /**
      * Save the configured networks in supplicant to disk
+     * @return {@code true} if it succeeds, {@code false} otherwise
      */
     boolean saveConfig() {
         return mWifiNative.saveConfig();
@@ -410,6 +420,8 @@
     /**
      * Start WPS pin method configuration with pin obtained
      * from the access point
+     * @param config WPS configuration
+     * @return Wps result containing status and pin
      */
     WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
         WpsResult result = new WpsResult();
@@ -445,6 +457,8 @@
 
     /**
      * Start WPS push button configuration
+     * @param config WPS configuration
+     * @return WpsResult indicating status and pin
      */
     WpsResult startWpsPbc(WpsInfo config) {
         WpsResult result = new WpsResult();
@@ -461,6 +475,7 @@
 
     /**
      * Fetch the link properties for a given network id
+     * @return LinkProperties for the given network id
      */
     LinkProperties getLinkProperties(int netId) {
         WifiConfiguration config = mConfiguredNetworks.get(netId);
@@ -474,6 +489,7 @@
      *       right now until NetworkUtils is fixed. When we do
      *       that, we should remove handling DhcpInfo and move
      *       to using LinkProperties
+     * @return DhcpInfoInternal for the given network id
      */
     DhcpInfoInternal getIpConfiguration(int netId) {
         DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
@@ -516,6 +532,7 @@
 
     /**
      * clear IP configuration for a given network id
+     * @param network id
      */
     void clearIpConfiguration(int netId) {
         WifiConfiguration config = mConfiguredNetworks.get(netId);
@@ -530,6 +547,8 @@
 
     /**
      * Fetch the proxy properties for a given network id
+     * @param network id
+     * @return ProxyProperties for the network id
      */
     ProxyProperties getProxyProperties(int netId) {
         LinkProperties linkProperties = getLinkProperties(netId);
@@ -541,6 +560,8 @@
 
     /**
      * Return if the specified network is using static IP
+     * @param network id
+     * @return {@code true} if using static ip for netId
      */
     boolean isUsingStaticIp(int netId) {
         WifiConfiguration config = mConfiguredNetworks.get(netId);
@@ -599,16 +620,6 @@
         sendConfiguredNetworksChangedBroadcast();
     }
 
-    void updateIpAndProxyFromWpsConfig(int netId, WpsInfo wpsConfig) {
-        WifiConfiguration config = mConfiguredNetworks.get(netId);
-        if (config != null) {
-            config.ipAssignment = wpsConfig.ipAssignment;
-            config.proxySettings = wpsConfig.proxySettings;
-            config.linkProperties = wpsConfig.linkProperties;
-            writeIpAndProxyConfigurations();
-        }
-    }
-
     /* Mark all networks except specified netId as disabled */
     private void markAllNetworksDisabledExcept(int netId) {
         for(WifiConfiguration config : mConfiguredNetworks.values()) {
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 1a0e0da..bdee12a 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -23,11 +23,15 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.os.RemoteException;
 import android.os.WorkSource;
 import android.os.Messenger;
+import android.util.SparseArray;
 
 import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
 
 import java.util.List;
 
@@ -289,24 +293,6 @@
     public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
 
     /**
-     * Broadcast intent action for reporting errors
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ERROR_ACTION = "android.net.wifi.ERROR";
-    /**
-     * The type of error being reported
-     * @hide
-     */
-    public static final String EXTRA_ERROR_CODE = "errorCode";
-
-    /**
-     * Valid error codes
-     * @hide
-     */
-    public static final int WPS_OVERLAP_ERROR = 1;
-
-    /**
      * Broadcast intent action indicating that the configured networks changed.
      * This can be as a result of adding/updating/deleting a network
      * @hide
@@ -466,9 +452,6 @@
     /* Number of currently active WifiLocks and MulticastLocks */
     private int mActiveLockCount;
 
-    /* For communication with WifiService */
-    private AsyncChannel mAsyncChannel = new AsyncChannel();
-
     /**
      * Create a new WifiManager instance.
      * Applications will almost always want to use
@@ -622,17 +605,6 @@
     }
 
     /**
-     * Disable a configured network asynchronously.  This call is for abnormal network
-     * events, and the user may be notified of network change, if they recently attempted
-     * to connect to the specified network.
-     * @param netId the ID of the network as returned by {@link #addNetwork}.
-     * @hide
-     */
-    public void disableNetwork(int netId, int reason) {
-        mAsyncChannel.sendMessage(CMD_DISABLE_NETWORK, netId, reason);
-    }
-
-    /**
      * Disassociate from the currently active access point. This may result
      * in the asynchronous delivery of state change events.
      * @return {@code true} if the operation succeeded
@@ -1067,37 +1039,237 @@
 
     /* TODO: deprecate synchronous API and open up the following API */
 
+    private static final int BASE = Protocol.BASE_WIFI_MANAGER;
+
     /* Commands to WifiService */
     /** @hide */
-    public static final int CMD_CONNECT_NETWORK             = 1;
+    public static final int CONNECT_NETWORK                 = BASE + 1;
     /** @hide */
-    public static final int CMD_FORGET_NETWORK              = 2;
+    public static final int CONNECT_NETWORK_FAILED          = BASE + 2;
     /** @hide */
-    public static final int CMD_SAVE_NETWORK                = 3;
-    /** @hide */
-    public static final int CMD_START_WPS                   = 4;
-    /** @hide */
-    public static final int CMD_DISABLE_NETWORK             = 5;
+    public static final int CONNECT_NETWORK_SUCCEEDED       = BASE + 3;
 
-    /* Events from WifiService */
     /** @hide */
-    public static final int CMD_WPS_COMPLETED               = 11;
+    public static final int FORGET_NETWORK                  = BASE + 4;
+    /** @hide */
+    public static final int FORGET_NETWORK_FAILED           = BASE + 5;
+    /** @hide */
+    public static final int FORGET_NETWORK_SUCCEEDED        = BASE + 6;
+
+    /** @hide */
+    public static final int SAVE_NETWORK                    = BASE + 7;
+    /** @hide */
+    public static final int SAVE_NETWORK_FAILED             = BASE + 8;
+    /** @hide */
+    public static final int SAVE_NETWORK_SUCCEEDED          = BASE + 9;
+
+    /** @hide */
+    public static final int START_WPS                       = BASE + 10;
+    /** @hide */
+    public static final int START_WPS_SUCCEEDED             = BASE + 11;
+    /** @hide */
+    public static final int WPS_FAILED                      = BASE + 12;
+   /** @hide */
+    public static final int WPS_COMPLETED                   = BASE + 13;
+
+    /** @hide */
+    public static final int DISABLE_NETWORK                 = BASE + 14;
+    /** @hide */
+    public static final int DISABLE_NETWORK_FAILED          = BASE + 15;
+    /** @hide */
+    public static final int DISABLE_NETWORK_SUCCEEDED       = BASE + 16;
 
     /* For system use only */
     /** @hide */
-    public static final int CMD_ENABLE_TRAFFIC_STATS_POLL   = 21;
+    public static final int ENABLE_TRAFFIC_STATS_POLL       = BASE + 21;
     /** @hide */
-    public static final int CMD_TRAFFIC_STATS_POLL          = 22;
+    public static final int TRAFFIC_STATS_POLL              = BASE + 22;
+
 
     /**
-     * Initiate an asynchronous channel connection setup
-     * @param srcContext is the context of the source
-     * @param srcHandler is the handler on which the source receives messages
+     * Passed with {@link ActionListener#onFailure}.
+     * Indicates that the operation failed due to an internal error.
      * @hide
      */
-     public void asyncConnect(Context srcContext, Handler srcHandler) {
-        mAsyncChannel.connect(srcContext, srcHandler, getMessenger());
-     }
+    public static final int ERROR               = 0;
+
+    /**
+     * Passed with {@link ActionListener#onFailure}.
+     * Indicates that the operation is already in progress
+     * @hide
+     */
+    public static final int IN_PROGRESS         = 1;
+
+    /**
+     * Passed with {@link ActionListener#onFailure}.
+     * Indicates that the operation failed because the framework is busy and
+     * unable to service the request
+     * @hide
+     */
+    public static final int BUSY                = 2;
+
+    /* WPS specific errors */
+    /** WPS overlap detected {@hide} */
+    public static final int WPS_OVERLAP_ERROR   = 3;
+
+    /** Interface for callback invocation when framework channel is lost {@hide} */
+    public interface ChannelListener {
+        /**
+         * The channel to the framework has been disconnected.
+         * Application could try re-initializing using {@link #initialize}
+         */
+        public void onChannelDisconnected();
+    }
+
+    /** Interface for callback invocation on an application action {@hide} */
+    public interface ActionListener {
+        /** The operation succeeded */
+        public void onSuccess();
+        /**
+         * The operation failed
+         * @param reason The reason for failure could be one of
+         * {@link #ERROR}, {@link #IN_PROGRESS} or {@link #BUSY}
+         */
+        public void onFailure(int reason);
+    }
+
+    /** Interface for callback invocation on a start WPS action {@hide} */
+    public interface WpsListener {
+        /** WPS start succeeded */
+        public void onStartSuccess(String pin);
+
+        /** WPS operation completed succesfully */
+        public void onCompletion();
+
+        /**
+         * WPS operation failed
+         * @param reason The reason for failure could be one of
+         * {@link #IN_PROGRESS}, {@link #WPS_OVERLAP_ERROR},{@link #ERROR} or {@link #BUSY}
+         */
+        public void onFailure(int reason);
+    }
+
+    /**
+     * A channel that connects the application to the Wifi framework.
+     * Most operations require a Channel as an argument. An instance of Channel is obtained
+     * by doing a call on {@link #initialize}
+     * @hide
+     */
+    public static class Channel {
+        Channel(Looper looper, ChannelListener l) {
+            mAsyncChannel = new AsyncChannel();
+            mHandler = new WifiHandler(looper);
+            mChannelListener = l;
+        }
+        private ChannelListener mChannelListener;
+        private SparseArray<Object> mListenerMap = new SparseArray<Object>();
+        private Object mListenerMapLock = new Object();
+        private int mListenerKey = 0;
+
+        AsyncChannel mAsyncChannel;
+        WifiHandler mHandler;
+        class WifiHandler extends Handler {
+            WifiHandler(Looper looper) {
+                super(looper);
+            }
+
+            @Override
+            public void handleMessage(Message message) {
+                Object listener = removeListener(message.arg2);
+                switch (message.what) {
+                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+                        if (mChannelListener != null) {
+                            mChannelListener.onChannelDisconnected();
+                            mChannelListener = null;
+                        }
+                        break;
+                        /* ActionListeners grouped together */
+                    case WifiManager.CONNECT_NETWORK_FAILED:
+                    case WifiManager.FORGET_NETWORK_FAILED:
+                    case WifiManager.SAVE_NETWORK_FAILED:
+                    case WifiManager.DISABLE_NETWORK_FAILED:
+                        if (listener != null) {
+                            ((ActionListener) listener).onFailure(message.arg1);
+                        }
+                        break;
+                        /* ActionListeners grouped together */
+                    case WifiManager.CONNECT_NETWORK_SUCCEEDED:
+                    case WifiManager.FORGET_NETWORK_SUCCEEDED:
+                    case WifiManager.SAVE_NETWORK_SUCCEEDED:
+                    case WifiManager.DISABLE_NETWORK_SUCCEEDED:
+                        if (listener != null) {
+                            ((ActionListener) listener).onSuccess();
+                        }
+                        break;
+                    case WifiManager.START_WPS_SUCCEEDED:
+                        if (listener != null) {
+                            WpsResult result = (WpsResult) message.obj;
+                            ((WpsListener) listener).onStartSuccess(result.pin);
+                            //Listener needs to stay until completion or failure
+                            synchronized(mListenerMapLock) {
+                                mListenerMap.put(message.arg2, listener);
+                            }
+                        }
+                        break;
+                    case WifiManager.WPS_COMPLETED:
+                        if (listener != null) {
+                            ((WpsListener) listener).onCompletion();
+                        }
+                        break;
+                    case WifiManager.WPS_FAILED:
+                        if (listener != null) {
+                            ((WpsListener) listener).onFailure(message.arg1);
+                        }
+                        break;
+                    default:
+                        //ignore
+                        break;
+                }
+            }
+        }
+
+        int putListener(Object listener) {
+            if (listener == null) return 0;
+            int key;
+            synchronized (mListenerMapLock) {
+                key = mListenerKey++;
+                mListenerMap.put(key, listener);
+            }
+            return key;
+        }
+
+        Object removeListener(int key) {
+            synchronized (mListenerMapLock) {
+                Object listener = mListenerMap.get(key);
+                mListenerMap.remove(key);
+                return listener;
+            }
+        }
+    }
+
+    /**
+     * Registers the application with the Wi-Fi framework. This function
+     * must be the first to be called before any Wi-Fi operations are performed.
+     *
+     * @param srcContext is the context of the source
+     * @param srcLooper is the Looper on which the callbacks are receivied
+     * @param listener for callback at loss of framework communication. Can be null.
+     * @return Channel instance that is necessary for performing any further Wi-Fi operations.
+     *         A null is returned upon failure to initialize.
+     * @hide
+     */
+    public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
+        Messenger messenger = getWifiServiceMessenger();
+        if (messenger == null) return null;
+
+        Channel c = new Channel(srcLooper, listener);
+        if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger)
+                == AsyncChannel.STATUS_SUCCESSFUL) {
+            return c;
+        } else {
+            return null;
+        }
+    }
 
     /**
      * Connect to a network with the given configuration. The network also
@@ -1107,15 +1279,17 @@
      * sequence of addNetwork(), enableNetwork(), saveConfiguration() and
      * reconnect()
      *
+     * @param c is the channel created at {@link #initialize}
      * @param config the set of variables that describe the configuration,
      *            contained in a {@link WifiConfiguration} object.
+     * @param listener for callbacks on success or failure. Can be null.
      * @hide
      */
-    public void connectNetwork(WifiConfiguration config) {
-        if (config == null) {
-            return;
-        }
-        mAsyncChannel.sendMessage(CMD_CONNECT_NETWORK, config);
+    public void connect(Channel c, WifiConfiguration config, ActionListener listener) {
+        if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+        if (config == null) throw new IllegalArgumentException("config cannot be null");
+
+        c.mAsyncChannel.sendMessage(CONNECT_NETWORK, 0, c.putListener(listener), config);
     }
 
     /**
@@ -1124,15 +1298,17 @@
      * This function is used instead of a enableNetwork(), saveConfiguration() and
      * reconnect()
      *
+     * @param c is the channel created at {@link #initialize}
      * @param networkId the network id identifiying the network in the
      *                supplicant configuration list
+     * @param listener for callbacks on success or failure. Can be null.
      * @hide
      */
-    public void connectNetwork(int networkId) {
-        if (networkId < 0) {
-            return;
-        }
-        mAsyncChannel.sendMessage(CMD_CONNECT_NETWORK, networkId);
+    public void connect(Channel c, int networkId, ActionListener listener) {
+        if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+        if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
+
+        c.mAsyncChannel.sendMessage(CONNECT_NETWORK, networkId, c.putListener(listener));
     }
 
     /**
@@ -1146,16 +1322,17 @@
      * For an existing network, it accomplishes the task of updateNetwork()
      * and saveConfiguration()
      *
+     * @param c is the channel created at {@link #initialize}
      * @param config the set of variables that describe the configuration,
      *            contained in a {@link WifiConfiguration} object.
+     * @param listener for callbacks on success or failure. Can be null.
      * @hide
      */
-    public void saveNetwork(WifiConfiguration config) {
-        if (config == null) {
-            return;
-        }
+    public void save(Channel c, WifiConfiguration config, ActionListener listener) {
+        if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+        if (config == null) throw new IllegalArgumentException("config cannot be null");
 
-        mAsyncChannel.sendMessage(CMD_SAVE_NETWORK, config);
+        c.mAsyncChannel.sendMessage(SAVE_NETWORK, 0, c.putListener(listener), config);
     }
 
     /**
@@ -1164,30 +1341,47 @@
      * This function is used instead of a sequence of removeNetwork()
      * and saveConfiguration().
      *
+     * @param c is the channel created at {@link #initialize}
      * @param config the set of variables that describe the configuration,
      *            contained in a {@link WifiConfiguration} object.
+     * @param listener for callbacks on success or failure. Can be null.
      * @hide
      */
-    public void forgetNetwork(int netId) {
-        if (netId < 0) {
-            return;
-        }
+    public void forget(Channel c, int netId, ActionListener listener) {
+        if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+        if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
 
-        mAsyncChannel.sendMessage(CMD_FORGET_NETWORK, netId);
+        c.mAsyncChannel.sendMessage(FORGET_NETWORK, netId, c.putListener(listener));
+    }
+
+    /**
+     * Disable network
+     *
+     * @param c is the channel created at {@link #initialize}
+     * @param netId is the network Id
+     * @param listener for callbacks on success or failure. Can be null.
+     * @hide
+     */
+    public void disable(Channel c, int netId, ActionListener listener) {
+        if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+        if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
+
+        c.mAsyncChannel.sendMessage(DISABLE_NETWORK, netId, c.putListener(listener));
     }
 
     /**
      * Start Wi-fi Protected Setup
      *
+     * @param c is the channel created at {@link #initialize}
      * @param config WPS configuration
+     * @param listener for callbacks on success or failure. Can be null.
      * @hide
      */
-    public void startWps(WpsInfo config) {
-        if (config == null) {
-            return;
-        }
+    public void startWps(Channel c, WpsInfo config, WpsListener listener) {
+        if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+        if (config == null) throw new IllegalArgumentException("config cannot be null");
 
-        mAsyncChannel.sendMessage(CMD_START_WPS, config);
+        c.mAsyncChannel.sendMessage(START_WPS, 0, c.putListener(listener), config);
     }
 
     /**
@@ -1197,15 +1391,30 @@
      * @return Messenger pointing to the WifiService handler
      * @hide
      */
-    public Messenger getMessenger() {
+    public Messenger getWifiServiceMessenger() {
         try {
-            return mService.getMessenger();
+            return mService.getWifiServiceMessenger();
         } catch (RemoteException e) {
             return null;
         }
     }
 
     /**
+     * Get a reference to WifiStateMachine handler.
+     * @return Messenger pointing to the WifiService handler
+     * @hide
+     */
+    public Messenger getWifiStateMachineMessenger() {
+        try {
+            return mService.getWifiStateMachineMessenger();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+
+
+    /**
      * Returns the file in which IP and proxy configuration data is stored
      * @hide
      */
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index d05e0b8..e1cfba3 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -64,7 +64,10 @@
        "pre-shared key may be incorrect";
 
     /* WPS events */
+    private static final String WPS_SUCCESS_STR = "WPS-SUCCESS";
+    private static final String WPS_FAIL_STR    = "WPS-FAIL";
     private static final String WPS_OVERLAP_STR = "WPS-OVERLAP-DETECTED";
+    private static final String WPS_TIMEOUT_STR = "WPS-TIMEOUT";
 
     /**
      * Names of events from wpa_supplicant (minus the prefix). In the
@@ -221,10 +224,16 @@
     public static final int SUPPLICANT_STATE_CHANGE_EVENT        = BASE + 6;
     /* Password failure and EAP authentication failure */
     public static final int AUTHENTICATION_FAILURE_EVENT         = BASE + 7;
-    /* WPS overlap detected */
-    public static final int WPS_OVERLAP_EVENT                    = BASE + 8;
+    /* WPS success detected */
+    public static final int WPS_SUCCESS_EVENT                    = BASE + 8;
+    /* WPS failure detected */
+    public static final int WPS_FAIL_EVENT                       = BASE + 9;
+     /* WPS overlap detected */
+    public static final int WPS_OVERLAP_EVENT                    = BASE + 10;
+     /* WPS timeout detected */
+    public static final int WPS_TIMEOUT_EVENT                    = BASE + 11;
     /* Driver was hung */
-    public static final int DRIVER_HUNG_EVENT                    = BASE + 9;
+    public static final int DRIVER_HUNG_EVENT                    = BASE + 12;
 
     /* P2P events */
     public static final int P2P_DEVICE_FOUND_EVENT               = BASE + 21;
@@ -304,8 +313,14 @@
                     if (eventStr.startsWith(WPA_EVENT_PREFIX_STR) &&
                             0 < eventStr.indexOf(PASSWORD_MAY_BE_INCORRECT_STR)) {
                         mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
+                    } else if (eventStr.startsWith(WPS_SUCCESS_STR)) {
+                        mStateMachine.sendMessage(WPS_SUCCESS_EVENT);
+                    } else if (eventStr.startsWith(WPS_FAIL_STR)) {
+                        mStateMachine.sendMessage(WPS_FAIL_EVENT);
                     } else if (eventStr.startsWith(WPS_OVERLAP_STR)) {
                         mStateMachine.sendMessage(WPS_OVERLAP_EVENT);
+                    } else if (eventStr.startsWith(WPS_TIMEOUT_STR)) {
+                        mStateMachine.sendMessage(WPS_TIMEOUT_EVENT);
                     } else if (eventStr.startsWith(P2P_EVENT_PREFIX_STR)) {
                         handleP2pEvents(eventStr);
                     } else if (eventStr.startsWith(HOST_AP_EVENT_PREFIX_STR)) {
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 1b64f3e..d6988cd 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -184,7 +184,6 @@
     private WifiInfo mWifiInfo;
     private NetworkInfo mNetworkInfo;
     private SupplicantStateTracker mSupplicantStateTracker;
-    private WpsStateMachine mWpsStateMachine;
     private DhcpStateMachine mDhcpStateMachine;
 
     private AlarmManager mAlarmManager;
@@ -275,16 +274,14 @@
     static final int CMD_ENABLE_NETWORK                   = BASE + 54;
     /* Enable all networks */
     static final int CMD_ENABLE_ALL_NETWORKS              = BASE + 55;
-    /* Disable a network. The device does not attempt a connection to the given network. */
-    static final int CMD_DISABLE_NETWORK                  = BASE + 56;
     /* Blacklist network. De-prioritizes the given BSSID for connection. */
-    static final int CMD_BLACKLIST_NETWORK                = BASE + 57;
+    static final int CMD_BLACKLIST_NETWORK                = BASE + 56;
     /* Clear the blacklist network list */
-    static final int CMD_CLEAR_BLACKLIST                  = BASE + 58;
+    static final int CMD_CLEAR_BLACKLIST                  = BASE + 57;
     /* Save configuration */
-    static final int CMD_SAVE_CONFIG                      = BASE + 59;
+    static final int CMD_SAVE_CONFIG                      = BASE + 58;
     /* Get configured networks*/
-    static final int CMD_GET_CONFIGURED_NETWORKS          = BASE + 60;
+    static final int CMD_GET_CONFIGURED_NETWORKS          = BASE + 59;
 
     /* Supplicant commands after driver start*/
     /* Initiate a scan */
@@ -328,28 +325,7 @@
     static final int MULTICAST_V6  = 1;
     static final int MULTICAST_V4  = 0;
 
-    /* Connect to a specified network (network id
-     * or WifiConfiguration) This involves increasing
-     * the priority of the network, enabling the network
-     * (while disabling others) and issuing a reconnect.
-     * Note that CMD_RECONNECT just does a reconnect to
-     * an existing network. All the networks get enabled
-     * upon a successful connection or a failure.
-     */
-    static final int CMD_CONNECT_NETWORK                  = BASE + 86;
-    /* Save the specified network. This involves adding
-     * an enabled network (if new) and updating the
-     * config and issuing a save on supplicant config.
-     */
-    static final int CMD_SAVE_NETWORK                     = BASE + 87;
-    /* Delete the specified network. This involves
-     * removing the network and issuing a save on
-     * supplicant config.
-     */
-    static final int CMD_FORGET_NETWORK                   = BASE + 88;
-    /* Start Wi-Fi protected setup */
-    static final int CMD_START_WPS                        = BASE + 89;
-    /* Set the frequency band */
+   /* Set the frequency band */
     static final int CMD_SET_FREQUENCY_BAND               = BASE + 90;
     /* Enable background scan for configured networks */
     static final int CMD_ENABLE_BACKGROUND_SCAN           = BASE + 91;
@@ -358,12 +334,6 @@
     /* Reset the supplicant state tracker */
     static final int CMD_RESET_SUPPLICANT_STATE           = BASE + 111;
 
-    /* Commands/events reported by WpsStateMachine */
-    /* Indicates the completion of WPS activity */
-    static final int WPS_COMPLETED_EVENT                  = BASE + 121;
-    /* Reset the WPS state machine */
-    static final int CMD_RESET_WPS_STATE                  = BASE + 122;
-
     /* P2p commands */
     public static final int CMD_ENABLE_P2P                = BASE + 131;
     public static final int CMD_DISABLE_P2P               = BASE + 132;
@@ -463,8 +433,12 @@
     private State mScanModeState = new ScanModeState();
     /* Connecting to an access point */
     private State mConnectModeState = new ConnectModeState();
-    /* Fetching IP after network connection (assoc+auth complete) */
-    private State mConnectingState = new ConnectingState();
+    /* Connected at 802.11 (L2) level */
+    private State mL2ConnectedState = new L2ConnectedState();
+    /* fetching IP after connection to access point (assoc+auth complete) */
+    private State mObtainingIpState = new ObtainingIpState();
+    /* Waiting for link quality verification to be complete */
+    private State mVerifyingLinkState = new VerifyingLinkState();
     /* Connected with IP addr */
     private State mConnectedState = new ConnectedState();
     /* disconnect issued, waiting for network disconnect confirmation */
@@ -472,7 +446,7 @@
     /* Network is not connected, supplicant assoc+auth is not complete */
     private State mDisconnectedState = new DisconnectedState();
     /* Waiting for WPS to be completed*/
-    private State mWaitForWpsCompletionState = new WaitForWpsCompletionState();
+    private State mWpsRunningState = new WpsRunningState();
 
     /* Soft ap is starting up */
     private State mSoftApStartingState = new SoftApStartingState();
@@ -566,7 +540,6 @@
         mWifiInfo = new WifiInfo();
         mSupplicantStateTracker = new SupplicantStateTracker(context, this, mWifiConfigStore,
                 getHandler());
-        mWpsStateMachine = new WpsStateMachine(context, this, mWifiConfigStore, getHandler());
         mLinkProperties = new LinkProperties();
 
         WifiApConfigStore wifiApConfigStore = WifiApConfigStore.makeWifiApConfigStore(
@@ -629,11 +602,13 @@
                 addState(mDriverStartedState, mSupplicantStartedState);
                     addState(mScanModeState, mDriverStartedState);
                     addState(mConnectModeState, mDriverStartedState);
-                        addState(mConnectingState, mConnectModeState);
-                        addState(mConnectedState, mConnectModeState);
+                        addState(mL2ConnectedState, mConnectModeState);
+                            addState(mObtainingIpState, mL2ConnectedState);
+                            addState(mVerifyingLinkState, mL2ConnectedState);
+                            addState(mConnectedState, mL2ConnectedState);
                         addState(mDisconnectingState, mConnectModeState);
                         addState(mDisconnectedState, mConnectModeState);
-                        addState(mWaitForWpsCompletionState, mConnectModeState);
+                        addState(mWpsRunningState, mConnectModeState);
                 addState(mDriverStoppingState, mSupplicantStartedState);
                 addState(mDriverStoppedState, mSupplicantStartedState);
             addState(mSupplicantStoppingState, mDefaultState);
@@ -655,6 +630,9 @@
      * Methods exposed for public use
      ********************************************************/
 
+    public Messenger getMessenger() {
+        return new Messenger(getHandler());
+    }
     /**
      * TODO: doc
      */
@@ -899,9 +877,8 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
-        Message resultMsg = channel.sendMessageSynchronously(CMD_DISABLE_NETWORK, netId,
-                WifiConfiguration.DISABLED_UNKNOWN_REASON);
-        boolean result = (resultMsg.arg1 != FAILURE);
+        Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId);
+        boolean result = (resultMsg.arg1 != WifiManager.DISABLE_NETWORK_FAILED);
         resultMsg.recycle();
         return result;
     }
@@ -924,39 +901,6 @@
         sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
     }
 
-    public void connectNetwork(int netId) {
-        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, netId, 0));
-    }
-
-    public void connectNetwork(WifiConfiguration wifiConfig) {
-        /* arg1 is used to indicate netId, force a netId value of
-         * WifiConfiguration.INVALID_NETWORK_ID when we are passing
-         * a configuration since the default value of 0 is a valid netId
-         */
-        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
-                0, wifiConfig));
-    }
-
-    public void saveNetwork(WifiConfiguration wifiConfig) {
-        sendMessage(obtainMessage(CMD_SAVE_NETWORK, wifiConfig));
-    }
-
-    public void forgetNetwork(int netId) {
-        sendMessage(obtainMessage(CMD_FORGET_NETWORK, netId, 0));
-    }
-
-    public void disableNetwork(Messenger replyTo, int netId, int reason) {
-        Message message = obtainMessage(CMD_DISABLE_NETWORK, netId, reason);
-        message.replyTo = replyTo;
-        sendMessage(message);
-    }
-
-    public void startWps(Messenger replyTo, WpsInfo config) {
-        Message msg = obtainMessage(CMD_START_WPS, config);
-        msg.replyTo = replyTo;
-        sendMessage(msg);
-    }
-
     public void enableRssiPolling(boolean enabled) {
        sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
     }
@@ -1543,22 +1487,17 @@
         Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
-        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
+        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
         intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
         if (bssid != null)
             intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
-        if (mNetworkInfo.getState() == NetworkInfo.State.CONNECTED)
+        if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
+                mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
             intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
+        }
         mContext.sendStickyBroadcast(intent);
     }
 
-    private void sendErrorBroadcast(int errorCode) {
-        Intent intent = new Intent(WifiManager.ERROR_ACTION);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        intent.putExtra(WifiManager.EXTRA_ERROR_CODE, errorCode);
-        mContext.sendBroadcast(intent);
-    }
-
     private void sendLinkConfigurationChangedBroadcast() {
         Intent intent = new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -1615,7 +1554,6 @@
         }
 
         mSupplicantStateTracker.sendMessage(Message.obtain(message));
-        mWpsStateMachine.sendMessage(Message.obtain(message));
 
         return state;
     }
@@ -1740,9 +1678,6 @@
             }
         } else {
             configureLinkProperties();
-            setNetworkDetailedState(DetailedState.CONNECTED);
-            mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
-            sendNetworkStateChangeBroadcast(mLastBssid);
         }
     }
 
@@ -1828,14 +1763,13 @@
                     /* Synchronous call returns */
                 case CMD_PING_SUPPLICANT:
                 case CMD_ENABLE_NETWORK:
-                case CMD_DISABLE_NETWORK:
                 case CMD_ADD_OR_UPDATE_NETWORK:
                 case CMD_REMOVE_NETWORK:
                 case CMD_SAVE_CONFIG:
-                    mReplyChannel.replyToMessage(message, message.what, FAILURE);
+                    replyToMessage(message, message.what, FAILURE);
                     break;
                 case CMD_GET_CONFIGURED_NETWORKS:
-                    mReplyChannel.replyToMessage(message, message.what,
+                    replyToMessage(message, message.what,
                             mWifiConfigStore.getConfiguredNetworks());
                     break;
                 case CMD_ENABLE_RSSI_POLL:
@@ -1878,9 +1812,6 @@
                 case CMD_SET_HIGH_PERF_MODE:
                 case CMD_SET_COUNTRY_CODE:
                 case CMD_SET_FREQUENCY_BAND:
-                case CMD_CONNECT_NETWORK:
-                case CMD_SAVE_NETWORK:
-                case CMD_FORGET_NETWORK:
                 case CMD_RSSI_POLL:
                 case CMD_ENABLE_ALL_NETWORKS:
                 case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
@@ -1890,15 +1821,32 @@
                 case CMD_SET_AP_CONFIG_COMPLETED:
                 case CMD_REQUEST_AP_CONFIG:
                 case CMD_RESPONSE_AP_CONFIG:
+                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
+                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
                     break;
                 case WifiMonitor.DRIVER_HUNG_EVENT:
                     setWifiEnabled(false);
                     setWifiEnabled(true);
                     break;
-                case CMD_START_WPS:
-                    /* Return failure when the state machine cannot handle WPS initiation*/
-                    mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED,
-                                new WpsResult(Status.FAILURE));
+                case WifiManager.CONNECT_NETWORK:
+                    replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
+                            WifiManager.BUSY);
+                    break;
+                case WifiManager.FORGET_NETWORK:
+                    replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
+                            WifiManager.BUSY);
+                    break;
+                case WifiManager.SAVE_NETWORK:
+                    replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
+                            WifiManager.BUSY);
+                    break;
+                case WifiManager.START_WPS:
+                    replyToMessage(message, WifiManager.WPS_FAILED,
+                            WifiManager.BUSY);
+                    break;
+                case WifiManager.DISABLE_NETWORK:
+                    replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
+                            WifiManager.BUSY);
                     break;
                 default:
                     loge("Error! unhandled message" + message);
@@ -2212,7 +2160,6 @@
                     /* Reset the supplicant state to indicate the supplicant
                      * state is not known at this time */
                     mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
-                    mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
                     /* Initialize data structures */
                     mLastBssid = null;
                     mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
@@ -2296,7 +2243,6 @@
                     handleNetworkDisconnect();
                     sendSupplicantConnectionChangedBroadcast(false);
                     mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
-                    mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
                     transitionTo(mDriverLoadedState);
                     sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
                     break;
@@ -2308,20 +2254,20 @@
                     break;
                 case CMD_PING_SUPPLICANT:
                     boolean ok = mWifiNative.ping();
-                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
                     break;
                 case CMD_ADD_OR_UPDATE_NETWORK:
                     config = (WifiConfiguration) message.obj;
-                    mReplyChannel.replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
+                    replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
                             mWifiConfigStore.addOrUpdateNetwork(config));
                     break;
                 case CMD_REMOVE_NETWORK:
                     ok = mWifiConfigStore.removeNetwork(message.arg1);
-                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
                     break;
                 case CMD_ENABLE_NETWORK:
                     ok = mWifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
-                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+                    replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
                     break;
                 case CMD_ENABLE_ALL_NETWORKS:
                     long time =  android.os.SystemClock.elapsedRealtime();
@@ -2330,9 +2276,14 @@
                         mLastEnableAllNetworksTime = time;
                     }
                     break;
-                case CMD_DISABLE_NETWORK:
-                    ok = mWifiConfigStore.disableNetwork(message.arg1, message.arg2);
-                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+                case WifiManager.DISABLE_NETWORK:
+                    if (mWifiConfigStore.disableNetwork(message.arg1,
+                            WifiConfiguration.DISABLED_UNKNOWN_REASON) == true) {
+                        replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
+                    } else {
+                        replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
+                                WifiManager.ERROR);
+                    }
                     break;
                 case CMD_BLACKLIST_NETWORK:
                     mWifiNative.addToBlacklist((String)message.obj);
@@ -2342,7 +2293,7 @@
                     break;
                 case CMD_SAVE_CONFIG:
                     ok = mWifiConfigStore.saveConfig();
-                    mReplyChannel.replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
+                    replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
 
                     // Inform the backup manager about a data change
                     IBackupManager ibm = IBackupManager.Stub.asInterface(
@@ -2363,12 +2314,25 @@
                 case CMD_SET_SCAN_MODE:
                     mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
                     break;
-                case CMD_SAVE_NETWORK:
+                case WifiManager.SAVE_NETWORK:
                     config = (WifiConfiguration) message.obj;
-                    mWifiConfigStore.saveNetwork(config);
+                    NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
+                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
+                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
+                    } else {
+                        loge("Failed to save network");
+                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
+                                WifiManager.ERROR);
+                    }
                     break;
-                case CMD_FORGET_NETWORK:
-                    mWifiConfigStore.forgetNetwork(message.arg1);
+                case WifiManager.FORGET_NETWORK:
+                    if (mWifiConfigStore.forgetNetwork(message.arg1)) {
+                        replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
+                    } else {
+                        loge("Failed to forget network");
+                        replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
+                                WifiManager.ERROR);
+                    }
                     break;
                 default:
                     return NOT_HANDLED;
@@ -2407,7 +2371,6 @@
             setWifiState(WIFI_STATE_DISABLING);
             sendSupplicantConnectionChangedBroadcast(false);
             mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
-            mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
         }
         @Override
         public boolean processMessage(Message message) {
@@ -2800,10 +2763,6 @@
                 case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
                     mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
                     break;
-                case WifiMonitor.WPS_OVERLAP_EVENT:
-                    /* We just need to broadcast the error */
-                    sendErrorBroadcast(WifiManager.WPS_OVERLAP_ERROR);
-                    break;
                 case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
                     SupplicantState state = handleSupplicantStateChange(message);
                     // Due to a WEXT bug, during the time of driver start/stop
@@ -2840,7 +2799,7 @@
                 case CMD_REASSOCIATE:
                     mWifiNative.reassociate();
                     break;
-                case CMD_CONNECT_NETWORK:
+                case WifiManager.CONNECT_NETWORK:
                     int netId = message.arg1;
                     WifiConfiguration config = (WifiConfiguration) message.obj;
 
@@ -2858,15 +2817,44 @@
                     }
 
                     /* The state tracker handles enabling networks upon completion/failure */
-                    mSupplicantStateTracker.sendMessage(CMD_CONNECT_NETWORK);
+                    mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
 
-                    mWifiNative.reconnect();
+                    if (mWifiNative.reconnect()) {
+                        replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
+                    } else {
+                        loge("Failed to initiate connection");
+                        replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
+                                WifiManager.ERROR);
+                    }
+
                     /* Expect a disconnection from the old connection */
                     transitionTo(mDisconnectingState);
                     break;
-                case CMD_START_WPS:
-                    mWpsStateMachine.sendMessage(Message.obtain(message));
-                    transitionTo(mWaitForWpsCompletionState);
+                case WifiManager.START_WPS:
+                    WpsInfo wpsInfo = (WpsInfo) message.obj;
+                    WpsResult result;
+                    switch (wpsInfo.setup) {
+                        case WpsInfo.PBC:
+                            result = mWifiConfigStore.startWpsPbc(wpsInfo);
+                            break;
+                        case WpsInfo.KEYPAD:
+                            result = mWifiConfigStore.startWpsWithPinFromAccessPoint(wpsInfo);
+                            break;
+                        case WpsInfo.DISPLAY:
+                            result = mWifiConfigStore.startWpsWithPinFromDevice(wpsInfo);
+                            break;
+                        default:
+                            result = new WpsResult(Status.FAILURE);
+                            Log.e(TAG, "Invalid setup for WPS");
+                            break;
+                    }
+                    if (result.status == Status.SUCCESS) {
+                        replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, result);
+                        transitionTo(mWpsRunningState);
+                    } else {
+                        Log.e(TAG, "Failed to start WPS with config " + wpsInfo.toString());
+                        replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
+                    }
                     break;
                 case WifiMonitor.SCAN_RESULTS_EVENT:
                     /* Set the scan setting back to "connect" mode */
@@ -2885,7 +2873,7 @@
                     /* send event to CM & network change broadcast */
                     setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
                     sendNetworkStateChangeBroadcast(mLastBssid);
-                    transitionTo(mConnectingState);
+                    transitionTo(mObtainingIpState);
                     break;
                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
                     if (DBG) log("Network connection lost");
@@ -2900,20 +2888,138 @@
         }
     }
 
-    class ConnectingState extends State {
-
+    class L2ConnectedState extends State {
         @Override
         public void enter() {
             if (DBG) log(getName() + "\n");
             EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
-            try {
-                mNwService.enableIpv6(mInterfaceName);
-            } catch (RemoteException re) {
-                loge("Failed to enable IPv6: " + re);
-            } catch (IllegalStateException e) {
-                loge("Failed to enable IPv6: " + e);
+            mRssiPollToken++;
+            if (mEnableRssiPolling) {
+                sendMessage(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0));
             }
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) log(getName() + message.toString() + "\n");
+            boolean eventLoggingEnabled = true;
+            switch (message.what) {
+              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                  handlePreDhcpSetup();
+                  mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
+                  break;
+              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
+                  handlePostDhcpSetup();
+                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
+                      if (DBG) log("DHCP successful");
+                      handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
+                      transitionTo(mVerifyingLinkState);
+                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
+                      if (DBG) log("DHCP failed");
+                      handleFailedIpConfiguration();
+                      transitionTo(mDisconnectingState);
+                  }
+                  break;
+                case CMD_DISCONNECT:
+                    mWifiNative.disconnect();
+                    transitionTo(mDisconnectingState);
+                    break;
+                case CMD_SET_SCAN_MODE:
+                    if (message.arg1 == SCAN_ONLY_MODE) {
+                        sendMessage(CMD_DISCONNECT);
+                        deferMessage(message);
+                    }
+                    break;
+                case CMD_START_SCAN:
+                    eventLoggingEnabled = false;
+                    /* When the network is connected, re-scanning can trigger
+                     * a reconnection. Put it in scan-only mode during scan.
+                     * When scan results are received, the mode is switched
+                     * back to CONNECT_MODE.
+                     */
+                    mWifiNative.setScanResultHandling(SCAN_ONLY_MODE);
+                    /* Have the parent state handle the rest */
+                    return NOT_HANDLED;
+                    /* Ignore connection to same network */
+                case WifiManager.CONNECT_NETWORK:
+                    int netId = message.arg1;
+                    if (mWifiInfo.getNetworkId() == netId) {
+                        break;
+                    }
+                    return NOT_HANDLED;
+                case WifiManager.SAVE_NETWORK:
+                    WifiConfiguration config = (WifiConfiguration) message.obj;
+                    NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
+                    if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
+                        if (result.hasIpChanged()) {
+                            log("Reconfiguring IP on connection");
+                            transitionTo(mObtainingIpState);
+                        }
+                        if (result.hasProxyChanged()) {
+                            log("Reconfiguring proxy on connection");
+                            configureLinkProperties();
+                            sendLinkConfigurationChangedBroadcast();
+                        }
+                    }
+
+                    if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
+                        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
+                    } else {
+                        loge("Failed to save network");
+                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
+                                WifiManager.ERROR);
+                    }
+                    break;
+                    /* Ignore */
+                case WifiMonitor.NETWORK_CONNECTION_EVENT:
+                    break;
+                case CMD_RSSI_POLL:
+                    eventLoggingEnabled = false;
+                    if (message.arg1 == mRssiPollToken) {
+                        // Get Info and continue polling
+                        fetchRssiAndLinkSpeedNative();
+                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
+                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
+                    } else {
+                        // Polling has completed
+                    }
+                    break;
+                case CMD_ENABLE_RSSI_POLL:
+                    mEnableRssiPolling = (message.arg1 == 1);
+                    mRssiPollToken++;
+                    if (mEnableRssiPolling) {
+                        // first poll
+                        fetchRssiAndLinkSpeedNative();
+                        sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
+                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+
+            if (eventLoggingEnabled) {
+                EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            }
+            return HANDLED;
+        }
+
+        @Override
+        public void exit() {
+            /* If a scan result is pending in connected state, the supplicant
+             * is in SCAN_ONLY_MODE. Restore CONNECT_MODE on exit
+             */
+            if (mScanResultIsPending) {
+                mWifiNative.setScanResultHandling(CONNECT_MODE);
+            }
+        }
+    }
+
+    class ObtainingIpState extends State {
+        @Override
+        public void enter() {
+            if (DBG) log(getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
 
             if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
                 //start DHCP
@@ -2943,55 +3049,16 @@
       @Override
       public boolean processMessage(Message message) {
           if (DBG) log(getName() + message.toString() + "\n");
-
           switch(message.what) {
-              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
-                  handlePreDhcpSetup();
-                  mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
-                  break;
-              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
-                  handlePostDhcpSetup();
-                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
-                      handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
-                      transitionTo(mConnectedState);
-                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
-                      handleFailedIpConfiguration();
-                      transitionTo(mDisconnectingState);
-                  }
-                  break;
-              case CMD_STATIC_IP_SUCCESS:
+            case CMD_STATIC_IP_SUCCESS:
                   handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
-                  transitionTo(mConnectedState);
+                  transitionTo(mVerifyingLinkState);
                   break;
               case CMD_STATIC_IP_FAILURE:
                   handleFailedIpConfiguration();
                   transitionTo(mDisconnectingState);
                   break;
-              case CMD_DISCONNECT:
-                  mWifiNative.disconnect();
-                  transitionTo(mDisconnectingState);
-                  break;
-                  /* Ignore connection to same network */
-              case CMD_CONNECT_NETWORK:
-                  int netId = message.arg1;
-                  if (mWifiInfo.getNetworkId() == netId) {
-                      break;
-                  }
-                  return NOT_HANDLED;
-              case CMD_SAVE_NETWORK:
-                  deferMessage(message);
-                  break;
-                  /* Ignore */
-              case WifiMonitor.NETWORK_CONNECTION_EVENT:
-                  break;
-              case CMD_SET_SCAN_MODE:
-                  if (message.arg1 == SCAN_ONLY_MODE) {
-                      sendMessage(CMD_DISCONNECT);
-                      deferMessage(message);
-                  }
-                  break;
-                  /* Defer scan when IP is being fetched */
-              case CMD_START_SCAN:
+             case WifiManager.SAVE_NETWORK:
                   deferMessage(message);
                   break;
                   /* Defer any power mode changes since we must keep active power mode at DHCP */
@@ -2999,128 +3066,87 @@
                   deferMessage(message);
                   break;
               default:
-                return NOT_HANDLED;
+                  return NOT_HANDLED;
           }
           EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
           return HANDLED;
       }
     }
 
+    class VerifyingLinkState extends State {
+        @Override
+        public void enter() {
+            if (DBG) log(getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+            setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
+            mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
+            sendNetworkStateChangeBroadcast(mLastBssid);
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            switch (message.what) {
+                case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
+                    //stay here
+                    break;
+                case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
+                    try {
+                        mNwService.enableIpv6(mInterfaceName);
+                    } catch (RemoteException re) {
+                        loge("Failed to enable IPv6: " + re);
+                    } catch (IllegalStateException e) {
+                        loge("Failed to enable IPv6: " + e);
+                    }
+
+                    setNetworkDetailedState(DetailedState.CONNECTED);
+                    mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
+                    sendNetworkStateChangeBroadcast(mLastBssid);
+                    transitionTo(mConnectedState);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
     class ConnectedState extends State {
         @Override
         public void enter() {
             if (DBG) log(getName() + "\n");
             EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-            mRssiPollToken++;
-            if (mEnableRssiPolling) {
-                sendMessage(obtainMessage(WifiStateMachine.CMD_RSSI_POLL, mRssiPollToken, 0));
-            }
-        }
+       }
         @Override
         public boolean processMessage(Message message) {
             if (DBG) log(getName() + message.toString() + "\n");
-            boolean eventLoggingEnabled = true;
             switch (message.what) {
-              case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
-                  handlePreDhcpSetup();
-                  mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
-                  break;
-              case DhcpStateMachine.CMD_POST_DHCP_ACTION:
-                  handlePostDhcpSetup();
-                  if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
-                      handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
-                  } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
-                      handleFailedIpConfiguration();
-                      transitionTo(mDisconnectingState);
-                  }
-                  break;
-                case CMD_DISCONNECT:
-                    mWifiNative.disconnect();
-                    transitionTo(mDisconnectingState);
-                    break;
-                case CMD_SET_SCAN_MODE:
-                    if (message.arg1 == SCAN_ONLY_MODE) {
-                        sendMessage(CMD_DISCONNECT);
-                        deferMessage(message);
+               case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
+                    if (DBG) log("Watchdog reports poor link");
+                    try {
+                        mNwService.disableIpv6(mInterfaceName);
+                    } catch (RemoteException re) {
+                        loge("Failed to disable IPv6: " + re);
+                    } catch (IllegalStateException e) {
+                        loge("Failed to disable IPv6: " + e);
                     }
-                    break;
-                case CMD_START_SCAN:
-                    eventLoggingEnabled = false;
-                    /* When the network is connected, re-scanning can trigger
-                     * a reconnection. Put it in scan-only mode during scan.
-                     * When scan results are received, the mode is switched
-                     * back to CONNECT_MODE.
-                     */
-                    mWifiNative.setScanResultHandling(SCAN_ONLY_MODE);
-                    /* Have the parent state handle the rest */
-                    return NOT_HANDLED;
-                    /* Ignore connection to same network */
-                case CMD_CONNECT_NETWORK:
-                    int netId = message.arg1;
-                    if (mWifiInfo.getNetworkId() == netId) {
-                        break;
-                    }
-                    return NOT_HANDLED;
-                case CMD_SAVE_NETWORK:
-                    WifiConfiguration config = (WifiConfiguration) message.obj;
-                    NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
-                    if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
-                        if (result.hasIpChanged()) {
-                            log("Reconfiguring IP on connection");
-                            transitionTo(mConnectingState);
-                        }
-                        if (result.hasProxyChanged()) {
-                            log("Reconfiguring proxy on connection");
-                            configureLinkProperties();
-                            sendLinkConfigurationChangedBroadcast();
-                        }
-                    }
-                    break;
-                    /* Ignore */
-                case WifiMonitor.NETWORK_CONNECTION_EVENT:
-                    break;
-                case CMD_RSSI_POLL:
-                    eventLoggingEnabled = false;
-                    if (message.arg1 == mRssiPollToken) {
-                        // Get Info and continue polling
-                        fetchRssiAndLinkSpeedNative();
-                        sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
-                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
-                    } else {
-                        // Polling has completed
-                    }
-                    break;
-                case CMD_ENABLE_RSSI_POLL:
-                    mEnableRssiPolling = (message.arg1 == 1);
-                    mRssiPollToken++;
-                    if (mEnableRssiPolling) {
-                        // first poll
-                        fetchRssiAndLinkSpeedNative();
-                        sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
-                                mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
-                    }
+                    /* Report a disconnect */
+                    setNetworkDetailedState(DetailedState.DISCONNECTED);
+                    mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
+                    sendNetworkStateChangeBroadcast(mLastBssid);
+
+                    transitionTo(mVerifyingLinkState);
                     break;
                 default:
                     return NOT_HANDLED;
             }
-            if (eventLoggingEnabled) {
-                EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
         @Override
         public void exit() {
-
             /* Request a CS wakelock during transition to mobile */
             checkAndSetConnectivityInstance();
             mCm.requestNetworkTransitionWakelock(TAG);
-
-            /* If a scan result is pending in connected state, the supplicant
-             * is in SCAN_ONLY_MODE. Restore CONNECT_MODE on exit
-             */
-            if (mScanResultIsPending) {
-                mWifiNative.setScanResultHandling(CONNECT_MODE);
-            }
         }
     }
 
@@ -3268,43 +3294,69 @@
         }
     }
 
-    class WaitForWpsCompletionState extends State {
+    class WpsRunningState extends State {
+        //Tracks the source to provide a reply
+        private Message mSourceMessage;
         @Override
         public void enter() {
             if (DBG) log(getName() + "\n");
             EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+            mSourceMessage = Message.obtain(getCurrentMessage());
         }
         @Override
         public boolean processMessage(Message message) {
             if (DBG) log(getName() + message.toString() + "\n");
             switch (message.what) {
+                case WifiMonitor.WPS_SUCCESS_EVENT:
+                    replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
+                    mSourceMessage.recycle();
+                    mSourceMessage = null;
+                    transitionTo(mDisconnectedState);
+                    break;
+                case WifiMonitor.WPS_OVERLAP_EVENT:
+                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
+                            WifiManager.WPS_OVERLAP_ERROR);
+                    mSourceMessage.recycle();
+                    mSourceMessage = null;
+                    transitionTo(mDisconnectedState);
+                    break;
+                case WifiMonitor.WPS_FAIL_EVENT:
+                case WifiMonitor.WPS_TIMEOUT_EVENT:
+                    replyToMessage(mSourceMessage, WifiManager.WPS_FAILED, WifiManager.ERROR);
+                    mSourceMessage.recycle();
+                    mSourceMessage = null;
+                    transitionTo(mDisconnectedState);
+                    break;
+                case WifiManager.START_WPS:
+                    replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.IN_PROGRESS);
+                    break;
                 /* Defer all commands that can cause connections to a different network
                  * or put the state machine out of connect mode
                  */
                 case CMD_STOP_DRIVER:
                 case CMD_SET_SCAN_MODE:
-                case CMD_CONNECT_NETWORK:
+                case WifiManager.CONNECT_NETWORK:
                 case CMD_ENABLE_NETWORK:
                 case CMD_RECONNECT:
                 case CMD_REASSOCIATE:
-                case WifiMonitor.NETWORK_CONNECTION_EVENT: /* Handled after IP & proxy update */
+                case WifiMonitor.NETWORK_CONNECTION_EVENT: /* Handled after exiting WPS state */
                     deferMessage(message);
                     break;
                 case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
                     if (DBG) log("Network connection lost");
                     handleNetworkDisconnect();
                     break;
-                case WPS_COMPLETED_EVENT:
-                    /* we are still disconnected until we see a network connection
-                     * notification */
-                    transitionTo(mDisconnectedState);
-                    break;
                 default:
                     return NOT_HANDLED;
             }
             EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
             return HANDLED;
         }
+
+        public void exit() {
+            mWifiConfigStore.enableAllNetworks();
+            mWifiConfigStore.loadConfiguredNetworks();
+        }
     }
 
     class SoftApStartingState extends State {
@@ -3564,6 +3616,43 @@
         }
     }
 
+    //State machine initiated requests can have replyTo set to null indicating
+    //there are no recepients, we ignore those reply actions
+    private void replyToMessage(Message msg, int what) {
+        if (msg.replyTo == null) return;
+        Message dstMsg = obtainMessageWithArg2(msg);
+        dstMsg.what = what;
+        mReplyChannel.replyToMessage(msg, dstMsg);
+    }
+
+    private void replyToMessage(Message msg, int what, int arg1) {
+        if (msg.replyTo == null) return;
+        Message dstMsg = obtainMessageWithArg2(msg);
+        dstMsg.what = what;
+        dstMsg.arg1 = arg1;
+        mReplyChannel.replyToMessage(msg, dstMsg);
+    }
+
+    private void replyToMessage(Message msg, int what, Object obj) {
+        if (msg.replyTo == null) return;
+        Message dstMsg = obtainMessageWithArg2(msg);
+        dstMsg.what = what;
+        dstMsg.obj = obj;
+        mReplyChannel.replyToMessage(msg, dstMsg);
+    }
+
+    /**
+     * arg2 on the source message has a unique id that needs to be retained in replies
+     * to match the request
+     *
+     * see WifiManager for details
+     */
+    private Message obtainMessageWithArg2(Message srcMsg) {
+        Message msg = Message.obtain();
+        msg.arg2 = srcMsg.arg2;
+        return msg;
+    }
+
     private void log(String s) {
         Log.d(TAG, s);
     }
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 0ca3852..a2f6343 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -26,9 +26,12 @@
 import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.net.arp.ArpPeer;
 import android.net.ConnectivityManager;
-import android.net.DnsPinger;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
 import android.net.NetworkInfo;
+import android.net.RouteInfo;
 import android.net.Uri;
 import android.os.Message;
 import android.os.SystemClock;
@@ -38,6 +41,7 @@
 import android.util.Log;
 
 import com.android.internal.R;
+import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
@@ -46,49 +50,66 @@
 import java.io.PrintWriter;
 import java.net.HttpURLConnection;
 import java.net.InetAddress;
+import java.net.SocketException;
 import java.net.URL;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
 
 /**
- * {@link WifiWatchdogStateMachine} monitors the initial connection to a Wi-Fi
- * network with multiple access points. After the framework successfully
- * connects to an access point, the watchdog verifies connectivity by 'pinging'
- * the configured DNS server using {@link DnsPinger}.
- * <p>
- * On DNS check failure, the BSSID is blacklisted if it is reasonably likely
- * that another AP might have internet access; otherwise the SSID is disabled.
- * <p>
- * On DNS success, the WatchdogService initiates a walled garden check via an
- * http get. A browser window is activated if a walled garden is detected.
+ * WifiWatchdogStateMachine monitors the connection to a Wi-Fi
+ * network. After the framework notifies that it has connected to an
+ * acccess point and is waiting for link to be verified, the watchdog
+ * takes over and verifies if the link is good by doing ARP pings to
+ * the gateway using {@link ArpPeer}.
+ *
+ * Upon successful verification, the watchdog notifies and continues
+ * to monitor the link afterwards when the RSSI level falls below
+ * a certain threshold.
+
+ * When Wi-fi connects at L2 layer, the beacons from access point reach
+ * the device and it can maintain a connection, but the application
+ * connectivity can be flaky (due to bigger packet size exchange).
+ *
+ * We now monitor the quality of the last hop on
+ * Wi-Fi using signal strength and ARP connectivity as indicators
+ * to decide if the link is good enough to switch to Wi-Fi as the uplink.
+ *
+ * ARP pings are useful for link validation but can still get through
+ * when the application traffic fails to go through and are thus not
+ * the best indicator of real packet loss since they are tiny packets
+ * (28 bytes) and have a much low chance of packet corruption than the
+ * regular data packets.
+ *
+ * When signal strength and ARP are used together, it ends up working well in tests.
+ * The goal is to switch to Wi-Fi after validating ARP transfer
+ * and RSSI and then switching out of Wi-Fi when we hit a low
+ * signal strength threshold and then waiting until the signal strength
+ * improves and validating ARP transfer.
  *
  * @hide
  */
 public class WifiWatchdogStateMachine extends StateMachine {
 
-    private static final boolean DBG = false;
+    /* STOPSHIP: Keep this configurable for debugging until ship */
+    private static boolean DBG = false;
     private static final String TAG = "WifiWatchdogStateMachine";
-    private static final String DISABLED_NETWORK_NOTIFICATION_ID = "WifiWatchdog.networkdisabled";
     private static final String WALLED_GARDEN_NOTIFICATION_ID = "WifiWatchdog.walledgarden";
 
-    private static final int WIFI_SIGNAL_LEVELS = 4;
-    /**
-     * Low signal is defined as less than or equal to cut off
-     */
-    private static final int LOW_SIGNAL_CUTOFF = 0;
+    /* Wi-fi connection is considered poor below this
+       RSSI level threshold and the watchdog report it
+       to the WifiStateMachine */
+    private static final int RSSI_LEVEL_CUTOFF = 1;
+    /* Wi-fi connection is monitored actively below this
+       threshold */
+    private static final int RSSI_LEVEL_MONITOR = 2;
 
-    private static final long DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS = 2 * 60 * 1000;
-    private static final long DEFAULT_DNS_CHECK_LONG_INTERVAL_MS = 60 * 60 * 1000;
+    private int mCurrentSignalLevel;
+
+    private static final long DEFAULT_ARP_CHECK_INTERVAL_MS = 2 * 60 * 1000;
     private static final long DEFAULT_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000;
 
-    private static final int DEFAULT_MAX_SSID_BLACKLISTS = 7;
-    private static final int DEFAULT_NUM_DNS_PINGS = 5; // Multiple pings to detect setup issues
-    private static final int DEFAULT_MIN_DNS_RESPONSES = 1;
+    private static final int DEFAULT_NUM_ARP_PINGS = 5;
+    private static final int DEFAULT_MIN_ARP_RESPONSES = 1;
 
-    private static final int DEFAULT_DNS_PING_TIMEOUT_MS = 2000;
-
-    private static final long DEFAULT_BLACKLIST_FOLLOWUP_INTERVAL_MS = 15 * 1000;
+    private static final int DEFAULT_ARP_PING_TIMEOUT_MS = 100;
 
     // See http://go/clientsdns for usage approval
     private static final String DEFAULT_WALLED_GARDEN_URL =
@@ -102,10 +123,6 @@
      */
     private static final int WALLED_GARDEN_START_DELAY_MS = 3000;
 
-    private static final int DNS_INTRATEST_PING_INTERVAL_MS = 200;
-    /* With some router setups, it takes a few hunder milli-seconds before connection is active */
-    private static final int DNS_START_DELAY_MS = 1000;
-
     private static final int BASE = Protocol.BASE_WIFI_WATCHDOG;
 
     /**
@@ -118,99 +135,76 @@
      * which has a non-null networkInfo object
      */
     private static final int EVENT_NETWORK_STATE_CHANGE             = BASE + 2;
-    /**
-     * Indicates the signal has changed. Passed with arg1
-     * {@link #mNetEventCounter} and arg2 [raw signal strength]
-     */
+    /* Passed with RSSI information */
     private static final int EVENT_RSSI_CHANGE                      = BASE + 3;
-    private static final int EVENT_SCAN_RESULTS_AVAILABLE           = BASE + 4;
     private static final int EVENT_WIFI_RADIO_STATE_CHANGE          = BASE + 5;
     private static final int EVENT_WATCHDOG_SETTINGS_CHANGE         = BASE + 6;
 
-    private static final int MESSAGE_HANDLE_WALLED_GARDEN           = BASE + 100;
-    private static final int MESSAGE_HANDLE_BAD_AP                  = BASE + 101;
-    /**
-     * arg1 == mOnlineWatchState.checkCount
-     */
-    private static final int MESSAGE_SINGLE_DNS_CHECK               = BASE + 102;
-    private static final int MESSAGE_NETWORK_FOLLOWUP               = BASE + 103;
-    private static final int MESSAGE_DELAYED_WALLED_GARDEN_CHECK    = BASE + 104;
+    /* Internal messages */
+    private static final int CMD_ARP_CHECK                          = BASE + 11;
+    private static final int CMD_DELAYED_WALLED_GARDEN_CHECK        = BASE + 12;
+
+    /* Notifications to WifiStateMachine */
+    static final int POOR_LINK_DETECTED                             = BASE + 21;
+    static final int GOOD_LINK_DETECTED                             = BASE + 22;
+
+    private static final int SINGLE_ARP_CHECK = 0;
+    private static final int FULL_ARP_CHECK   = 1;
 
     private Context mContext;
     private ContentResolver mContentResolver;
     private WifiManager mWifiManager;
-    private DnsPinger mDnsPinger;
     private IntentFilter mIntentFilter;
     private BroadcastReceiver mBroadcastReceiver;
+    private AsyncChannel mWsmChannel = new AsyncChannel();;
 
     private DefaultState mDefaultState = new DefaultState();
     private WatchdogDisabledState mWatchdogDisabledState = new WatchdogDisabledState();
     private WatchdogEnabledState mWatchdogEnabledState = new WatchdogEnabledState();
     private NotConnectedState mNotConnectedState = new NotConnectedState();
+    private VerifyingLinkState mVerifyingLinkState = new VerifyingLinkState();
     private ConnectedState mConnectedState = new ConnectedState();
-    private DnsCheckingState mDnsCheckingState = new DnsCheckingState();
+    private WalledGardenCheckState mWalledGardenCheckState = new WalledGardenCheckState();
+    /* Online and watching link connectivity */
     private OnlineWatchState mOnlineWatchState = new OnlineWatchState();
+    /* Online and doing nothing */
     private OnlineState mOnlineState = new OnlineState();
-    private DnsCheckFailureState mDnsCheckFailureState = new DnsCheckFailureState();
-    private DelayWalledGardenState mDelayWalledGardenState = new DelayWalledGardenState();
-    private WalledGardenState mWalledGardenState = new WalledGardenState();
-    private BlacklistedApState mBlacklistedApState = new BlacklistedApState();
 
-    private long mDnsCheckShortIntervalMs;
-    private long mDnsCheckLongIntervalMs;
+    private int mArpToken = 0;
+    private long mArpCheckIntervalMs;
     private long mWalledGardenIntervalMs;
-    private int mMaxSsidBlacklists;
-    private int mNumDnsPings;
-    private int mMinDnsResponses;
-    private int mDnsPingTimeoutMs;
-    private long mBlacklistFollowupIntervalMs;
+    private int mNumArpPings;
+    private int mMinArpResponses;
+    private int mArpPingTimeoutMs;
     private boolean mPoorNetworkDetectionEnabled;
     private boolean mWalledGardenTestEnabled;
     private String mWalledGardenUrl;
 
-    private boolean mShowDisabledNotification;
-    /**
-     * The {@link WifiInfo} object passed to WWSM on network broadcasts
-     */
-    private WifiInfo mConnectionInfo;
-    private int mNetEventCounter = 0;
+    private WifiInfo mWifiInfo;
+    private LinkProperties mLinkProperties;
 
-    /**
-     * Currently maintained but not used, TODO
-     */
-    private HashSet<String> mBssids = new HashSet<String>();
-    private int mNumCheckFailures = 0;
+    private long mLastWalledGardenCheckTime = 0;
 
-    private Long mLastWalledGardenCheckTime = null;
-
-    /**
-     * This is set by the blacklisted state and reset when connected to a new AP.
-     * It triggers a disableNetwork call if a DNS check fails.
-     */
-    public boolean mDisableAPNextFailure = false;
     private static boolean sWifiOnly = false;
-    private boolean mDisabledNotificationShown;
     private boolean mWalledGardenNotificationShown;
-    public boolean mHasConnectedWifiManager = false;
 
     /**
      * STATE MAP
      *          Default
      *         /       \
-     * Disabled     Enabled
-     *             /       \
-     * NotConnected      Connected
-     *                  /---------\
-     *               (all other states)
+     * Disabled      Enabled
+     *             /     \     \
+     * NotConnected  Verifying  Connected
+     *                         /---------\
+     *                       (all other states)
      */
     private WifiWatchdogStateMachine(Context context) {
         super(TAG);
         mContext = context;
         mContentResolver = context.getContentResolver();
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-        mDnsPinger = new DnsPinger(mContext, "WifiWatchdogStateMachine.DnsPinger",
-                                this.getHandler().getLooper(), this.getHandler(),
-                                ConnectivityManager.TYPE_WIFI);
+        mWsmChannel.connectSync(mContext, getHandler(),
+                mWifiManager.getWifiStateMachineMessenger());
 
         setupNetworkReceiver();
 
@@ -221,16 +215,17 @@
             addState(mWatchdogDisabledState, mDefaultState);
             addState(mWatchdogEnabledState, mDefaultState);
                 addState(mNotConnectedState, mWatchdogEnabledState);
+                addState(mVerifyingLinkState, mWatchdogEnabledState);
                 addState(mConnectedState, mWatchdogEnabledState);
-                    addState(mDnsCheckingState, mConnectedState);
-                    addState(mDnsCheckFailureState, mConnectedState);
-                    addState(mDelayWalledGardenState, mConnectedState);
-                    addState(mWalledGardenState, mConnectedState);
-                    addState(mBlacklistedApState, mConnectedState);
+                    addState(mWalledGardenCheckState, mConnectedState);
                     addState(mOnlineWatchState, mConnectedState);
                     addState(mOnlineState, mConnectedState);
 
-        setInitialState(mWatchdogDisabledState);
+        if (isWatchdogEnabled()) {
+            setInitialState(mNotConnectedState);
+        } else {
+            setInitialState(mWatchdogDisabledState);
+        }
         updateSettings();
     }
 
@@ -242,19 +237,15 @@
         sWifiOnly = (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
 
         // Disable for wifi only devices.
-        if (Settings.Secure.getString(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON) == null &&
-                sWifiOnly) {
+        if (Settings.Secure.getString(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON) == null
+                && sWifiOnly) {
             putSettingsBoolean(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON, false);
         }
         WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context);
         wwsm.start();
-        wwsm.sendMessage(EVENT_WATCHDOG_TOGGLED);
         return wwsm;
     }
 
-    /**
-   *
-   */
     private void setupNetworkReceiver() {
         mBroadcastReceiver = new BroadcastReceiver() {
             @Override
@@ -263,10 +254,8 @@
                 if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                     sendMessage(EVENT_NETWORK_STATE_CHANGE, intent);
                 } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
-                    obtainMessage(EVENT_RSSI_CHANGE, mNetEventCounter,
-                            intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200)).sendToTarget();
-                } else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
-                    sendMessage(EVENT_SCAN_RESULTS_AVAILABLE);
+                    obtainMessage(EVENT_RSSI_CHANGE,
+                            intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200), 0).sendToTarget();
                 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
                     sendMessage(EVENT_WIFI_RADIO_STATE_CHANGE,
                             intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
@@ -279,7 +268,7 @@
         mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
-        mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+        mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
     }
 
     /**
@@ -311,39 +300,29 @@
 
         mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(
-                        Settings.Secure.WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS),
+                        Settings.Secure.WIFI_WATCHDOG_ARP_CHECK_INTERVAL_MS),
                         false, contentObserver);
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS),
-                false, contentObserver);
-        mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS),
                 false, contentObserver);
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MAX_SSID_BLACKLISTS),
+                Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_NUM_ARP_PINGS),
                 false, contentObserver);
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_NUM_DNS_PINGS),
+                Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MIN_ARP_RESPONSES),
                 false, contentObserver);
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MIN_DNS_RESPONSES),
+                Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ARP_PING_TIMEOUT_MS),
                 false, contentObserver);
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS),
+                Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED),
                 false, contentObserver);
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(
-                        Settings.Secure.WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS),
-                        false, contentObserver);
-        mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED),
                 false, contentObserver);
         mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL),
                 false, contentObserver);
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP)
-                , false, contentObserver);
     }
 
     /**
@@ -375,17 +354,20 @@
         }
     }
 
-    private boolean rssiStrengthAboveCutoff(int rssi) {
-        return WifiManager.calculateSignalLevel(rssi, WIFI_SIGNAL_LEVELS) > LOW_SIGNAL_CUTOFF;
-    }
-
     public void dump(PrintWriter pw) {
         pw.print("WatchdogStatus: ");
-        pw.print("State " + getCurrentState());
-        pw.println(", network [" + mConnectionInfo + "]");
-        pw.print("checkFailures   " + mNumCheckFailures);
-        pw.println(", bssids: " + mBssids);
-        pw.println("lastSingleCheck: " + mOnlineWatchState.lastCheckTime);
+        pw.print("State: " + getCurrentState());
+        pw.println("mWifiInfo: [" + mWifiInfo + "]");
+        pw.println("mLinkProperties: [" + mLinkProperties + "]");
+        pw.println("mCurrentSignalLevel: [" + mCurrentSignalLevel + "]");
+        pw.println("mArpCheckIntervalMs: [" + mArpCheckIntervalMs+ "]");
+        pw.println("mWalledGardenIntervalMs: [" + mWalledGardenIntervalMs + "]");
+        pw.println("mNumArpPings: [" + mNumArpPings + "]");
+        pw.println("mMinArpResponses: [" + mMinArpResponses + "]");
+        pw.println("mArpPingTimeoutMs: [" + mArpPingTimeoutMs + "]");
+        pw.println("mPoorNetworkDetectionEnabled: [" + mPoorNetworkDetectionEnabled + "]");
+        pw.println("mWalledGardenTestEnabled: [" + mWalledGardenTestEnabled + "]");
+        pw.println("mWalledGardenUrl: [" + mWalledGardenUrl + "]");
     }
 
     private boolean isWatchdogEnabled() {
@@ -393,31 +375,22 @@
     }
 
     private void updateSettings() {
-        mDnsCheckShortIntervalMs = Secure.getLong(mContentResolver,
-                Secure.WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS,
-                DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS);
-        mDnsCheckLongIntervalMs = Secure.getLong(mContentResolver,
-                Secure.WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS,
-                DEFAULT_DNS_CHECK_LONG_INTERVAL_MS);
-        mMaxSsidBlacklists = Secure.getInt(mContentResolver,
-                Secure.WIFI_WATCHDOG_MAX_SSID_BLACKLISTS,
-                DEFAULT_MAX_SSID_BLACKLISTS);
-        mNumDnsPings = Secure.getInt(mContentResolver,
-                Secure.WIFI_WATCHDOG_NUM_DNS_PINGS,
-                DEFAULT_NUM_DNS_PINGS);
-        mMinDnsResponses = Secure.getInt(mContentResolver,
-                Secure.WIFI_WATCHDOG_MIN_DNS_RESPONSES,
-                DEFAULT_MIN_DNS_RESPONSES);
-        mDnsPingTimeoutMs = Secure.getInt(mContentResolver,
-                Secure.WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS,
-                DEFAULT_DNS_PING_TIMEOUT_MS);
-        mBlacklistFollowupIntervalMs = Secure.getLong(mContentResolver,
-                Settings.Secure.WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS,
-                DEFAULT_BLACKLIST_FOLLOWUP_INTERVAL_MS);
-        //TODO: enable this by default after changing watchdog behavior
-        //Also, update settings description
+        if (DBG) log("Updating secure settings");
+
+        mArpCheckIntervalMs = Secure.getLong(mContentResolver,
+                Secure.WIFI_WATCHDOG_ARP_CHECK_INTERVAL_MS,
+                DEFAULT_ARP_CHECK_INTERVAL_MS);
+        mNumArpPings = Secure.getInt(mContentResolver,
+                Secure.WIFI_WATCHDOG_NUM_ARP_PINGS,
+                DEFAULT_NUM_ARP_PINGS);
+        mMinArpResponses = Secure.getInt(mContentResolver,
+                Secure.WIFI_WATCHDOG_MIN_ARP_RESPONSES,
+                DEFAULT_MIN_ARP_RESPONSES);
+        mArpPingTimeoutMs = Secure.getInt(mContentResolver,
+                Secure.WIFI_WATCHDOG_ARP_PING_TIMEOUT_MS,
+                DEFAULT_ARP_PING_TIMEOUT_MS);
         mPoorNetworkDetectionEnabled = getSettingsBoolean(mContentResolver,
-                Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, false);
+                Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, true);
         mWalledGardenTestEnabled = getSettingsBoolean(mContentResolver,
                 Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, true);
         mWalledGardenUrl = getSettingsStr(mContentResolver,
@@ -426,69 +399,6 @@
         mWalledGardenIntervalMs = Secure.getLong(mContentResolver,
                 Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS,
                 DEFAULT_WALLED_GARDEN_INTERVAL_MS);
-        mShowDisabledNotification = getSettingsBoolean(mContentResolver,
-                Settings.Secure.WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP, true);
-    }
-
-    /**
-     * Helper to return wait time left given a min interval and last run
-     *
-     * @param interval minimum wait interval
-     * @param lastTime last time action was performed in
-     *            SystemClock.elapsedRealtime(). Null if never.
-     * @return non negative time to wait
-     */
-    private static long waitTime(long interval, Long lastTime) {
-        if (lastTime == null)
-            return 0;
-        long wait = interval + lastTime - SystemClock.elapsedRealtime();
-        return wait > 0 ? wait : 0;
-    }
-
-    private static String wifiInfoToStr(WifiInfo wifiInfo) {
-        if (wifiInfo == null)
-            return "null";
-        return "(" + wifiInfo.getSSID() + ", " + wifiInfo.getBSSID() + ")";
-    }
-
-    /**
-     * Uses {@link #mConnectionInfo}.
-     */
-    private void updateBssids() {
-        String curSsid = mConnectionInfo.getSSID();
-        List<ScanResult> results = mWifiManager.getScanResults();
-        int oldNumBssids = mBssids.size();
-
-        if (results == null) {
-            if (DBG) {
-                log("updateBssids: Got null scan results!");
-            }
-            return;
-        }
-
-        for (ScanResult result : results) {
-            if (result == null || result.SSID == null) {
-                if (DBG) {
-                    log("Received invalid scan result: " + result);
-                }
-                continue;
-            }
-            if (curSsid.equals(result.SSID))
-                mBssids.add(result.BSSID);
-        }
-    }
-
-    private void resetWatchdogState() {
-        if (DBG) {
-            log("Resetting watchdog state...");
-        }
-        mConnectionInfo = null;
-        mDisableAPNextFailure = false;
-        mLastWalledGardenCheckTime = null;
-        mNumCheckFailures = 0;
-        mBssids.clear();
-        setDisabledNetworkNotificationVisible(false);
-        setWalledGardenNotificationVisible(false);
     }
 
     private void setWalledGardenNotificationVisible(boolean visible) {
@@ -507,7 +417,7 @@
 
             CharSequence title = r.getString(R.string.wifi_available_sign_in, 0);
             CharSequence details = r.getString(R.string.wifi_available_sign_in_detailed,
-                    mConnectionInfo.getSSID());
+                    mWifiInfo.getSSID());
 
             Notification notification = new Notification();
             notification.when = 0;
@@ -524,41 +434,6 @@
         mWalledGardenNotificationShown = visible;
     }
 
-    private void setDisabledNetworkNotificationVisible(boolean visible) {
-        // If it should be hidden and it is already hidden, then noop
-        if (!visible && !mDisabledNotificationShown) {
-            return;
-        }
-
-        Resources r = Resources.getSystem();
-        NotificationManager notificationManager = (NotificationManager) mContext
-            .getSystemService(Context.NOTIFICATION_SERVICE);
-
-        if (visible) {
-            CharSequence title = r.getText(R.string.wifi_watchdog_network_disabled);
-            String msg = mConnectionInfo.getSSID() +
-                r.getText(R.string.wifi_watchdog_network_disabled_detailed);
-
-            Notification wifiDisabledWarning = new Notification.Builder(mContext)
-                .setSmallIcon(R.drawable.stat_sys_warning)
-                .setDefaults(Notification.DEFAULT_ALL)
-                .setTicker(title)
-                .setContentTitle(title)
-                .setContentText(msg)
-                .setContentIntent(PendingIntent.getActivity(mContext, 0,
-                            new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK)
-                            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0))
-                .setWhen(System.currentTimeMillis())
-                .setAutoCancel(true)
-                .getNotification();
-
-            notificationManager.notify(DISABLED_NETWORK_NOTIFICATION_ID, 1, wifiDisabledWarning);
-        } else {
-            notificationManager.cancel(DISABLED_NETWORK_NOTIFICATION_ID, 1);
-        }
-        mDisabledNotificationShown = visible;
-    }
-
     class DefaultState extends State {
         @Override
         public boolean processMessage(Message msg) {
@@ -568,11 +443,20 @@
                     if (DBG) {
                         log("Updating wifi-watchdog secure settings");
                     }
-                    return HANDLED;
-            }
-            if (DBG) {
-                log("Caught message " + msg.what + " in state " +
-                        getCurrentState().getName());
+                    break;
+                case EVENT_RSSI_CHANGE:
+                    mCurrentSignalLevel = WifiManager.calculateSignalLevel(msg.arg1,
+                            WifiManager.RSSI_LEVELS);
+                    break;
+                case EVENT_WIFI_RADIO_STATE_CHANGE:
+                case EVENT_NETWORK_STATE_CHANGE:
+                case CMD_ARP_CHECK:
+                case CMD_DELAYED_WALLED_GARDEN_CHECK:
+                    //ignore
+                    break;
+                default:
+                    log("Unhandled message " + msg + " in state " + getCurrentState().getName());
+                    break;
             }
             return HANDLED;
         }
@@ -586,6 +470,20 @@
                     if (isWatchdogEnabled())
                         transitionTo(mNotConnectedState);
                     return HANDLED;
+                case EVENT_NETWORK_STATE_CHANGE:
+                    Intent intent = (Intent) msg.obj;
+                    NetworkInfo networkInfo = (NetworkInfo)
+                            intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+
+                    switch (networkInfo.getDetailedState()) {
+                        case VERIFYING_POOR_LINK:
+                            if (DBG) log("Watchdog disabled, verify link");
+                            mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
+                            break;
+                        default:
+                            break;
+                    }
+                    break;
             }
             return NOT_HANDLED;
         }
@@ -594,10 +492,8 @@
     class WatchdogEnabledState extends State {
         @Override
         public void enter() {
-            resetWatchdogState();
-            mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
             if (DBG) log("WifiWatchdogService enabled");
-        }
+       }
 
         @Override
         public boolean processMessage(Message msg) {
@@ -605,77 +501,57 @@
                 case EVENT_WATCHDOG_TOGGLED:
                     if (!isWatchdogEnabled())
                         transitionTo(mWatchdogDisabledState);
-                    return HANDLED;
+                    break;
                 case EVENT_NETWORK_STATE_CHANGE:
-                    Intent stateChangeIntent = (Intent) msg.obj;
+                    Intent intent = (Intent) msg.obj;
                     NetworkInfo networkInfo = (NetworkInfo)
-                            stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+                            intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
 
-                    setDisabledNetworkNotificationVisible(false);
-                    setWalledGardenNotificationVisible(false);
-                    switch (networkInfo.getState()) {
-                        case CONNECTED:
-                            WifiInfo wifiInfo = (WifiInfo)
-                                stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
-                            if (wifiInfo == null) {
-                                loge("Connected --> WifiInfo object null!");
-                                return HANDLED;
-                            }
-
-                            if (wifiInfo.getSSID() == null || wifiInfo.getBSSID() == null) {
-                                loge("Received wifiInfo object with null elts: "
-                                        + wifiInfoToStr(wifiInfo));
-                                return HANDLED;
-                            }
-
-                            initConnection(wifiInfo);
-                            mConnectionInfo = wifiInfo;
-                            mNetEventCounter++;
+                    switch (networkInfo.getDetailedState()) {
+                        case VERIFYING_POOR_LINK:
+                            mLinkProperties = (LinkProperties) intent.getParcelableExtra(
+                                    WifiManager.EXTRA_LINK_PROPERTIES);
+                            mWifiInfo = (WifiInfo) intent.getParcelableExtra(
+                                    WifiManager.EXTRA_WIFI_INFO);
                             if (mPoorNetworkDetectionEnabled) {
-                                updateBssids();
-                                transitionTo(mDnsCheckingState);
+                                if (mWifiInfo == null) {
+                                    log("Ignoring link verification, mWifiInfo is NULL");
+                                    mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
+                                } else {
+                                    transitionTo(mVerifyingLinkState);
+                                }
                             } else {
-                                transitionTo(mDelayWalledGardenState);
+                                mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
+                            }
+                            break;
+                        case CONNECTED:
+                            if (shouldCheckWalledGarden()) {
+                                transitionTo(mWalledGardenCheckState);
+                            } else {
+                                transitionTo(mOnlineWatchState);
                             }
                             break;
                         default:
-                            mNetEventCounter++;
                             transitionTo(mNotConnectedState);
                             break;
                     }
-                    return HANDLED;
+                    break;
                 case EVENT_WIFI_RADIO_STATE_CHANGE:
                     if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING) {
                         if (DBG) log("WifiStateDisabling -- Resetting WatchdogState");
-                        resetWatchdogState();
-                        mNetEventCounter++;
                         transitionTo(mNotConnectedState);
                     }
-                    return HANDLED;
+                    break;
+                default:
+                    return NOT_HANDLED;
             }
 
-            return NOT_HANDLED;
-        }
-
-        /**
-         * @param wifiInfo Info object with non-null ssid and bssid
-         */
-        private void initConnection(WifiInfo wifiInfo) {
-            if (DBG) {
-                log("Connected:: old " + wifiInfoToStr(mConnectionInfo) +
-                        " ==> new " + wifiInfoToStr(wifiInfo));
-            }
-
-            if (mConnectionInfo == null || !wifiInfo.getSSID().equals(mConnectionInfo.getSSID())) {
-                resetWatchdogState();
-            } else if (!wifiInfo.getBSSID().equals(mConnectionInfo.getBSSID())) {
-                mDisableAPNextFailure = false;
-            }
+            setWalledGardenNotificationVisible(false);
+            return HANDLED;
         }
 
         @Override
         public void exit() {
-            mContext.unregisterReceiver(mBroadcastReceiver);
             if (DBG) log("WifiWatchdogService disabled");
         }
     }
@@ -683,17 +559,74 @@
     class NotConnectedState extends State {
     }
 
-    class ConnectedState extends State {
+    class VerifyingLinkState extends State {
+        @Override
+        public void enter() {
+            if (DBG) log(getName() + "\n");
+            //Treat entry as an rssi change
+            handleRssiChange();
+        }
+
+        private void handleRssiChange() {
+            if (mCurrentSignalLevel <= RSSI_LEVEL_CUTOFF) {
+                //stay here
+                if (DBG) log("enter VerifyingLinkState, stay level: " + mCurrentSignalLevel);
+            } else {
+                if (DBG) log("enter VerifyingLinkState, arp check level: " + mCurrentSignalLevel);
+                sendMessage(obtainMessage(CMD_ARP_CHECK, ++mArpToken, 0));
+            }
+        }
+
         @Override
         public boolean processMessage(Message msg) {
             switch (msg.what) {
-                case EVENT_SCAN_RESULTS_AVAILABLE:
-                    if (mPoorNetworkDetectionEnabled) {
-                        updateBssids();
-                    }
-                    return HANDLED;
                 case EVENT_WATCHDOG_SETTINGS_CHANGE:
                     updateSettings();
+                    if (!mPoorNetworkDetectionEnabled) {
+                        mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
+                    }
+                    break;
+                case EVENT_RSSI_CHANGE:
+                    int signalLevel = WifiManager.calculateSignalLevel(msg.arg1,
+                            WifiManager.RSSI_LEVELS);
+                    if (DBG) log("RSSI change old: " + mCurrentSignalLevel + "new: " + signalLevel);
+                    mCurrentSignalLevel = signalLevel;
+
+                    handleRssiChange();
+                    break;
+                case CMD_ARP_CHECK:
+                    if (msg.arg1 == mArpToken) {
+                        if (doArpTest(FULL_ARP_CHECK) == true) {
+                            if (DBG) log("Notify link is good " + mCurrentSignalLevel);
+                            mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
+                        } else {
+                            if (DBG) log("Continue ARP check, rssi level: " + mCurrentSignalLevel);
+                            sendMessageDelayed(obtainMessage(CMD_ARP_CHECK, ++mArpToken, 0),
+                                    mArpCheckIntervalMs);
+                        }
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+    }
+
+    class ConnectedState extends State {
+        @Override
+        public void enter() {
+            if (DBG) log(getName() + "\n");
+        }
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_WATCHDOG_SETTINGS_CHANGE:
+                    updateSettings();
+                    //STOPSHIP: Remove this at ship
+                    DBG = true;
+                    if (DBG) log("Updated secure settings and turned debug on");
+
                     if (mPoorNetworkDetectionEnabled) {
                         transitionTo(mOnlineWatchState);
                     } else {
@@ -705,402 +638,162 @@
         }
     }
 
-    class DnsCheckingState extends State {
-        List<InetAddress> mDnsList;
-        int[] dnsCheckSuccesses;
-        String dnsCheckLogStr;
-        String[] dnsResponseStrs;
-        /** Keeps track of active dns pings.  Map is from pingID to index in mDnsList */
-        HashMap<Integer, Integer> idDnsMap = new HashMap<Integer, Integer>();
-
+    class WalledGardenCheckState extends State {
+        private int mWalledGardenToken = 0;
         @Override
         public void enter() {
-            mDnsList = mDnsPinger.getDnsList();
-            int numDnses = mDnsList.size();
-            dnsCheckSuccesses = new int[numDnses];
-            dnsResponseStrs = new String[numDnses];
-            for (int i = 0; i < numDnses; i++)
-                dnsResponseStrs[i] = "";
-
-            if (DBG) {
-                dnsCheckLogStr = String.format("Pinging %s on ssid [%s]: ",
-                        mDnsList, mConnectionInfo.getSSID());
-                log(dnsCheckLogStr);
-            }
-
-            idDnsMap.clear();
-            for (int i=0; i < mNumDnsPings; i++) {
-                for (int j = 0; j < numDnses; j++) {
-                    idDnsMap.put(mDnsPinger.pingDnsAsync(mDnsList.get(j), mDnsPingTimeoutMs,
-                            DNS_START_DELAY_MS + DNS_INTRATEST_PING_INTERVAL_MS * i), j);
-                }
-            }
-        }
-
-        @Override
-        public boolean processMessage(Message msg) {
-            if (msg.what != DnsPinger.DNS_PING_RESULT) {
-                return NOT_HANDLED;
-            }
-
-            int pingID = msg.arg1;
-            int pingResponseTime = msg.arg2;
-
-            Integer dnsServerId = idDnsMap.get(pingID);
-            if (dnsServerId == null) {
-                loge("Received a Dns response with unknown ID!");
-                return HANDLED;
-            }
-
-            idDnsMap.remove(pingID);
-            if (pingResponseTime >= 0)
-                dnsCheckSuccesses[dnsServerId]++;
-
-            if (DBG) {
-                if (pingResponseTime >= 0) {
-                    dnsResponseStrs[dnsServerId] += "|" + pingResponseTime;
-                } else {
-                    dnsResponseStrs[dnsServerId] += "|x";
-                }
-            }
-
-            /**
-             * After a full ping count, if we have more responses than this
-             * cutoff, the outcome is success; else it is 'failure'.
-             */
-
-            /**
-             * Our final success count will be at least this big, so we're
-             * guaranteed to succeed.
-             */
-            if (dnsCheckSuccesses[dnsServerId] >= mMinDnsResponses) {
-                // DNS CHECKS OK, NOW WALLED GARDEN
-                if (DBG) {
-                    log(makeLogString() + "  SUCCESS");
-                }
-
-                if (!shouldCheckWalledGarden()) {
-                    transitionTo(mOnlineWatchState);
-                    return HANDLED;
-                }
-
-                transitionTo(mDelayWalledGardenState);
-                return HANDLED;
-            }
-
-            if (idDnsMap.isEmpty()) {
-                if (DBG) {
-                    log(makeLogString() + "  FAILURE");
-                }
-                transitionTo(mDnsCheckFailureState);
-                return HANDLED;
-            }
-
-            return HANDLED;
-        }
-
-        private String makeLogString() {
-            String logStr = dnsCheckLogStr;
-            for (String respStr : dnsResponseStrs)
-                logStr += " [" + respStr + "]";
-            return logStr;
-        }
-
-        @Override
-        public void exit() {
-            mDnsPinger.cancelPings();
-        }
-
-        private boolean shouldCheckWalledGarden() {
-            if (!mWalledGardenTestEnabled) {
-                if (DBG)
-                    log("Skipping walled garden check - disabled");
-                return false;
-            }
-            long waitTime = waitTime(mWalledGardenIntervalMs,
-                    mLastWalledGardenCheckTime);
-            if (waitTime > 0) {
-                if (DBG) {
-                    log("Skipping walled garden check - wait " +
-                            waitTime + " ms.");
-                }
-                return false;
-            }
-            return true;
-        }
-    }
-
-    class DelayWalledGardenState extends State {
-        @Override
-        public void enter() {
-            sendMessageDelayed(MESSAGE_DELAYED_WALLED_GARDEN_CHECK, WALLED_GARDEN_START_DELAY_MS);
+            if (DBG) log(getName() + "\n");
+            sendMessageDelayed(obtainMessage(CMD_DELAYED_WALLED_GARDEN_CHECK,
+                    ++mWalledGardenToken, 0), WALLED_GARDEN_START_DELAY_MS);
         }
 
         @Override
         public boolean processMessage(Message msg) {
             switch (msg.what) {
-                case MESSAGE_DELAYED_WALLED_GARDEN_CHECK:
-                    mLastWalledGardenCheckTime = SystemClock.elapsedRealtime();
-                    if (isWalledGardenConnection()) {
-                        if (DBG) log("Walled garden test complete - walled garden detected");
-                        transitionTo(mWalledGardenState);
-                    } else {
-                        if (DBG) log("Walled garden test complete - online");
-                        if (mPoorNetworkDetectionEnabled) {
-                            transitionTo(mOnlineWatchState);
-                        } else {
-                            transitionTo(mOnlineState);
+                case CMD_DELAYED_WALLED_GARDEN_CHECK:
+                    if (msg.arg1 == mWalledGardenToken) {
+                        mLastWalledGardenCheckTime = SystemClock.elapsedRealtime();
+                        if (isWalledGardenConnection()) {
+                            if (DBG) log("Walled garden detected");
+                            setWalledGardenNotificationVisible(true);
                         }
+                        transitionTo(mOnlineWatchState);
                     }
-                    return HANDLED;
+                    break;
                 default:
                     return NOT_HANDLED;
             }
+            return HANDLED;
         }
     }
 
     class OnlineWatchState extends State {
-        /**
-         * Signals a short-wait message is enqueued for the current 'guard' counter
-         */
-        boolean unstableSignalChecks = false;
-
-        /**
-         * The signal is unstable.  We should enqueue a short-wait check, if one is enqueued
-         * already
-         */
-        boolean signalUnstable = false;
-
-        /**
-         * A monotonic counter to ensure that at most one check message will be processed from any
-         * set of check messages currently enqueued.  Avoids duplicate checks when a low-signal
-         * event is observed.
-         */
-        int checkGuard = 0;
-        Long lastCheckTime = null;
-
-        /** Keeps track of dns pings.  Map is from pingID to InetAddress used for ping */
-        HashMap<Integer, InetAddress> pingInfoMap = new HashMap<Integer, InetAddress>();
-
-        @Override
         public void enter() {
-            lastCheckTime = SystemClock.elapsedRealtime();
-            signalUnstable = false;
-            checkGuard++;
-            unstableSignalChecks = false;
-            pingInfoMap.clear();
-            triggerSingleDnsCheck();
+            if (DBG) log(getName() + "\n");
+            if (mPoorNetworkDetectionEnabled) {
+                //Treat entry as an rssi change
+                handleRssiChange();
+            } else {
+                transitionTo(mOnlineState);
+            }
+        }
+
+        private void handleRssiChange() {
+            if (mCurrentSignalLevel <= RSSI_LEVEL_CUTOFF) {
+                if (DBG) log("Transition out, below cut off level: " + mCurrentSignalLevel);
+                mWsmChannel.sendMessage(POOR_LINK_DETECTED);
+            } else if (mCurrentSignalLevel <= RSSI_LEVEL_MONITOR) {
+                if (DBG) log("Start monitoring, level: " + mCurrentSignalLevel);
+                sendMessage(obtainMessage(CMD_ARP_CHECK, ++mArpToken, 0));
+            }
         }
 
         @Override
         public boolean processMessage(Message msg) {
             switch (msg.what) {
                 case EVENT_RSSI_CHANGE:
-                    if (msg.arg1 != mNetEventCounter) {
-                        if (DBG) {
-                            log("Rssi change message out of sync, ignoring");
-                        }
-                        return HANDLED;
-                    }
-                    int newRssi = msg.arg2;
-                    signalUnstable = !rssiStrengthAboveCutoff(newRssi);
-                    if (DBG) {
-                        log("OnlineWatchState:: new rssi " + newRssi + " --> level " +
-                                WifiManager.calculateSignalLevel(newRssi, WIFI_SIGNAL_LEVELS));
-                    }
+                    int signalLevel = WifiManager.calculateSignalLevel(msg.arg1,
+                            WifiManager.RSSI_LEVELS);
+                    if (DBG) log("RSSI change old: " + mCurrentSignalLevel + "new: " + signalLevel);
+                    mCurrentSignalLevel = signalLevel;
 
-                    if (signalUnstable && !unstableSignalChecks) {
-                        if (DBG) {
-                            log("Sending triggered check msg");
-                        }
-                        triggerSingleDnsCheck();
-                    }
-                    return HANDLED;
-                case MESSAGE_SINGLE_DNS_CHECK:
-                    if (msg.arg1 != checkGuard) {
-                        if (DBG) {
-                            log("Single check msg out of sync, ignoring.");
-                        }
-                        return HANDLED;
-                    }
-                    lastCheckTime = SystemClock.elapsedRealtime();
-                    pingInfoMap.clear();
-                    for (InetAddress curDns: mDnsPinger.getDnsList()) {
-                        pingInfoMap.put(mDnsPinger.pingDnsAsync(curDns, mDnsPingTimeoutMs, 0),
-                                curDns);
-                    }
-                    return HANDLED;
-                case DnsPinger.DNS_PING_RESULT:
-                    InetAddress curDnsServer = pingInfoMap.get(msg.arg1);
-                    if (curDnsServer == null) {
-                        return HANDLED;
-                    }
-                    pingInfoMap.remove(msg.arg1);
-                    int responseTime = msg.arg2;
-                    if (responseTime >= 0) {
-                        if (DBG) {
-                            log("Single DNS ping OK. Response time: "
-                                    + responseTime + " from DNS " + curDnsServer);
-                        }
-                        pingInfoMap.clear();
+                    handleRssiChange();
 
-                        checkGuard++;
-                        unstableSignalChecks = false;
-                        triggerSingleDnsCheck();
-                    } else {
-                        if (pingInfoMap.isEmpty()) {
-                            if (DBG) {
-                                log("Single dns ping failure. All dns servers failed, "
-                                        + "starting full checks.");
+                    break;
+                case CMD_ARP_CHECK:
+                    if (msg.arg1 == mArpToken) {
+                        if (doArpTest(SINGLE_ARP_CHECK) != true) {
+                            if (DBG) log("single ARP fail, full ARP check");
+                            //do a full test
+                            if (doArpTest(FULL_ARP_CHECK) != true) {
+                                if (DBG) log("notify full ARP fail, level: " + mCurrentSignalLevel);
+                                mWsmChannel.sendMessage(POOR_LINK_DETECTED);
                             }
-                            transitionTo(mDnsCheckingState);
+                        }
+
+                        if (mCurrentSignalLevel <= RSSI_LEVEL_MONITOR) {
+                            if (DBG) log("Continue ARP check, rssi level: " + mCurrentSignalLevel);
+                            sendMessageDelayed(obtainMessage(CMD_ARP_CHECK, ++mArpToken, 0),
+                                    mArpCheckIntervalMs);
                         }
                     }
-                    return HANDLED;
+                    break;
+                default:
+                    return NOT_HANDLED;
             }
-            return NOT_HANDLED;
-        }
-
-        @Override
-        public void exit() {
-            mDnsPinger.cancelPings();
-        }
-
-        /**
-         * Times a dns check with an interval based on {@link #signalUnstable}
-         */
-        private void triggerSingleDnsCheck() {
-            long waitInterval;
-            if (signalUnstable) {
-                waitInterval = mDnsCheckShortIntervalMs;
-                unstableSignalChecks = true;
-            } else {
-                waitInterval = mDnsCheckLongIntervalMs;
-            }
-            sendMessageDelayed(obtainMessage(MESSAGE_SINGLE_DNS_CHECK, checkGuard, 0),
-                    waitTime(waitInterval, lastCheckTime));
+            return HANDLED;
         }
     }
 
-
     /* Child state of ConnectedState indicating that we are online
      * and there is nothing to do
      */
     class OnlineState extends State {
     }
 
-    class DnsCheckFailureState extends State {
-
-        @Override
-        public void enter() {
-            mNumCheckFailures++;
-            obtainMessage(MESSAGE_HANDLE_BAD_AP, mNetEventCounter, 0).sendToTarget();
+    private boolean shouldCheckWalledGarden() {
+        if (!mWalledGardenTestEnabled) {
+            if (DBG) log("Skipping walled garden check - disabled");
+            return false;
         }
 
-        @Override
-        public boolean processMessage(Message msg) {
-            if (msg.what != MESSAGE_HANDLE_BAD_AP) {
-                return NOT_HANDLED;
+        long waitTime = (mWalledGardenIntervalMs + mLastWalledGardenCheckTime)
+            - SystemClock.elapsedRealtime();
+
+        if (mLastWalledGardenCheckTime != 0 && waitTime > 0) {
+            if (DBG) {
+                log("Skipping walled garden check - wait " +
+                        waitTime + " ms.");
             }
+            return false;
+        }
+        return true;
+    }
 
-            if (msg.arg1 != mNetEventCounter) {
-                if (DBG) {
-                    log("Msg out of sync, ignoring...");
-                }
-                return HANDLED;
-            }
+    private boolean doArpTest(int type) {
+        boolean success;
 
-            if (mDisableAPNextFailure || mNumCheckFailures >= mBssids.size()
-                    || mNumCheckFailures >= mMaxSsidBlacklists) {
-                if (sWifiOnly) {
-                    log("Would disable bad network, but device has no mobile data!" +
-                            "  Going idle...");
-                    // This state should be called idle -- will be changing flow.
-                    transitionTo(mNotConnectedState);
-                    return HANDLED;
-                }
+        String iface = mLinkProperties.getInterfaceName();
+        String mac = mWifiInfo.getMacAddress();
+        InetAddress inetAddress = null;
+        InetAddress gateway = null;
 
-                // TODO : Unban networks if they had low signal ?
-                log("Disabling current SSID " + wifiInfoToStr(mConnectionInfo)
-                        + ".  " + "numCheckFailures " + mNumCheckFailures
-                        + ", numAPs " + mBssids.size());
-                int networkId = mConnectionInfo.getNetworkId();
-                if (!mHasConnectedWifiManager) {
-                    mWifiManager.asyncConnect(mContext, getHandler());
-                    mHasConnectedWifiManager = true;
-                }
-                mWifiManager.disableNetwork(networkId, WifiConfiguration.DISABLED_DNS_FAILURE);
-                if (mShowDisabledNotification) {
-                    setDisabledNetworkNotificationVisible(true);
-                }
-                transitionTo(mNotConnectedState);
+        for (LinkAddress la : mLinkProperties.getLinkAddresses()) {
+            inetAddress = la.getAddress();
+            break;
+        }
+
+        for (RouteInfo route : mLinkProperties.getRoutes()) {
+            gateway = route.getGateway();
+            break;
+        }
+
+        if (DBG) log("ARP " + iface + "addr: " + inetAddress + "mac: " + mac + "gw: " + gateway);
+
+        try {
+            ArpPeer peer = new ArpPeer(iface, inetAddress, mac, gateway);
+            if (type == SINGLE_ARP_CHECK) {
+                success = (peer.doArp(mArpPingTimeoutMs) != null);
+                if (DBG) log("single ARP test result: " + success);
             } else {
-                log("Blacklisting current BSSID.  " + wifiInfoToStr(mConnectionInfo)
-                       + "numCheckFailures " + mNumCheckFailures + ", numAPs " + mBssids.size());
-
-                mWifiManager.addToBlacklist(mConnectionInfo.getBSSID());
-                mWifiManager.reassociate();
-                transitionTo(mBlacklistedApState);
-            }
-            return HANDLED;
-        }
-    }
-
-    class WalledGardenState extends State {
-        @Override
-        public void enter() {
-            obtainMessage(MESSAGE_HANDLE_WALLED_GARDEN, mNetEventCounter, 0).sendToTarget();
-        }
-
-        @Override
-        public boolean processMessage(Message msg) {
-            if (msg.what != MESSAGE_HANDLE_WALLED_GARDEN) {
-                return NOT_HANDLED;
-            }
-
-            if (msg.arg1 != mNetEventCounter) {
-                if (DBG) {
-                    log("WalledGardenState::Msg out of sync, ignoring...");
+                int responses = 0;
+                for (int i=0; i < mNumArpPings; i++) {
+                    if(peer.doArp(mArpPingTimeoutMs) != null) responses++;
                 }
-                return HANDLED;
+                if (DBG) log("full ARP test result: " + responses + "/" + mNumArpPings);
+                success = (responses >= mMinArpResponses);
             }
-            setWalledGardenNotificationVisible(true);
-            if (mPoorNetworkDetectionEnabled) {
-                transitionTo(mOnlineWatchState);
-            } else {
-                transitionTo(mOnlineState);
-            }
-            return HANDLED;
+            peer.close();
+        } catch (SocketException se) {
+            //Consider an Arp socket creation issue as a successful Arp
+            //test to avoid any wifi connectivity issues
+            loge("ARP test initiation failure: " + se);
+            success = true;
         }
+
+        return success;
     }
 
-    class BlacklistedApState extends State {
-        @Override
-        public void enter() {
-            mDisableAPNextFailure = true;
-            sendMessageDelayed(obtainMessage(MESSAGE_NETWORK_FOLLOWUP, mNetEventCounter, 0),
-                    mBlacklistFollowupIntervalMs);
-        }
-
-        @Override
-        public boolean processMessage(Message msg) {
-            if (msg.what != MESSAGE_NETWORK_FOLLOWUP) {
-                return NOT_HANDLED;
-            }
-
-            if (msg.arg1 != mNetEventCounter) {
-                if (DBG) {
-                    log("BlacklistedApState::Msg out of sync, ignoring...");
-                }
-                return HANDLED;
-            }
-
-            transitionTo(mDnsCheckingState);
-            return HANDLED;
-        }
-    }
-
-
     /**
      * Convenience function for retrieving a single secure settings value
      * as a string with a default value.
diff --git a/wifi/java/android/net/wifi/WpsInfo.java b/wifi/java/android/net/wifi/WpsInfo.java
index f70e5af..b80df21 100644
--- a/wifi/java/android/net/wifi/WpsInfo.java
+++ b/wifi/java/android/net/wifi/WpsInfo.java
@@ -16,9 +16,6 @@
 
 package android.net.wifi;
 
-import android.net.LinkProperties;
-import android.net.wifi.WifiConfiguration.IpAssignment;
-import android.net.wifi.WifiConfiguration.ProxySettings;
 import android.os.Parcelable;
 import android.os.Parcel;
 
@@ -51,22 +48,10 @@
     /** Passed with pin method configuration */
     public String pin;
 
-    /** @hide */
-    public IpAssignment ipAssignment;
-
-    /** @hide */
-    public ProxySettings proxySettings;
-
-    /** @hide */
-    public LinkProperties linkProperties;
-
     public WpsInfo() {
         setup = INVALID;
         BSSID = null;
         pin = null;
-        ipAssignment = IpAssignment.UNASSIGNED;
-        proxySettings = ProxySettings.UNASSIGNED;
-        linkProperties = new LinkProperties();
     }
 
     public String toString() {
@@ -77,12 +62,6 @@
         sbuf.append('\n');
         sbuf.append(" pin: ").append(pin);
         sbuf.append('\n');
-        sbuf.append("IP assignment: " + ipAssignment.toString());
-        sbuf.append("\n");
-        sbuf.append("Proxy settings: " + proxySettings.toString());
-        sbuf.append("\n");
-        sbuf.append(linkProperties.toString());
-        sbuf.append("\n");
         return sbuf.toString();
     }
 
@@ -97,9 +76,6 @@
             setup = source.setup;
             BSSID = source.BSSID;
             pin = source.pin;
-            ipAssignment = source.ipAssignment;
-            proxySettings = source.proxySettings;
-            linkProperties = new LinkProperties(source.linkProperties);
         }
     }
 
@@ -108,9 +84,6 @@
         dest.writeInt(setup);
         dest.writeString(BSSID);
         dest.writeString(pin);
-        dest.writeString(ipAssignment.name());
-        dest.writeString(proxySettings.name());
-        dest.writeParcelable(linkProperties, flags);
     }
 
     /** Implement the Parcelable interface */
@@ -121,9 +94,6 @@
                 config.setup = in.readInt();
                 config.BSSID = in.readString();
                 config.pin = in.readString();
-                config.ipAssignment = IpAssignment.valueOf(in.readString());
-                config.proxySettings = ProxySettings.valueOf(in.readString());
-                config.linkProperties = in.readParcelable(null);
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/WpsStateMachine.java b/wifi/java/android/net/wifi/WpsStateMachine.java
deleted file mode 100644
index 441a3b0..0000000
--- a/wifi/java/android/net/wifi/WpsStateMachine.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi;
-
-import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-
-import android.content.Context;
-import android.content.Intent;
-import android.net.wifi.StateChangeResult;
-import android.net.wifi.WpsResult.Status;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Parcelable;
-import android.util.Log;
-
-/**
- * Manages a WPS connection.
- *
- * WPS consists as a series of EAP exchange triggered
- * by a user action that leads to a successful connection
- * after automatic creation of configuration in the
- * supplicant. We currently support the following methods
- * of WPS setup
- * 1. Pin method: Pin can be either obtained from the device
- *    or from the access point to connect to.
- * 2. Push button method: This involves pushing a button on
- *    the access point and the device
- *
- * After a successful WPS setup, the state machine
- * reloads the configuration and updates the IP and proxy
- * settings, if any.
- */
-class WpsStateMachine extends StateMachine {
-
-    private static final String TAG = "WpsStateMachine";
-    private static final boolean DBG = false;
-
-    private WifiStateMachine mWifiStateMachine;
-    private WifiConfigStore mWifiConfigStore;
-
-    private WpsInfo mWpsInfo;
-
-    private Context mContext;
-    AsyncChannel mReplyChannel = new AsyncChannel();
-
-    private State mDefaultState = new DefaultState();
-    private State mInactiveState = new InactiveState();
-    private State mActiveState = new ActiveState();
-
-    public WpsStateMachine(Context context, WifiStateMachine wsm, WifiConfigStore wcs, Handler t) {
-        super(TAG, t.getLooper());
-
-        mContext = context;
-        mWifiStateMachine = wsm;
-        mWifiConfigStore = wcs;
-        addState(mDefaultState);
-            addState(mInactiveState, mDefaultState);
-            addState(mActiveState, mDefaultState);
-
-        setInitialState(mInactiveState);
-
-        //start the state machine
-        start();
-    }
-
-
-    /********************************************************
-     * HSM states
-     *******************************************************/
-
-    class DefaultState extends State {
-        @Override
-         public void enter() {
-             if (DBG) Log.d(TAG, getName() + "\n");
-         }
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
-            WpsInfo wpsConfig;
-            switch (message.what) {
-                case WifiStateMachine.CMD_START_WPS:
-                    mWpsInfo = (WpsInfo) message.obj;
-                    WpsResult result;
-                    switch (mWpsInfo.setup) {
-                        case WpsInfo.PBC:
-                            result = mWifiConfigStore.startWpsPbc(mWpsInfo);
-                            break;
-                        case WpsInfo.KEYPAD:
-                            result = mWifiConfigStore.startWpsWithPinFromAccessPoint(mWpsInfo);
-                            break;
-                        case WpsInfo.DISPLAY:
-                            result = mWifiConfigStore.startWpsWithPinFromDevice(mWpsInfo);
-                            break;
-                        default:
-                            result = new WpsResult(Status.FAILURE);
-                            Log.e(TAG, "Invalid setup for WPS");
-                            break;
-                    }
-                    mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED, result);
-                    if (result.status == Status.SUCCESS) {
-                        transitionTo(mActiveState);
-                    } else {
-                        Log.e(TAG, "Failed to start WPS with config " + mWpsInfo.toString());
-                    }
-                    break;
-                case WifiStateMachine.CMD_RESET_WPS_STATE:
-                    transitionTo(mInactiveState);
-                    break;
-                default:
-                    Log.e(TAG, "Failed to handle " + message);
-                    break;
-            }
-            return HANDLED;
-        }
-    }
-
-    class ActiveState extends State {
-        @Override
-         public void enter() {
-             if (DBG) Log.d(TAG, getName() + "\n");
-         }
-
-        @Override
-        public boolean processMessage(Message message) {
-            boolean retValue = HANDLED;
-            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
-            switch (message.what) {
-                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
-                    StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-                    SupplicantState supState = (SupplicantState) stateChangeResult.state;
-                    switch (supState) {
-                        case COMPLETED:
-                            /* During WPS setup, all other networks are disabled. After
-                             * a successful connect a new config is created in the supplicant.
-                             *
-                             * We need to enable all networks after a successful connection
-                             * and the configuration list needs to be reloaded from the supplicant.
-                             */
-                            Log.d(TAG, "WPS set up successful");
-                            mWifiConfigStore.enableAllNetworks();
-                            mWifiConfigStore.loadConfiguredNetworks();
-                            mWifiConfigStore.updateIpAndProxyFromWpsConfig(
-                                    stateChangeResult.networkId, mWpsInfo);
-                            mWifiStateMachine.sendMessage(WifiStateMachine.WPS_COMPLETED_EVENT);
-                            transitionTo(mInactiveState);
-                            break;
-                        case INACTIVE:
-                            /* A failed WPS connection */
-                            Log.d(TAG, "WPS set up failed, enabling other networks");
-                            mWifiConfigStore.enableAllNetworks();
-                            mWifiStateMachine.sendMessage(WifiStateMachine.WPS_COMPLETED_EVENT);
-                            transitionTo(mInactiveState);
-                            break;
-                        default:
-                            if (DBG) Log.d(TAG, "Ignoring supplicant state " + supState.name());
-                            break;
-                    }
-                    break;
-                case WifiStateMachine.CMD_START_WPS:
-                    /* Ignore request and send an in progress message */
-                    mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED,
-                                new WpsResult(Status.IN_PROGRESS));
-                    break;
-                default:
-                    retValue = NOT_HANDLED;
-            }
-            return retValue;
-        }
-    }
-
-    class InactiveState extends State {
-        @Override
-        public void enter() {
-            if (DBG) Log.d(TAG, getName() + "\n");
-        }
-
-        @Override
-        public boolean processMessage(Message message) {
-            boolean retValue = HANDLED;
-            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
-            switch (message.what) {
-                //Ignore supplicant state changes
-                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
-                    break;
-                default:
-                    retValue = NOT_HANDLED;
-            }
-            return retValue;
-        }
-    }
-
-}