Merge change 5615 into donut

* changes:
  Implement the generic mini-keystore for security.
diff --git a/api/current.xml b/api/current.xml
index 673d053..7faca03 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -3408,50 +3408,6 @@
  visibility="public"
 >
 </field>
-<field name="donut_resource_pad22"
- type="int"
- transient="false"
- volatile="false"
- value="16843402"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad23"
- type="int"
- transient="false"
- volatile="false"
- value="16843401"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad24"
- type="int"
- transient="false"
- volatile="false"
- value="16843400"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad25"
- type="int"
- transient="false"
- volatile="false"
- value="16843399"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="donut_resource_pad3"
  type="int"
  transient="false"
@@ -6730,6 +6686,17 @@
  visibility="public"
 >
 </field>
+<field name="progressBarStyleInverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843399"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="progressBarStyleLarge"
  type="int"
  transient="false"
@@ -6741,6 +6708,17 @@
  visibility="public"
 >
 </field>
+<field name="progressBarStyleLargeInverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843401"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="progressBarStyleSmall"
  type="int"
  transient="false"
@@ -6752,6 +6730,17 @@
  visibility="public"
 >
 </field>
+<field name="progressBarStyleSmallInverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843400"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="progressBarStyleSmallTitle"
  type="int"
  transient="false"
@@ -7302,6 +7291,17 @@
  visibility="public"
 >
 </field>
+<field name="searchSettingsDescription"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843402"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="searchSuggestAuthority"
  type="int"
  transient="false"
@@ -15156,6 +15156,17 @@
  visibility="public"
 >
 </field>
+<field name="Widget_ProgressBar_Inverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973915"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget_ProgressBar_Large"
  type="int"
  transient="false"
@@ -15167,6 +15178,17 @@
  visibility="public"
 >
 </field>
+<field name="Widget_ProgressBar_Large_Inverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973916"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget_ProgressBar_Small"
  type="int"
  transient="false"
@@ -15178,6 +15200,17 @@
  visibility="public"
 >
 </field>
+<field name="Widget_ProgressBar_Small_Inverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973917"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget_RatingBar"
  type="int"
  transient="false"
@@ -15409,39 +15442,6 @@
  visibility="public"
 >
 </field>
-<field name="donut_resource_pad20"
- type="int"
- transient="false"
- volatile="false"
- value="16973917"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad21"
- type="int"
- transient="false"
- volatile="false"
- value="16973916"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad22"
- type="int"
- transient="false"
- volatile="false"
- value="16973915"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="donut_resource_pad3"
  type="int"
  transient="false"
@@ -31346,6 +31346,17 @@
  visibility="public"
 >
 </field>
+<field name="ACTION_BATTERY_OKAY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.BATTERY_OKAY&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTION_BOOT_COMPLETED"
  type="java.lang.String"
  transient="false"
@@ -56678,6 +56689,47 @@
 </package>
 <package name="android.graphics.drawable"
 >
+<interface name="Animatable"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="isRunning"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="start"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="stop"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
 <class name="AnimationDrawable"
  extends="android.graphics.drawable.DrawableContainer"
  abstract="false"
@@ -56686,6 +56738,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.graphics.drawable.Animatable">
+</implements>
 <implements name="java.lang.Runnable">
 </implements>
 <constructor name="AnimationDrawable"
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index 105d4d2..022fe5a 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -410,11 +410,14 @@
 // pass the buffered ISurface to the camera service
 status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface)
 {
-    LOGD("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid());
+    LOGD("setPreviewDisplay(%p) (pid %d)",
+         ((surface == NULL) ? NULL : surface.get()), getCallingPid());
     Mutex::Autolock lock(mLock);
     status_t result = checkPid();
     if (result != NO_ERROR) return result;
+
     Mutex::Autolock surfaceLock(mSurfaceLock);
+    result = NO_ERROR;
     // asBinder() is safe on NULL (returns NULL)
     if (surface->asBinder() != mSurface->asBinder()) {
         if (mSurface != 0 && !mUseOverlay) {
@@ -422,8 +425,17 @@
             mSurface->unregisterBuffers();
         }
         mSurface = surface;
+        // If preview has been already started, set overlay or register preview
+        // buffers now.
+        if (mHardware->previewEnabled()) {
+            if (mUseOverlay) {
+                result = setOverlay();
+            } else if (mSurface != 0) {
+                result = registerPreviewBuffers();
+            }
+        }
     }
-    return NO_ERROR;
+    return result;
 }
 
 // set the preview callback flag to affect how the received frames from
@@ -436,7 +448,7 @@
     mPreviewCallbackFlag = callback_flag;
 }
 
-// start preview mode, must call setPreviewDisplay first
+// start preview mode
 status_t CameraService::Client::startCameraMode(camera_mode mode)
 {
     int callingPid = getCallingPid();
@@ -456,16 +468,18 @@
         return INVALID_OPERATION;
     }
 
-    if (mSurface == 0) {
-        LOGE("setPreviewDisplay must be called before startCameraMode!");
-        return INVALID_OPERATION;
-    }
-
     switch(mode) {
     case CAMERA_RECORDING_MODE:
+        if (mSurface == 0) {
+            LOGE("setPreviewDisplay must be called before startRecordingMode.");
+            return INVALID_OPERATION;
+        }
         return startRecordingMode();
 
     default: // CAMERA_PREVIEW_MODE
+        if (mSurface == 0) {
+            LOGD("mSurface is not set yet.");
+        }
         return startPreviewMode();
     }
 }
@@ -498,6 +512,62 @@
     return ret;
 }
 
+status_t CameraService::Client::setOverlay()
+{
+    LOGD("setOverlay");
+    int w, h;
+    CameraParameters params(mHardware->getParameters());
+    params.getPreviewSize(&w, &h);
+
+    const char *format = params.getPreviewFormat();
+    int fmt;
+    if (!strcmp(format, "yuv422i"))
+        fmt = OVERLAY_FORMAT_YCbCr_422_I;
+    else if (!strcmp(format, "rgb565"))
+        fmt = OVERLAY_FORMAT_RGB_565;
+    else {
+        LOGE("Invalid preview format for overlays");
+        return -EINVAL;
+    }
+
+    status_t ret = NO_ERROR;
+    if (mSurface != 0) {
+        sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
+        ret = mHardware->setOverlay(new Overlay(ref));
+    } else {
+        ret = mHardware->setOverlay(NULL);
+    }
+    if (ret != NO_ERROR) {
+        LOGE("mHardware->setOverlay() failed with status %d\n", ret);
+    }
+    return ret;
+}
+
+status_t CameraService::Client::registerPreviewBuffers()
+{
+    int w, h;
+    CameraParameters params(mHardware->getParameters());
+    params.getPreviewSize(&w, &h);
+
+    uint32_t transform = 0;
+    if (params.getOrientation() ==
+        CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
+      LOGV("portrait mode");
+      transform = ISurface::BufferHeap::ROT_90;
+    }
+    ISurface::BufferHeap buffers(w, h, w, h,
+                                 PIXEL_FORMAT_YCbCr_420_SP,
+                                 transform,
+                                 0,
+                                 mHardware->getPreviewHeap());
+
+    status_t ret = mSurface->registerBuffers(buffers);
+    if (ret != NO_ERROR) {
+        LOGE("registerBuffers failed with status %d", ret);
+    }
+    return ret;
+}
+
 status_t CameraService::Client::startPreviewMode()
 {
     LOGD("startPreviewMode (pid %d)", getCallingPid());
@@ -511,55 +581,24 @@
 #if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
     debug_frame_cnt = 0;
 #endif
-    status_t ret = UNKNOWN_ERROR;
-    int w, h;
-    CameraParameters params(mHardware->getParameters());
-    params.getPreviewSize(&w, &h);
+    status_t ret = NO_ERROR;
 
     if (mUseOverlay) {
-        const char *format = params.getPreviewFormat();
-        int fmt;
-        LOGD("Use Overlays");
-        if (!strcmp(format, "yuv422i"))
-            fmt = OVERLAY_FORMAT_YCbCr_422_I;
-        else if (!strcmp(format, "rgb565"))
-            fmt = OVERLAY_FORMAT_RGB_565;
-        else {
-            LOGE("Invalid preview format for overlays");
-            return -EINVAL;
+        // If preview display has been set, set overlay now.
+        if (mSurface != 0) {
+            ret = setOverlay();
         }
-        sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
-        ret = mHardware->setOverlay(new Overlay(ref));
-        if (ret != NO_ERROR) {
-            LOGE("mHardware->setOverlay() failed with status %d\n", ret);
-            return ret;
-        }
+        if (ret != NO_ERROR) return ret;
         ret = mHardware->startPreview(NULL, mCameraService.get());
-        if (ret != NO_ERROR)
-            LOGE("mHardware->startPreview() failed with status %d\n", ret);
-
     } else {
         ret = mHardware->startPreview(previewCallback,
                                       mCameraService.get());
-        if (ret == NO_ERROR) {
-
-            mSurface->unregisterBuffers();
-
-            uint32_t transform = 0;
-            if (params.getOrientation() ==
-                CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
-              LOGV("portrait mode");
-              transform = ISurface::BufferHeap::ROT_90;
-            }
-            ISurface::BufferHeap buffers(w, h, w, h,
-                                         PIXEL_FORMAT_YCbCr_420_SP,
-                                         transform,
-                                         0,
-                                         mHardware->getPreviewHeap());
-
-            mSurface->registerBuffers(buffers);
-        } else {
-          LOGE("mHardware->startPreview() failed with status %d", ret);
+        if (ret != NO_ERROR) return ret;
+        // If preview display has been set, register preview buffers now.
+        if (mSurface != 0) {
+           // Unregister here because the surface registered with raw heap.
+           mSurface->unregisterBuffers();
+           ret = registerPreviewBuffers();
         }
     }
     return ret;
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
index 8b8b54c..0f07673 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -157,6 +157,8 @@
         status_t                startCameraMode(camera_mode mode);
         status_t                startPreviewMode();
         status_t                startRecordingMode();
+        status_t                setOverlay();
+        status_t                registerPreviewBuffers();
 
         // Ensures atomicity among the public methods
         mutable     Mutex                       mLock;
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index c3ddd20..c90b862 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -62,6 +62,16 @@
         String op = args[0];
         mNextArg = 1;
 
+        if ("enabled".equals(op)) {
+            doEnabled();
+            return;
+        }
+
+        if ("enable".equals(op)) {
+            doEnable();
+            return;
+        }
+
         if ("run".equals(op)) {
             doRun();
             return;
@@ -91,6 +101,41 @@
         showUsage();
     }
 
+    private String enableToString(boolean enabled) {
+        return enabled ? "enabled" : "disabled";
+    }
+
+    private void doEnabled() {
+        try {
+            boolean isEnabled = mBmgr.isBackupEnabled();
+            System.out.println("Backup Manager currently "
+                    + enableToString(isEnabled));
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(BMGR_NOT_RUNNING_ERR);
+        }
+    }
+
+    private void doEnable() {
+        String arg = nextArg();
+        if (arg == null) {
+            showUsage();
+            return;
+        }
+
+        try {
+            boolean enable = Boolean.parseBoolean(arg);
+            mBmgr.setBackupEnabled(enable);
+            System.out.println("Backup Manager now " + enableToString(enable));
+        } catch (NumberFormatException e) {
+            showUsage();
+            return;
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(BMGR_NOT_RUNNING_ERR);
+        }
+    }
+
     private void doRun() {
         try {
             mBmgr.backupNow();
@@ -291,6 +336,8 @@
     private static void showUsage() {
         System.err.println("usage: bmgr [backup|restore|list|transport|run]");
         System.err.println("       bmgr backup PACKAGE");
+        System.err.println("       bmgr enable BOOL");
+        System.err.println("       bmgr enabled");
         System.err.println("       bmgr list transports");
         System.err.println("       bmgr list sets");
         System.err.println("       bmgr transport WHICH");
@@ -301,6 +348,14 @@
         System.err.println("Note that the backup pass will effectively be a no-op if the package");
         System.err.println("does not actually have changed data to store.");
         System.err.println("");
+        System.err.println("The 'enable' command enables or disables the entire backup mechanism.");
+        System.err.println("If the argument is 'true' it will be enabled, otherwise it will be");
+        System.err.println("disabled.  When disabled, neither backup or restore operations will");
+        System.err.println("be performed.");
+        System.err.println("");
+        System.err.println("The 'enabled' command reports the current enabled/disabled state of");
+        System.err.println("the backup mechanism.");
+        System.err.println("");
         System.err.println("The 'list transports' command reports the names of the backup transports");
         System.err.println("currently available on the device.  These names can be passed as arguments");
         System.err.println("to the 'transport' command.  The currently selected transport is indicated");
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 62dc651..5ee29ac 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3981,7 +3981,10 @@
             ProviderRecord pr = mProviderMap.get(name);
             if (pr.mProvider.asBinder() == provider.asBinder()) {
                 Log.i(TAG, "Removing dead content provider: " + name);
-                mProviderMap.remove(name);
+                ProviderRecord removed = mProviderMap.remove(name);
+                if (removed != null) {
+                    removed.mProvider.asBinder().unlinkToDeath(removed, 0);
+                }
             }
         }
     }
@@ -3990,7 +3993,10 @@
         ProviderRecord pr = mProviderMap.get(name);
         if (pr.mProvider.asBinder() == provider.asBinder()) {
             Log.i(TAG, "Removing dead content provider: " + name);
-            mProviderMap.remove(name);
+            ProviderRecord removed = mProviderMap.remove(name);
+            if (removed != null) {
+                removed.mProvider.asBinder().unlinkToDeath(removed, 0);
+            }
         }
     }
 
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 6fe4896..0785029 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -34,6 +34,7 @@
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Animatable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.SystemClock;
@@ -241,8 +242,15 @@
         // Reset any stored values from last time dialog was shown.
         mStoredComponentName = null;
         mStoredAppSearchData = null;
-        
-        return doShow(initialQuery, selectInitialQuery, componentName, appSearchData, globalSearch);
+
+        boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData,
+                globalSearch);
+        if (success) {
+            // Display the drop down as soon as possible instead of waiting for the rest of the
+            // pending UI stuff to get done, so that things appear faster to the user.
+            mSearchAutoComplete.showDropDownAfterLayout();
+        }
+        return success;
     }
     
     /**
@@ -422,11 +430,11 @@
         if (working) {
             mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
                     null, null, mWorkingSpinner, null);
-//            mWorkingSpinner.start();
+            ((Animatable) mWorkingSpinner).start();
         } else {
             mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
                     null, null, null, null);
-//            mWorkingSpinner.stop();
+            ((Animatable) mWorkingSpinner).stop();
         }
     }
     
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 10c2b02..03e8623 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -26,7 +26,6 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 
 import com.android.internal.appwidget.IAppWidgetHost;
 import com.android.internal.appwidget.IAppWidgetService;
@@ -40,7 +39,7 @@
     static final int HANDLE_UPDATE = 1;
     static final int HANDLE_PROVIDER_CHANGED = 2;
 
-    static Object sServiceLock = new Object();
+    final static Object sServiceLock = new Object();
     static IAppWidgetService sService;
 
     Context mContext;
@@ -85,7 +84,7 @@
 
     int mHostId;
     Callbacks mCallbacks = new Callbacks();
-    HashMap<Integer,AppWidgetHostView> mViews = new HashMap();
+    final HashMap<Integer,AppWidgetHostView> mViews = new HashMap<Integer, AppWidgetHostView>();
 
     public AppWidgetHost(Context context, int hostId) {
         mContext = context;
@@ -104,8 +103,8 @@
      * becomes visible, i.e. from onStart() in your Activity.
      */
     public void startListening() {
-        int[] updatedIds = null;
-        ArrayList<RemoteViews> updatedViews = new ArrayList();
+        int[] updatedIds;
+        ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>();
         
         try {
             if (mPackageName == null) {
@@ -209,7 +208,7 @@
         synchronized (mViews) {
             mViews.put(appWidgetId, view);
         }
-        RemoteViews views = null;
+        RemoteViews views;
         try {
             views = sService.getAppWidgetViews(appWidgetId);
         } catch (RemoteException e) {
@@ -231,6 +230,7 @@
     /**
      * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk.
      */
+    @SuppressWarnings({"UnusedDeclaration"})
     protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
     }
 
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index be0f96e..307c4c8 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -22,16 +22,12 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
-import android.os.Handler;
-import android.os.Message;
 import android.os.SystemClock;
-import android.util.Config;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.animation.Animation;
 import android.widget.FrameLayout;
 import android.widget.RemoteViews;
 import android.widget.TextView;
@@ -86,6 +82,7 @@
      * @param animationIn Resource ID of in animation to use
      * @param animationOut Resource ID of out animation to use
      */
+    @SuppressWarnings({"UnusedDeclaration"})
     public AppWidgetHostView(Context context, int animationIn, int animationOut) {
         super(context);
         mContext = context;
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index eca04b3..3660001 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -21,7 +21,9 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.TypedValue;
 import android.widget.RemoteViews;
 
 import com.android.internal.appwidget.IAppWidgetService;
@@ -187,6 +189,8 @@
     
     Context mContext;
 
+    private DisplayMetrics mDisplayMetrics;
+
     /**
      * Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
      * Context} object.
@@ -213,6 +217,7 @@
 
     private AppWidgetManager(Context context) {
         mContext = context;
+        mDisplayMetrics = context.getResources().getDisplayMetrics();
     }
 
     /**
@@ -292,7 +297,15 @@
      */
     public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
         try {
-            return sService.getAppWidgetInfo(appWidgetId);
+            AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId);
+            if (info != null) {
+                // Converting complex to dp.
+                info.minWidth = 
+                        TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
+                info.minHeight =
+                        TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
+            }
+            return info;
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl
index 39e160b..1f11762 100644
--- a/core/java/android/backup/IBackupManager.aidl
+++ b/core/java/android/backup/IBackupManager.aidl
@@ -48,6 +48,24 @@
     void agentDisconnected(String packageName);
 
     /**
+     * Enable/disable the backup service entirely.  When disabled, no backup
+     * or restore operations will take place.  Data-changed notifications will
+     * still be observed and collected, however, so that changes made while the
+     * mechanism was disabled will still be backed up properly if it is enabled
+     * at some point in the future.
+     *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     */
+    void setBackupEnabled(boolean isEnabled);
+
+    /**
+     * Report whether the backup mechanism is currently enabled.
+     *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     */
+    boolean isBackupEnabled();
+
+    /**
      * Schedule an immediate backup attempt for all pending updates.  This is
      * primarily intended for transports to use when they detect a suitable
      * opportunity for doing a backup pass.  If there are no pending updates to
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6fe5506..263f927 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1085,6 +1085,27 @@
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY";
     
+    /**
+     * Activity Action: Setup wizard to launch after a platform update.  This
+     * activity should have a string meta-data field associated with it,
+     * {@link #METADATA_SETUP_VERSION}, which defines the current version of
+     * the platform for setup.  The activity will be launched only if
+     * {@link android.provider.Settings.Secure#LAST_SETUP_SHOWN} is not the
+     * same value.
+     * <p>Input: Nothing.
+     * <p>Output: Nothing.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
+    
+    /**
+     * A string associated with a {@link #ACTION_UPGRADE_SETUP} activity
+     * describing the last run version of the platform that was setup.
+     * @hide
+     */
+    public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent broadcast actions (see action variable).
@@ -1290,6 +1311,13 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW";
     /**
+     * Broadcast Action:  Indicates the battery is now okay after being low.
+     * This will be sent after {@link #ACTION_BATTERY_LOW} once the battery has
+     * gone back up to an okay state.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_BATTERY_OKAY = "android.intent.action.BATTERY_OKAY";
+    /**
      * Broadcast Action:  External power has been connected to the device.
      * This is intended for applications that wish to register specifically to this notification.
      * Unlike ACTION_BATTERY_CHANGED, applications will be woken for this and so do not have to
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 09fbc97..3ce951f 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -155,7 +155,11 @@
      * @throws IOException if the method fails.
      */
     public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
-        setPreviewDisplay(holder.getSurface());
+        if (holder != null) {
+            setPreviewDisplay(holder.getSurface());
+        } else {
+            setPreviewDisplay((Surface)null);
+        }
     }
 
     private native final void setPreviewDisplay(Surface surface);
diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java
index c4ee5b0..6a97951 100644
--- a/core/java/android/net/http/RequestHandle.java
+++ b/core/java/android/net/http/RequestHandle.java
@@ -159,11 +159,11 @@
             e.printStackTrace();
         }
 
-        // update the "cookie" header based on the redirected url
-        mHeaders.remove("cookie");
+        // update the "Cookie" header based on the redirected url
+        mHeaders.remove("Cookie");
         String cookie = CookieManager.getInstance().getCookie(mUri);
         if (cookie != null && cookie.length() > 0) {
-            mHeaders.put("cookie", cookie);
+            mHeaders.put("Cookie", cookie);
         }
 
         if ((statusCode == 302 || statusCode == 303) && mMethod.equals("POST")) {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 51d1951..bc7b5be 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -344,7 +344,10 @@
                 // Check if file exists with a FileInputStream
                 FileInputStream stream = new FileInputStream(imagePath);
                 try {
-                    return insertImage(cr, BitmapFactory.decodeFile(imagePath), name, description);
+                    Bitmap bm = BitmapFactory.decodeFile(imagePath);
+                    String ret = insertImage(cr, bm, name, description);
+                    bm.recycle();
+                    return ret;
                 } finally {
                     try {
                         stream.close();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7356326..4a40058 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2226,6 +2226,26 @@
         public static final String USE_LOCATION_FOR_SERVICES = "use_location";
 
         /**
+         * Controls whether data backup is enabled.
+         * Type: int ( 0 = disabled, 1 = enabled )
+         * @hide
+         */
+        public static final String BACKUP_ENABLED = "backup_enabled";
+
+        /**
+         * Component of the transport to use for backup/restore.
+         * @hide
+         */
+        public static final String BACKUP_TRANSPORT = "backup_transport";
+        
+        /**
+         * Version for which the setup wizard was last shown.  Bumped for
+         * each release when there is new setup information to show.
+         * @hide
+         */
+        public static final String LAST_SETUP_SHOWN = "last_setup_shown";
+
+        /**
          * Helper method for determining if a location provider is enabled.
          * @param cr the content resolver to use
          * @param provider the location provider to query
@@ -2828,6 +2848,13 @@
                 "vending_heartbeat_frequency_ms";
 
         /**
+         * Frequency in milliseconds at which we should resend pending download
+         * requests to the API Server from the Vending Machine client.
+         */
+        public static final String VENDING_PENDING_DOWNLOAD_RESEND_FREQUENCY_MS =
+                "vending_pd_resend_frequency_ms";
+
+        /**
          * URL that points to the legal terms of service to display in Settings.
          * <p>
          * This should be a https URL. For a pretty user-friendly URL, use
diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java
index 90dfa0b..8ef1f15 100644
--- a/core/java/android/server/search/SearchableInfo.java
+++ b/core/java/android/server/search/SearchableInfo.java
@@ -67,6 +67,7 @@
     private final int mSearchImeOptions;
     private final boolean mIncludeInGlobalSearch;
     private final boolean mQueryAfterZeroResults;
+    private final String mSettingsDescription;
     private final String mSuggestAuthority;
     private final String mSuggestPath;
     private final String mSuggestSelection;
@@ -134,6 +135,14 @@
     public boolean shouldRewriteQueryFromText() {
         return 0 != (mSearchMode & SEARCH_MODE_QUERY_REWRITE_FROM_TEXT);
     }
+    
+    /**
+     * Gets the description to use for this source in system search settings, or null if
+     * none has been specified.
+     */
+    public String getSettingsDescription() {
+        return mSettingsDescription;
+    }
 
     /**
      * Retrieve the path for obtaining search suggestions.
@@ -280,6 +289,8 @@
         mQueryAfterZeroResults = a.getBoolean(
                 com.android.internal.R.styleable.Searchable_queryAfterZeroResults, false);
 
+        mSettingsDescription = a.getString(
+                com.android.internal.R.styleable.Searchable_searchSettingsDescription);
         mSuggestAuthority = a.getString(
                 com.android.internal.R.styleable.Searchable_searchSuggestAuthority);
         mSuggestPath = a.getString(
@@ -448,6 +459,7 @@
                         + ",suggestAuthority=" + searchable.getSuggestAuthority()
                         + ",target=" + searchable.getSearchActivity().getClassName()
                         + ",global=" + searchable.shouldIncludeInGlobalSearch()
+                        + ",settingsDescription=" + searchable.getSettingsDescription()
                         + ",threshold=" + searchable.getSuggestThreshold());
             } else {
                 Log.d(LOG_TAG, "Checked " + activityInfo.name + ", no searchable meta-data");
@@ -686,7 +698,8 @@
         mSearchImeOptions = in.readInt();
         mIncludeInGlobalSearch = in.readInt() != 0;
         mQueryAfterZeroResults = in.readInt() != 0;
-
+        
+        mSettingsDescription = in.readString();
         mSuggestAuthority = in.readString();
         mSuggestPath = in.readString();
         mSuggestSelection = in.readString();
@@ -723,6 +736,7 @@
         dest.writeInt(mIncludeInGlobalSearch ? 1 : 0);
         dest.writeInt(mQueryAfterZeroResults ? 1 : 0);
         
+        dest.writeString(mSettingsDescription);
         dest.writeString(mSuggestAuthority);
         dest.writeString(mSuggestPath);
         dest.writeString(mSuggestSelection);
diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl
index 47976e5..15f3876 100755
--- a/core/java/android/speech/tts/ITts.aidl
+++ b/core/java/android/speech/tts/ITts.aidl
@@ -43,6 +43,10 @@
 

     void addSpeechFile(in String text, in String filename);

 

+    String[] getLanguage();

+

+    int isLanguageAvailable(in String language, in String country, in String variant);

+

     void setLanguage(in String language, in String country, in String variant);

 

     boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory);
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 8fa06fa..b245713 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -251,14 +251,17 @@
      *
      * @param resourceId
      *            Example: <b><code>R.raw.south_south_east</code></b>
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void addSpeech(String text, String packagename, int resourceId) {
+    public int addSpeech(String text, String packagename, int resourceId) {
         synchronized(mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 mITts.addSpeech(text, packagename, resourceId);
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -272,6 +275,7 @@
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
@@ -285,14 +289,17 @@
      * @param filename
      *            The full path to the sound file (for example:
      *            "/sdcard/mysounds/hello.wav")
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void addSpeech(String text, String filename) {
+    public int addSpeech(String text, String filename) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 mITts.addSpeechFile(text, filename);
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -306,6 +313,7 @@
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
@@ -324,17 +332,20 @@
      *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
      * @param params
      *            The hashmap of speech parameters to be used.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void speak(String text, int queueMode, HashMap<String,String> params)
+    public int speak(String text, int queueMode, HashMap<String,String> params)
     {
         synchronized (mStartLock) {
             Log.i("TTS received: ", text);
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 // TODO support extra parameters, passing cache of current parameters for the moment
                 mITts.speak(text, queueMode, mCachedParams);
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -348,6 +359,7 @@
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
@@ -366,17 +378,22 @@
      *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
      * @param params
      *            The hashmap of speech parameters to be used.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
+     *
+     * {@hide}
      */
-    public void speakIpa(String ipaText, int queueMode, HashMap<String,String> params)
+    public int speakIpa(String ipaText, int queueMode, HashMap<String,String> params)
     {
         synchronized (mStartLock) {
             Log.i("TTS received: ", ipaText);
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 // TODO support extra parameters, passing cache of current parameters for the moment
                 mITts.speakIpa(ipaText, queueMode, mCachedParams);
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -390,6 +407,7 @@
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
@@ -403,16 +421,19 @@
      *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
      * @param params
      *            The hashmap of parameters to be used.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void playEarcon(String earcon, int queueMode,
+    public int playEarcon(String earcon, int queueMode,
             HashMap<String,String> params) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 // TODO support extra parameters, passing null for the moment
                 mITts.playEarcon(earcon, queueMode, null);
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -426,18 +447,30 @@
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
-
-    public void playSilence(long durationInMs, int queueMode) {
+    /**
+     * Plays silence for the specified amount of time using the specified
+     * queue mode.
+     *
+     * @param durationInMs
+     *            A long that indicates how long the silence should last.
+     * @param queueMode
+     *            See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
+     */
+    public int playSilence(long durationInMs, int queueMode) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 // TODO support extra parameters, passing cache of current parameters for the moment
                 mITts.playSilence(durationInMs, queueMode, mCachedParams);
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -451,6 +484,7 @@
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
@@ -487,14 +521,17 @@
 
     /**
      * Stops speech from the TTS.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void stop() {
+    public int stop() {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 mITts.stop();
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -508,6 +545,7 @@
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
@@ -524,23 +562,27 @@
      *            The speech rate for the TTS engine. 1 is the normal speed,
      *            lower values slow down the speech (0.5 is half the normal speech rate),
      *            greater values accelerate it (2 is twice the normal speech rate).
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void setSpeechRate(float speechRate) {
+    public int setSpeechRate(float speechRate) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_SUCCESS;
             }
             try {
                 if (speechRate > 0) {
                     mCachedRate = (int)(speechRate*100);
                     updateCachedParamArray();
                     mITts.setSpeechRate(mCachedRate);
+                    return TTS_SUCCESS;
                 }
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
@@ -557,21 +599,25 @@
      *            The pitch for the TTS engine. 1 is the normal pitch,
      *            lower values lower the tone of the synthesized voice,
      *            greater values increase it.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void setPitch(float pitch) {
+    public int setPitch(float pitch) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 if (pitch > 0) {
                     mITts.setPitch((int)(pitch*100));
+                    return TTS_SUCCESS;
                 }
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
         }
     }
 
@@ -585,11 +631,13 @@
      *
      * @param loc
      *            The locale describing the language to be used.
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public void setLanguage(Locale loc) {
+    public int setLanguage(Locale loc) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return;
+                return TTS_ERROR;
             }
             try {
                 mCachedLang = loc.getISO3Language();
@@ -597,29 +645,70 @@
                 mCachedVariant = loc.getVariant();
                 updateCachedParamArray();
                 mITts.setLanguage(mCachedLang, mCachedCountry, mCachedVariant);
+                return TTS_SUCCESS;
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
                 initTts();
             }
+            return TTS_ERROR;
+        }
+    }
+
+
+    /**
+     * Returns a Locale instance describing the language currently being used by the TTS engine.
+     * @return language, country (if any) and variant (if any) used by the engine stored in a Locale
+     *     instance, or null is the TTS engine has failed.
+     */
+    public Locale getLanguage() {
+        synchronized (mStartLock) {
+            if (!mStarted) {
+                return null;
+            }
+            try {
+                String[] locStrings =  mITts.getLanguage();
+                if (locStrings.length == 3) {
+                    return new Locale(locStrings[0], locStrings[1], locStrings[2]);
+                } else {
+                    return null;
+                }
+            } catch (RemoteException e) {
+                // TTS died; restart it.
+                mStarted = false;
+                initTts();
+            }
+            return null;
         }
     }
 
     /**
-     * Checks if the specified language as represented by the locale is available.
+     * Checks if the specified language as represented by the Locale is available.
      *
      * @param loc
-     *            The locale describing the language to be used.
+     *            The Locale describing the language to be used.
+     *
      * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
-               TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE.
+     *         TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE.
      */
     public int isLanguageAvailable(Locale loc) {
-        //TODO: Implement isLanguageAvailable
-        return TTS_LANG_NOT_SUPPORTED;
+        synchronized (mStartLock) {
+            if (!mStarted) {
+                return TTS_LANG_NOT_SUPPORTED;
+            }
+            try {
+                return mITts.isLanguageAvailable(loc.getISO3Language(), loc.getISO3Country(),
+                        loc.getVariant());
+            } catch (RemoteException e) {
+                // TTS died; restart it.
+                mStarted = false;
+                initTts();
+            }
+            return TTS_LANG_NOT_SUPPORTED;
+        }
     }
 
 
-
     /**
      * Synthesizes the given text to a file using the specified parameters.
      *
@@ -630,17 +719,20 @@
      * @param filename
      *            The string that gives the full output filename; it should be
      *            something like "/sdcard/myappsounds/mysound.wav".
-     * @return A boolean that indicates if the synthesis succeeded
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
      */
-    public boolean synthesizeToFile(String text, HashMap<String,String> params,
+    public int synthesizeToFile(String text, HashMap<String,String> params,
             String filename) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return false;
+                return TTS_ERROR;
             }
             try {
                 // TODO support extra parameters, passing null for the moment
-                return mITts.synthesizeToFile(text, null, filename);
+                if (mITts.synthesizeToFile(text, null, filename)){
+                    return TTS_SUCCESS;
+                }
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -654,10 +746,11 @@
                 mStarted = false;
                 initTts();
             }
-            return false;
+            return TTS_ERROR;
         }
     }
 
+
     /**
      * Synthesizes the given IPA text to a file using the specified parameters.
      *
@@ -668,17 +761,22 @@
      * @param filename
      *            The string that gives the full output filename; it should be
      *            something like "/sdcard/myappsounds/mysound.wav".
-     * @return A boolean that indicates if the synthesis succeeded
+     *
+     * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
+     *
+     * {@hide}
      */
-    public boolean synthesizeIpaToFile(String ipaText,
+    public int synthesizeIpaToFile(String ipaText,
             HashMap<String,String> params, String filename) {
         synchronized (mStartLock) {
             if (!mStarted) {
-                return false;
+                return TTS_ERROR;
             }
             try {
                 // TODO support extra parameters, passing null for the moment
-                return mITts.synthesizeIpaToFile(ipaText, null, filename);
+                if (mITts.synthesizeIpaToFile(ipaText, null, filename)){
+                    return TTS_SUCCESS;
+                }
             } catch (RemoteException e) {
                 // TTS died; restart it.
                 mStarted = false;
@@ -692,7 +790,7 @@
                 mStarted = false;
                 initTts();
             }
-            return false;
+            return TTS_ERROR;
         }
     }
 
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index f2fb9cb..dfc16f5 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -81,6 +81,12 @@
 
         switch (event.getAction()) {
         case MotionEvent.ACTION_DOWN:
+            ds = buffer.getSpans(0, buffer.length(), DragState.class);
+
+            for (int i = 0; i < ds.length; i++) {
+                buffer.removeSpan(ds[i]);
+            }
+
             buffer.setSpan(new DragState(event.getX(), event.getY(),
                             widget.getScrollX(), widget.getScrollY()),
                     0, 0, Spannable.SPAN_MARK_MARK);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c9a785c..b3180ca 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5922,8 +5922,9 @@
             int height = mBottom - mTop;
 
             final AttachInfo attachInfo = mAttachInfo;
+            final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;
 
-            if (autoScale && attachInfo != null && attachInfo.mScalingRequired) {
+            if (autoScale && scalingRequired) {
                 width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
                 height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
             }
@@ -6014,7 +6015,7 @@
             computeScroll();
             final int restoreCount = canvas.save();
             
-            if (autoScale && attachInfo.mScalingRequired) {
+            if (autoScale && scalingRequired) {
                 final float scale = attachInfo.mApplicationScale;
                 canvas.scale(scale, scale);
             }
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 9a0f467..7393737 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -340,10 +340,19 @@
     }
 
     /**
-     * The default implementation does nothing.
+     * The default implementation turns this into the enter key.
      */
     public boolean performEditorAction(int actionCode) {
-        return false;
+        long eventTime = SystemClock.uptimeMillis();
+        sendKeyEvent(new KeyEvent(eventTime, eventTime,
+                KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0,
+                KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
+                | KeyEvent.FLAG_EDITOR_ACTION));
+        sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
+                KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0,
+                KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
+                | KeyEvent.FLAG_EDITOR_ACTION));
+        return true;
     }
 
     /**
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 6f1b160..66ab021 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -364,7 +364,7 @@
         String cookie = CookieManager.getInstance().getCookie(
                 mListener.getWebAddress());
         if (cookie != null && cookie.length() > 0) {
-            mHeaders.put("cookie", cookie);
+            mHeaders.put("Cookie", cookie);
         }
     }
 }
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index e84e5b0..2182384 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -124,6 +124,7 @@
     private boolean mBlockCompletion;
 
     private AutoCompleteTextView.ListSelectorHider mHideSelector;
+    private Runnable mShowDropDownRunnable;
 
     private AutoCompleteTextView.PassThroughClickListener mPassThroughClickListener;
 
@@ -1080,6 +1081,15 @@
     }
 
     /**
+     * Issues a runnable to show the dropdown as soon as possible.
+     *
+     * @hide internal used only by Search Dialog
+     */
+    public void showDropDownAfterLayout() {
+        post(mShowDropDownRunnable);
+    }
+
+    /**
      * <p>Displays the drop down on screen.</p>
      */
     public void showDropDown() {
@@ -1190,6 +1200,22 @@
 
             mHideSelector = new ListSelectorHider();
 
+            /**
+             * This Runnable exists for the sole purpose of checking if the view layout has got
+             * completed and if so call showDropDown to display the drop down. This is used to show
+             * the drop down as soon as possible after user opens up the search dialog, without
+             * waiting for the normal UI pipeline to do it's job which is slower than this method.
+             */
+            mShowDropDownRunnable = new Runnable() {
+                public void run() {
+                    // View layout should be all done before displaying the drop down.
+                    View view = getDropDownAnchorView();
+                    if (view != null && view.getWindowToken() != null) {
+                        showDropDown();
+                    }
+                }
+            };
+
             mDropDownList = new DropDownListView(context);
             mDropDownList.setSelector(mDropDownListHighlight);
             mDropDownList.setAdapter(mAdapter);
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 441414a..2c9e71e 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -30,6 +30,7 @@
 import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.StateListDrawable;
+import android.graphics.drawable.Animatable;
 import android.graphics.drawable.shapes.RoundRectShape;
 import android.graphics.drawable.shapes.Shape;
 import android.util.AttributeSet;
@@ -683,7 +684,7 @@
             return;
         }
 
-        if (mIndeterminateDrawable instanceof AnimationDrawable) {
+        if (mIndeterminateDrawable instanceof Animatable) {
             mShouldStartAnimationDrawable = true;
             mAnimation = null;
         } else {
@@ -708,8 +709,8 @@
     void stopAnimation() {
         mAnimation = null;
         mTransformation = null;
-        if (mIndeterminateDrawable instanceof AnimationDrawable) {
-            ((AnimationDrawable) mIndeterminateDrawable).stop();
+        if (mIndeterminateDrawable instanceof Animatable) {
+            ((Animatable) mIndeterminateDrawable).stop();
             mShouldStartAnimationDrawable = false;
         }
     }
@@ -818,8 +819,8 @@
             }
             d.draw(canvas);
             canvas.restore();
-            if (mShouldStartAnimationDrawable && d instanceof AnimationDrawable) {
-                ((AnimationDrawable) d).start();
+            if (mShouldStartAnimationDrawable && d instanceof Animatable) {
+                ((Animatable) d).start();
                 mShouldStartAnimationDrawable = false;
             }
         }
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 12bb01c..e62dda5 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -558,9 +558,9 @@
                 myWidth);
         int childHeightMeasureSpec;
         if (params.width == LayoutParams.FILL_PARENT) {
-            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY, myHeight);
+            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY);
         } else {
-            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST);
         }
         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
     }
@@ -1403,7 +1403,9 @@
             /*
              * START POOL IMPLEMENTATION
              */
-            private static final int POOL_LIMIT = 12;
+            // The pool is static, so all nodes instances are shared across
+            // activities, that's why we give it a rather high limit
+            private static final int POOL_LIMIT = 100;
             private static final Pool<Node> sPool = Pools.synchronizedPool(
                     Pools.finitePool(new PoolableManager<Node>() {
                         public Node newInstance() {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7936f65..1b5c5bc 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -20,10 +20,8 @@
 import android.app.PendingIntent.CanceledException;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.PorterDuff;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Parcel;
@@ -36,15 +34,12 @@
 import android.view.ViewGroup;
 import android.view.LayoutInflater.Filter;
 import android.view.View.OnClickListener;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
 
 import java.lang.Class;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 
@@ -69,13 +64,7 @@
      * The resource ID of the layout file. (Added to the parcel)
      */
     private int mLayoutId;
-    
-    /**
-     * The Context object used to inflate the layout file. Also may
-     * be used by actions if they need access to the senders resources.
-     */
-    private Context mContext;
-    
+
     /**
      * An array of actions to perform on the view tree once it has been
      * inflated
@@ -85,7 +74,7 @@
     
     /**
      * This annotation indicates that a subclass of View is alllowed to be used
-     * with the {@link android.widget.RemoteViews} mechanism.
+     * with the {@link RemoteViews} mechanism.
      */
     @Target({ ElementType.TYPE })
     @Retention(RetentionPolicy.RUNTIME)
@@ -116,7 +105,7 @@
         public int describeContents() {
             return 0;
         }
-    };
+    }
 
     /**
      * Equivalent to calling
@@ -232,15 +221,17 @@
                 targetDrawable = imageView.getDrawable();
             }
             
-            // Perform modifications only if values are set correctly
-            if (alpha != -1) {
-                targetDrawable.setAlpha(alpha);
-            }
-            if (colorFilter != -1 && filterMode != null) {
-                targetDrawable.setColorFilter(colorFilter, filterMode);
-            }
-            if (level != -1) {
-                targetDrawable.setLevel(level);
+            if (targetDrawable != null) {
+                // Perform modifications only if values are set correctly
+                if (alpha != -1) {
+                    targetDrawable.setAlpha(alpha);
+                }
+                if (colorFilter != -1 && filterMode != null) {
+                    targetDrawable.setColorFilter(colorFilter, filterMode);
+                }
+                if (level != -1) {
+                    targetDrawable.setLevel(level);
+                }
             }
         }
         
@@ -289,6 +280,7 @@
             this.viewId = in.readInt();
             this.methodName = in.readString();
             this.type = in.readInt();
+            //noinspection ConstantIfStatement
             if (false) {
                 Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId)
                         + " methodName=" + this.methodName + " type=" + this.type);
@@ -340,31 +332,32 @@
             out.writeInt(this.viewId);
             out.writeString(this.methodName);
             out.writeInt(this.type);
+            //noinspection ConstantIfStatement
             if (false) {
                 Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId)
                         + " methodName=" + this.methodName + " type=" + this.type);
             }
             switch (this.type) {
                 case BOOLEAN:
-                    out.writeInt(((Boolean)this.value).booleanValue() ? 1 : 0);
+                    out.writeInt((Boolean) this.value ? 1 : 0);
                     break;
                 case BYTE:
-                    out.writeByte(((Byte)this.value).byteValue());
+                    out.writeByte((Byte) this.value);
                     break;
                 case SHORT:
-                    out.writeInt(((Short)this.value).shortValue());
+                    out.writeInt((Short) this.value);
                     break;
                 case INT:
-                    out.writeInt(((Integer)this.value).intValue());
+                    out.writeInt((Integer) this.value);
                     break;
                 case LONG:
-                    out.writeLong(((Long)this.value).longValue());
+                    out.writeLong((Long) this.value);
                     break;
                 case FLOAT:
-                    out.writeFloat(((Float)this.value).floatValue());
+                    out.writeFloat((Float) this.value);
                     break;
                 case DOUBLE:
-                    out.writeDouble(((Double)this.value).doubleValue());
+                    out.writeDouble((Double) this.value);
                     break;
                 case CHAR:
                     out.writeInt((int)((Character)this.value).charValue());
@@ -430,7 +423,7 @@
             }
 
             Class klass = view.getClass();
-            Method method = null;
+            Method method;
             try {
                 method = klass.getMethod(this.methodName, getParameterType());
             }
@@ -446,6 +439,7 @@
             }
 
             try {
+                //noinspection ConstantIfStatement
                 if (false) {
                     Log.d("RemoteViews", "view: " + klass.getName() + " calling method: "
                         + this.methodName + "(" + param.getName() + ") with "
@@ -816,13 +810,12 @@
      * @return The inflated view hierarchy
      */
     public View apply(Context context, ViewGroup parent) {
-        View result = null;
+        View result;
 
         Context c = prepareContext(context);
 
-        Resources r = c.getResources();
-        LayoutInflater inflater = (LayoutInflater) c
-                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        LayoutInflater inflater = (LayoutInflater)
+                c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
         inflater = inflater.cloneInContext(c);
         inflater.setFilter(this);
@@ -858,7 +851,7 @@
     }
 
     private Context prepareContext(Context context) {
-        Context c = null;
+        Context c;
         String packageName = mPackage;
 
         if (packageName != null) {
@@ -872,8 +865,6 @@
             c = context;
         }
 
-        mContext = c;
-
         return c;
     }
 
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 8e48b38..3dcc09f 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -171,6 +171,10 @@
     // VM pointer will be NULL if object is released
     Mutex::Autolock _l(mLock);
     JNIEnv *env = AndroidRuntime::getJNIEnv();
+    if (mCameraJObjectWeak == NULL) {
+        LOGW("callback on dead camera object");
+        return;
+    }
 
     // return data based on callback type
     switch(msgType) {
@@ -262,7 +266,10 @@
     sp<Camera> camera = get_native_camera(env, thiz, NULL);
     if (camera == 0) return;
 
-    sp<Surface> surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface));
+    sp<Surface> surface = NULL;
+    if (jSurface != NULL) {
+        surface = reinterpret_cast<Surface*>(env->GetIntField(jSurface, fields.surface));
+    }
     if (camera->setPreviewDisplay(surface) != NO_ERROR) {
         jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed");
     }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7f24dcc..599360f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -973,7 +973,7 @@
     <permission android:name="android.permission.BACKUP"
         android:label="@string/permlab_backup"
         android:description="@string/permdesc_backup"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signatureOrSystem" />
 
     <!-- Allows an application to tell the AppWidget service which application
          can access AppWidget's data.  The normal user flow is that a user
diff --git a/core/res/res/drawable/progress_large.xml b/core/res/res/drawable/progress_large.xml
index 4669104..4f016bc 100644
--- a/core/res/res/drawable/progress_large.xml
+++ b/core/res/res/drawable/progress_large.xml
@@ -1,45 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-  
-          http://www.apache.org/licenses/LICENSE-2.0
-  
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
 -->
-
-    
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
-        android:pivotX="50%" android:pivotY="50%"
-        android:fromDegrees="0" android:toDegrees="360">
-        
-    <shape
-            android:shape="ring"
-            android:innerRadiusRatio="3"
-            android:thicknessRatio="8"
-            android:useLevel="false">
-
-        <size
-                android:width="76dip"
-                android:height="76dip"
-        />
-        
-        <gradient
-                android:type="sweep"
-                android:useLevel="false"
-                android:startColor="#4c737373"
-                android:centerColor="#4c737373"
-                android:centerY="0.50"
-                android:endColor="#ffffd300"
-        />
-        
-    </shape>
-    
-</rotate>
-    
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_black_76"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_large_white.xml b/core/res/res/drawable/progress_large_white.xml
new file mode 100644
index 0000000..c690ed4
--- /dev/null
+++ b/core/res/res/drawable/progress_large_white.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_white_76"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_medium.xml b/core/res/res/drawable/progress_medium.xml
index 92aebb5..eb1bd50 100644
--- a/core/res/res/drawable/progress_medium.xml
+++ b/core/res/res/drawable/progress_medium.xml
@@ -1,43 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-  
-          http://www.apache.org/licenses/LICENSE-2.0
-  
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
 -->
-
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
-        android:pivotX="50%" android:pivotY="50%"
-        android:fromDegrees="0" android:toDegrees="360">
-        
-    <shape
-            android:shape="ring"
-            android:innerRadiusRatio="3"
-            android:thicknessRatio="8"
-            android:useLevel="false">
-
-        <size
-                android:width="48dip"
-                android:height="48dip"
-        />
-        
-        <gradient
-                android:type="sweep"
-                android:useLevel="false"
-                android:startColor="#4c737373"
-                android:centerColor="#4c737373"
-                android:centerY="0.50"
-                android:endColor="#ffffd300"
-        />
-        
-    </shape>
-    
-</rotate>
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_black_48"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_medium_white.xml b/core/res/res/drawable/progress_medium_white.xml
new file mode 100644
index 0000000..b4f9b31
--- /dev/null
+++ b/core/res/res/drawable/progress_medium_white.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_white_48"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_small.xml b/core/res/res/drawable/progress_small.xml
index e5b0021..e0ee5e4 100644
--- a/core/res/res/drawable/progress_small.xml
+++ b/core/res/res/drawable/progress_small.xml
@@ -1,45 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-  
-          http://www.apache.org/licenses/LICENSE-2.0
-  
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
 -->
-
- 
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
-        android:pivotX="50%" android:pivotY="50%"
-        android:fromDegrees="0" android:toDegrees="360">
-
-    <!-- An extra pixel is added on both ratios for stroke -->        
-    <shape
-            android:shape="ring"
-            android:innerRadiusRatio="3.2"
-            android:thicknessRatio="5.333"
-            android:useLevel="false">
-
-        <size
-                android:width="16dip"
-                android:height="16dip"
-        />
-        
-        <gradient
-                android:type="sweep"
-                android:useLevel="false"
-                android:startColor="#4c737373"
-                android:centerColor="#4c737373"
-                android:centerY="0.50"
-                android:endColor="#ffffd300"
-        />
-        
-    </shape>
-    
-</rotate>
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_black_16"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_small_titlebar.xml b/core/res/res/drawable/progress_small_titlebar.xml
index cf8e41cb..8cfba86 100644
--- a/core/res/res/drawable/progress_small_titlebar.xml
+++ b/core/res/res/drawable/progress_small_titlebar.xml
@@ -1,45 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-  
-          http://www.apache.org/licenses/LICENSE-2.0
-  
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
 -->
-
- 
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
-        android:pivotX="50%" android:pivotY="50%"
-        android:fromDegrees="0" android:toDegrees="360">
-
-    <!-- An extra pixel is added on both ratios for stroke -->        
-    <shape
-            android:shape="ring"
-            android:innerRadiusRatio="3.2"
-            android:thicknessRatio="5.333"
-            android:useLevel="false">
-
-        <size
-                android:width="16dip"
-                android:height="16dip"
-        />
-        
-        <gradient
-                android:type="sweep"
-                android:useLevel="false"
-                android:startColor="#ff666666"
-                android:centerColor="#ff666666"
-                android:centerY="0.50"
-                android:endColor="#ffffd300"
-        />
-        
-    </shape>
-    
-</rotate>
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_white_16"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_small_white.xml b/core/res/res/drawable/progress_small_white.xml
new file mode 100644
index 0000000..8cfba86
--- /dev/null
+++ b/core/res/res/drawable/progress_small_white.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_white_16"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/spinner_black_16.png b/core/res/res/drawable/spinner_black_16.png
new file mode 100644
index 0000000..5ee33ce
--- /dev/null
+++ b/core/res/res/drawable/spinner_black_16.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_black_48.png b/core/res/res/drawable/spinner_black_48.png
new file mode 100644
index 0000000..3a68192
--- /dev/null
+++ b/core/res/res/drawable/spinner_black_48.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_black_76.png b/core/res/res/drawable/spinner_black_76.png
new file mode 100644
index 0000000..ec57460
--- /dev/null
+++ b/core/res/res/drawable/spinner_black_76.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_16.png b/core/res/res/drawable/spinner_white_16.png
new file mode 100644
index 0000000..dd2e1fd
--- /dev/null
+++ b/core/res/res/drawable/spinner_white_16.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_48.png b/core/res/res/drawable/spinner_white_48.png
new file mode 100644
index 0000000..d25a33e
--- /dev/null
+++ b/core/res/res/drawable/spinner_white_48.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_76.png b/core/res/res/drawable/spinner_white_76.png
new file mode 100644
index 0000000..f53e8ff
--- /dev/null
+++ b/core/res/res/drawable/spinner_white_76.png
Binary files differ
diff --git a/core/res/res/layout/character_picker.xml b/core/res/res/layout/character_picker.xml
index bb4955a..0344849 100644
--- a/core/res/res/layout/character_picker.xml
+++ b/core/res/res/layout/character_picker.xml
@@ -23,8 +23,8 @@
         android:id="@+id/characterPicker"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:padding="12dp"
-        android:verticalSpacing="8dp"
+        android:padding="4dp"
+        android:verticalSpacing="4dp"
         android:horizontalSpacing="8dp"
         android:stretchMode="spacingWidth"
         android:gravity="left"
diff --git a/core/res/res/values-da/donottranslate-cldr.xml b/core/res/res/values-da/donottranslate-cldr.xml
new file mode 100644
index 0000000..2d0db93
--- /dev/null
+++ b/core/res/res/values-da/donottranslate-cldr.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="month_long_standalone_january">januar</string>
+    <string name="month_long_standalone_february">februar</string>
+    <string name="month_long_standalone_march">marts</string>
+    <string name="month_long_standalone_april">april</string>
+    <string name="month_long_standalone_may">maj</string>
+    <string name="month_long_standalone_june">juni</string>
+    <string name="month_long_standalone_july">juli</string>
+    <string name="month_long_standalone_august">august</string>
+    <string name="month_long_standalone_september">september</string>
+    <string name="month_long_standalone_october">oktober</string>
+    <string name="month_long_standalone_november">november</string>
+    <string name="month_long_standalone_december">december</string>
+
+    <string name="month_long_january">januar</string>
+    <string name="month_long_february">februar</string>
+    <string name="month_long_march">marts</string>
+    <string name="month_long_april">april</string>
+    <string name="month_long_may">maj</string>
+    <string name="month_long_june">juni</string>
+    <string name="month_long_july">juli</string>
+    <string name="month_long_august">august</string>
+    <string name="month_long_september">september</string>
+    <string name="month_long_october">oktober</string>
+    <string name="month_long_november">november</string>
+    <string name="month_long_december">december</string>
+
+    <string name="month_medium_january">jan.</string>
+    <string name="month_medium_february">feb.</string>
+    <string name="month_medium_march">mar.</string>
+    <string name="month_medium_april">apr.</string>
+    <string name="month_medium_may">maj</string>
+    <string name="month_medium_june">jun.</string>
+    <string name="month_medium_july">jul.</string>
+    <string name="month_medium_august">aug.</string>
+    <string name="month_medium_september">sep.</string>
+    <string name="month_medium_october">okt.</string>
+    <string name="month_medium_november">nov.</string>
+    <string name="month_medium_december">dec.</string>
+
+    <string name="month_shortest_january">J</string>
+    <string name="month_shortest_february">F</string>
+    <string name="month_shortest_march">M</string>
+    <string name="month_shortest_april">A</string>
+    <string name="month_shortest_may">M</string>
+    <string name="month_shortest_june">J</string>
+    <string name="month_shortest_july">J</string>
+    <string name="month_shortest_august">A</string>
+    <string name="month_shortest_september">S</string>
+    <string name="month_shortest_october">O</string>
+    <string name="month_shortest_november">N</string>
+    <string name="month_shortest_december">D</string>
+
+    <string name="day_of_week_long_sunday">søndag</string>
+    <string name="day_of_week_long_monday">mandag</string>
+    <string name="day_of_week_long_tuesday">tirsdag</string>
+    <string name="day_of_week_long_wednesday">onsdag</string>
+    <string name="day_of_week_long_thursday">torsdag</string>
+    <string name="day_of_week_long_friday">fredag</string>
+    <string name="day_of_week_long_saturday">lørdag</string>
+
+    <string name="day_of_week_medium_sunday">søn</string>
+    <string name="day_of_week_medium_monday">man</string>
+    <string name="day_of_week_medium_tuesday">tir</string>
+    <string name="day_of_week_medium_wednesday">ons</string>
+    <string name="day_of_week_medium_thursday">tor</string>
+    <string name="day_of_week_medium_friday">fre</string>
+    <string name="day_of_week_medium_saturday">lør</string>
+
+    <string name="day_of_week_short_sunday">søn</string>
+    <string name="day_of_week_short_monday">man</string>
+    <string name="day_of_week_short_tuesday">tir</string>
+    <string name="day_of_week_short_wednesday">ons</string>
+    <string name="day_of_week_short_thursday">tor</string>
+    <string name="day_of_week_short_friday">fre</string>
+    <string name="day_of_week_short_saturday">lør</string>
+
+    <string name="day_of_week_shortest_sunday">S</string>
+    <string name="day_of_week_shortest_monday">M</string>
+    <string name="day_of_week_shortest_tuesday">T</string>
+    <string name="day_of_week_shortest_wednesday">O</string>
+    <string name="day_of_week_shortest_thursday">T</string>
+    <string name="day_of_week_shortest_friday">F</string>
+    <string name="day_of_week_shortest_saturday">L</string>
+
+    <string name="am">f.m.</string>
+    <string name="pm">e.m.</string>
+    <string name="yesterday">i går</string>
+    <string name="today">i dag</string>
+    <string name="tomorrow">i morgen</string>
+
+    <string name="hour_minute_24">%H.%M</string>
+    <string name="hour_minute_ampm">%-l.%M %p</string>
+    <string name="hour_minute_cap_ampm">%-l.%M %^p</string>
+    <string name="twelve_hour_time_format">h.mm a</string>
+    <string name="twenty_four_hour_time_format">HH.mm</string>
+    <string name="numeric_date">%d/%m/%Y</string>
+    <string name="numeric_date_format">dd/MM/yyyy</string>
+    <string name="numeric_date_template">"%s/%s/%s"</string>
+    <string name="month_day_year">%-e. %b %Y</string>
+    <string name="time_of_day">%H.%M.%S</string>
+    <string name="date_and_time">%H.%M.%S %d/%m/%Y</string>
+    <string name="date_time">%2$s %1$s</string>
+    <string name="time_date">%1$s %3$s</string>
+    <string name="abbrev_month_day_year">%d/%m/%Y</string>
+    <string name="month_day">%-e. %B</string>
+    <string name="month">%B</string>
+    <string name="month_year">%B %Y</string>
+    <string name="abbrev_month_day">%-e. %b</string>
+    <string name="abbrev_month">%b</string>
+    <string name="abbrev_month_year">%b %Y</string>
+    <string name="time1_time2">%1$s - %2$s</string>
+    <string name="date1_date2">%2$s - %5$s</string>
+    <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string>
+    <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s. %3$s-%2$s-%4$s - %10$s %6$s. %8$s-%7$s-%9$s</string>
+    <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s. %3$s-%2$s - %10$s %6$s. %8$s-%7$s</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s den %2$s - %6$s %4$s den %5$s</string>
+    <string name="wday1_date1_wday2_date2">%1$s den %2$s - %4$s den %5$s</string>
+    <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string>
+    <string name="time_wday_date">%1$s %2$s den %3$s</string>
+    <string name="wday_date">%2$s den %3$s</string>
+    <string name="time_wday">%1$s %2$s</string>
+    <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string>
+    <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string>
+    <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
+    <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s. %3$s. %2$s %4$s - %10$s %6$s. %8$s. %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s. %3$s. %2$s %4$s - %10$s %6$s. %8$s. %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s. %3$s. %2$s %4$s - %6$s. %8$s. %7$s %9$s</string>
+    <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string>
+    <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string>
+    <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
+    <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s den %3$s. %2$s - %6$s den %8$s. %7$s %9$s</string>
+    <string name="short_format_month">%b</string>
+</resources>
diff --git a/core/res/res/values-el/donottranslate-cldr.xml b/core/res/res/values-el/donottranslate-cldr.xml
new file mode 100644
index 0000000..e8f02fb
--- /dev/null
+++ b/core/res/res/values-el/donottranslate-cldr.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="month_long_standalone_january">Ιανουάριος</string>
+    <string name="month_long_standalone_february">Φεβρουάριος</string>
+    <string name="month_long_standalone_march">Μάρτιος</string>
+    <string name="month_long_standalone_april">Απρίλιος</string>
+    <string name="month_long_standalone_may">Μάιος</string>
+    <string name="month_long_standalone_june">Ιούνιος</string>
+    <string name="month_long_standalone_july">Ιούλιος</string>
+    <string name="month_long_standalone_august">Αύγουστος</string>
+    <string name="month_long_standalone_september">Σεπτέμβριος</string>
+    <string name="month_long_standalone_october">Οκτώβριος</string>
+    <string name="month_long_standalone_november">Νοέμβριος</string>
+    <string name="month_long_standalone_december">Δεκέμβριος</string>
+
+    <string name="month_long_january">Ιανουαρίου</string>
+    <string name="month_long_february">Φεβρουαρίου</string>
+    <string name="month_long_march">Μαρτίου</string>
+    <string name="month_long_april">Απριλίου</string>
+    <string name="month_long_may">Μαΐου</string>
+    <string name="month_long_june">Ιουνίου</string>
+    <string name="month_long_july">Ιουλίου</string>
+    <string name="month_long_august">Αυγούστου</string>
+    <string name="month_long_september">Σεπτεμβρίου</string>
+    <string name="month_long_october">Οκτωβρίου</string>
+    <string name="month_long_november">Νοεμβρίου</string>
+    <string name="month_long_december">Δεκεμβρίου</string>
+
+    <string name="month_medium_january">Ιαν</string>
+    <string name="month_medium_february">Φεβ</string>
+    <string name="month_medium_march">Μαρ</string>
+    <string name="month_medium_april">Απρ</string>
+    <string name="month_medium_may">Μαϊ</string>
+    <string name="month_medium_june">Ιουν</string>
+    <string name="month_medium_july">Ιουλ</string>
+    <string name="month_medium_august">Αυγ</string>
+    <string name="month_medium_september">Σεπ</string>
+    <string name="month_medium_october">Οκτ</string>
+    <string name="month_medium_november">Νοε</string>
+    <string name="month_medium_december">Δεκ</string>
+
+    <string name="month_shortest_january">Ι</string>
+    <string name="month_shortest_february">Φ</string>
+    <string name="month_shortest_march">Μ</string>
+    <string name="month_shortest_april">Α</string>
+    <string name="month_shortest_may">Μ</string>
+    <string name="month_shortest_june">Ι</string>
+    <string name="month_shortest_july">Ι</string>
+    <string name="month_shortest_august">Α</string>
+    <string name="month_shortest_september">Σ</string>
+    <string name="month_shortest_october">Ο</string>
+    <string name="month_shortest_november">Ν</string>
+    <string name="month_shortest_december">Δ</string>
+
+    <string name="day_of_week_long_sunday">Κυριακή</string>
+    <string name="day_of_week_long_monday">Δευτέρα</string>
+    <string name="day_of_week_long_tuesday">Τρίτη</string>
+    <string name="day_of_week_long_wednesday">Τετάρτη</string>
+    <string name="day_of_week_long_thursday">Πέμπτη</string>
+    <string name="day_of_week_long_friday">Παρασκευή</string>
+    <string name="day_of_week_long_saturday">Σάββατο</string>
+
+    <string name="day_of_week_medium_sunday">Κυρ</string>
+    <string name="day_of_week_medium_monday">Δευ</string>
+    <string name="day_of_week_medium_tuesday">Τρι</string>
+    <string name="day_of_week_medium_wednesday">Τετ</string>
+    <string name="day_of_week_medium_thursday">Πεμ</string>
+    <string name="day_of_week_medium_friday">Παρ</string>
+    <string name="day_of_week_medium_saturday">Σαβ</string>
+
+    <string name="day_of_week_short_sunday">Κυρ</string>
+    <string name="day_of_week_short_monday">Δευ</string>
+    <string name="day_of_week_short_tuesday">Τρι</string>
+    <string name="day_of_week_short_wednesday">Τετ</string>
+    <string name="day_of_week_short_thursday">Πεμ</string>
+    <string name="day_of_week_short_friday">Παρ</string>
+    <string name="day_of_week_short_saturday">Σαβ</string>
+
+    <string name="day_of_week_shortest_sunday">Κ</string>
+    <string name="day_of_week_shortest_monday">Δ</string>
+    <string name="day_of_week_shortest_tuesday">Τ</string>
+    <string name="day_of_week_shortest_wednesday">Τ</string>
+    <string name="day_of_week_shortest_thursday">Π</string>
+    <string name="day_of_week_shortest_friday">Π</string>
+    <string name="day_of_week_shortest_saturday">Σ</string>
+
+    <string name="am">π.μ.</string>
+    <string name="pm">μ.μ.</string>
+    <string name="yesterday">Χτες</string>
+    <string name="today">Σήμερα</string>
+    <string name="tomorrow">Αύριο</string>
+
+    <string name="hour_minute_24">%-k:%M</string>
+    <string name="hour_minute_ampm">%-l:%M %p</string>
+    <string name="hour_minute_cap_ampm">%-l:%M %p</string>
+    <string name="twelve_hour_time_format">h:mm a</string>
+    <string name="twenty_four_hour_time_format">H:mm</string>
+    <string name="numeric_date">%d/%m/%Y</string>
+    <string name="numeric_date_format">dd/MM/yyyy</string>
+    <string name="numeric_date_template">"%s/%s/%s"</string>
+    <string name="month_day_year">%d %B %Y</string>
+    <string name="time_of_day">%-l:%M:%S %p</string>
+    <string name="date_and_time">%-l:%M:%S %p %d %b %Y</string>
+    <string name="date_time">%2$s %1$s</string>
+    <string name="time_date">%1$s %3$s</string>
+    <string name="abbrev_month_day_year">%d %b %Y</string>
+    <string name="month_day">%-e %B</string>
+    <string name="month">%-B</string>
+    <string name="month_year">%-B %Y</string>
+    <string name="abbrev_month_day">%-e %b</string>
+    <string name="abbrev_month">%-b</string>
+    <string name="abbrev_month_year">%b %Y</string>
+    <string name="time1_time2">%1$s - %2$s</string>
+    <string name="date1_date2">%2$s - %5$s</string>
+    <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s/%2$s - %6$s, %8$s/%7$s</string>
+    <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s</string>
+    <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s/%2$s - %10$s %6$s, %8$s/%7$s</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string>
+    <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string>
+    <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string>
+    <string name="time_wday_date">%1$s %2$s, %3$s</string>
+    <string name="wday_date">%2$s, %3$s</string>
+    <string name="time_wday">%1$s %2$s</string>
+    <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string>
+    <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string>
+    <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string>
+    <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string>
+    <string name="same_month_md1_md2">%3$s-%8$s %2$s</string>
+    <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string>
+    <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
+    <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string>
+    <string name="short_format_month">%b</string>
+</resources>
diff --git a/core/res/res/values-sv/donottranslate-cldr.xml b/core/res/res/values-sv/donottranslate-cldr.xml
new file mode 100644
index 0000000..a6ffc9a
--- /dev/null
+++ b/core/res/res/values-sv/donottranslate-cldr.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="month_long_standalone_january">januari</string>
+    <string name="month_long_standalone_february">februari</string>
+    <string name="month_long_standalone_march">mars</string>
+    <string name="month_long_standalone_april">april</string>
+    <string name="month_long_standalone_may">maj</string>
+    <string name="month_long_standalone_june">juni</string>
+    <string name="month_long_standalone_july">juli</string>
+    <string name="month_long_standalone_august">augusti</string>
+    <string name="month_long_standalone_september">september</string>
+    <string name="month_long_standalone_october">oktober</string>
+    <string name="month_long_standalone_november">november</string>
+    <string name="month_long_standalone_december">december</string>
+
+    <string name="month_long_january">januari</string>
+    <string name="month_long_february">februari</string>
+    <string name="month_long_march">mars</string>
+    <string name="month_long_april">april</string>
+    <string name="month_long_may">maj</string>
+    <string name="month_long_june">juni</string>
+    <string name="month_long_july">juli</string>
+    <string name="month_long_august">augusti</string>
+    <string name="month_long_september">september</string>
+    <string name="month_long_october">oktober</string>
+    <string name="month_long_november">november</string>
+    <string name="month_long_december">december</string>
+
+    <string name="month_medium_january">jan</string>
+    <string name="month_medium_february">feb</string>
+    <string name="month_medium_march">mar</string>
+    <string name="month_medium_april">apr</string>
+    <string name="month_medium_may">maj</string>
+    <string name="month_medium_june">jun</string>
+    <string name="month_medium_july">jul</string>
+    <string name="month_medium_august">aug</string>
+    <string name="month_medium_september">sep</string>
+    <string name="month_medium_october">okt</string>
+    <string name="month_medium_november">nov</string>
+    <string name="month_medium_december">dec</string>
+
+    <string name="month_shortest_january">J</string>
+    <string name="month_shortest_february">F</string>
+    <string name="month_shortest_march">M</string>
+    <string name="month_shortest_april">A</string>
+    <string name="month_shortest_may">M</string>
+    <string name="month_shortest_june">J</string>
+    <string name="month_shortest_july">J</string>
+    <string name="month_shortest_august">A</string>
+    <string name="month_shortest_september">S</string>
+    <string name="month_shortest_october">O</string>
+    <string name="month_shortest_november">N</string>
+    <string name="month_shortest_december">D</string>
+
+    <string name="day_of_week_long_sunday">söndag</string>
+    <string name="day_of_week_long_monday">måndag</string>
+    <string name="day_of_week_long_tuesday">tisdag</string>
+    <string name="day_of_week_long_wednesday">onsdag</string>
+    <string name="day_of_week_long_thursday">torsdag</string>
+    <string name="day_of_week_long_friday">fredag</string>
+    <string name="day_of_week_long_saturday">lördag</string>
+
+    <string name="day_of_week_medium_sunday">sön</string>
+    <string name="day_of_week_medium_monday">mån</string>
+    <string name="day_of_week_medium_tuesday">tis</string>
+    <string name="day_of_week_medium_wednesday">ons</string>
+    <string name="day_of_week_medium_thursday">tors</string>
+    <string name="day_of_week_medium_friday">fre</string>
+    <string name="day_of_week_medium_saturday">lör</string>
+
+    <string name="day_of_week_short_sunday">sön</string>
+    <string name="day_of_week_short_monday">mån</string>
+    <string name="day_of_week_short_tuesday">tis</string>
+    <string name="day_of_week_short_wednesday">ons</string>
+    <string name="day_of_week_short_thursday">tors</string>
+    <string name="day_of_week_short_friday">fre</string>
+    <string name="day_of_week_short_saturday">lör</string>
+
+    <string name="day_of_week_shortest_sunday">S</string>
+    <string name="day_of_week_shortest_monday">M</string>
+    <string name="day_of_week_shortest_tuesday">T</string>
+    <string name="day_of_week_shortest_wednesday">O</string>
+    <string name="day_of_week_shortest_thursday">T</string>
+    <string name="day_of_week_shortest_friday">F</string>
+    <string name="day_of_week_shortest_saturday">L</string>
+
+    <string name="am">f.m.</string>
+    <string name="pm">e.m.</string>
+    <string name="yesterday">igår</string>
+    <string name="today">idag</string>
+    <string name="tomorrow">imorgon</string>
+
+    <string name="hour_minute_24">%-k.%M</string>
+    <string name="hour_minute_ampm">%-l:%M %p</string>
+    <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
+    <string name="twelve_hour_time_format">h:mm a</string>
+    <string name="twenty_four_hour_time_format">H.mm</string>
+    <string name="numeric_date">%Y-%m-%d</string>
+    <string name="numeric_date_format">yyyy-MM-dd</string>
+    <string name="numeric_date_template">"%s-%s-%s"</string>
+    <string name="month_day_year">%-e %B %Y</string>
+    <string name="time_of_day">%H:%M:%S</string>
+    <string name="date_and_time">%H:%M:%S %-e %b %Y</string>
+    <string name="date_time">%2$s %1$s</string>
+    <string name="time_date">%1$s %3$s</string>
+    <string name="abbrev_month_day_year">%-e %b %Y</string>
+    <string name="month_day">%-e %B</string>
+    <string name="month">%-B</string>
+    <string name="month_year">%Y %B</string>
+    <string name="abbrev_month_day">%-e %b</string>
+    <string name="abbrev_month">%-b</string>
+    <string name="abbrev_month_year">%Y %b</string>
+    <string name="time1_time2">%1$s – %2$s</string>
+    <string name="date1_date2">%2$s – %5$s</string>
+    <string name="numeric_md1_md2">%3$s/%2$s – %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s – %6$s %8$s/%7$s</string>
+    <string name="numeric_mdy1_mdy2">%4$s-%2$s-%3$s – %9$s-%7$s-%8$s</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %4$s-%2$s-%3$s – %6$s, %9$s-%7$s-%8$s</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s-%2$s-%3$s – %10$s %6$s, %9$s-%7$s-%8$s</string>
+    <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s – %10$s %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s/%2$s – %10$s %6$s %8$s/%7$s</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">%5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s – %6$s %4$s %5$s</string>
+    <string name="wday1_date1_wday2_date2">%1$s %2$s – %4$s %5$s</string>
+    <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string>
+    <string name="time_wday_date">%1$s %2$s %3$s</string>
+    <string name="wday_date">%2$s %3$s</string>
+    <string name="time_wday">%1$s %2$s</string>
+    <string name="same_year_md1_md2">%3$s %2$s – %8$s %7$s</string>
+    <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string>
+    <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string>
+    <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s – %6$s %8$s %7$s %9$s</string>
+    <string name="same_month_md1_md2">%3$s–%8$s %2$s</string>
+    <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string>
+    <string name="same_year_mdy1_mdy2">%3$s %2$s–%8$s %7$s %9$s</string>
+    <string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s–%6$s %8$s %7$s %9$s</string>
+    <string name="short_format_month">%b</string>
+</resources>
diff --git a/core/res/res/values-tr/donottranslate-cldr.xml b/core/res/res/values-tr/donottranslate-cldr.xml
new file mode 100644
index 0000000..d612305
--- /dev/null
+++ b/core/res/res/values-tr/donottranslate-cldr.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="month_long_standalone_january">Ocak</string>
+    <string name="month_long_standalone_february">Şubat</string>
+    <string name="month_long_standalone_march">Mart</string>
+    <string name="month_long_standalone_april">Nisan</string>
+    <string name="month_long_standalone_may">Mayıs</string>
+    <string name="month_long_standalone_june">Haziran</string>
+    <string name="month_long_standalone_july">Temmuz</string>
+    <string name="month_long_standalone_august">Ağustos</string>
+    <string name="month_long_standalone_september">Eylül</string>
+    <string name="month_long_standalone_october">Ekim</string>
+    <string name="month_long_standalone_november">Kasım</string>
+    <string name="month_long_standalone_december">Aralık</string>
+
+    <string name="month_long_january">Ocak</string>
+    <string name="month_long_february">Şubat</string>
+    <string name="month_long_march">Mart</string>
+    <string name="month_long_april">Nisan</string>
+    <string name="month_long_may">Mayıs</string>
+    <string name="month_long_june">Haziran</string>
+    <string name="month_long_july">Temmuz</string>
+    <string name="month_long_august">Ağustos</string>
+    <string name="month_long_september">Eylül</string>
+    <string name="month_long_october">Ekim</string>
+    <string name="month_long_november">Kasım</string>
+    <string name="month_long_december">Aralık</string>
+
+    <string name="month_medium_january">Oca</string>
+    <string name="month_medium_february">Şub</string>
+    <string name="month_medium_march">Mar</string>
+    <string name="month_medium_april">Nis</string>
+    <string name="month_medium_may">May</string>
+    <string name="month_medium_june">Haz</string>
+    <string name="month_medium_july">Tem</string>
+    <string name="month_medium_august">Ağu</string>
+    <string name="month_medium_september">Eyl</string>
+    <string name="month_medium_october">Eki</string>
+    <string name="month_medium_november">Kas</string>
+    <string name="month_medium_december">Ara</string>
+
+    <string name="month_shortest_january">O</string>
+    <string name="month_shortest_february">Ş</string>
+    <string name="month_shortest_march">M</string>
+    <string name="month_shortest_april">N</string>
+    <string name="month_shortest_may">M</string>
+    <string name="month_shortest_june">H</string>
+    <string name="month_shortest_july">T</string>
+    <string name="month_shortest_august">A</string>
+    <string name="month_shortest_september">E</string>
+    <string name="month_shortest_october">E</string>
+    <string name="month_shortest_november">K</string>
+    <string name="month_shortest_december">A</string>
+
+    <string name="day_of_week_long_sunday">Pazar</string>
+    <string name="day_of_week_long_monday">Pazartesi</string>
+    <string name="day_of_week_long_tuesday">Salı</string>
+    <string name="day_of_week_long_wednesday">Çarşamba</string>
+    <string name="day_of_week_long_thursday">Perşembe</string>
+    <string name="day_of_week_long_friday">Cuma</string>
+    <string name="day_of_week_long_saturday">Cumartesi</string>
+
+    <string name="day_of_week_medium_sunday">Paz</string>
+    <string name="day_of_week_medium_monday">Pzt</string>
+    <string name="day_of_week_medium_tuesday">Sal</string>
+    <string name="day_of_week_medium_wednesday">Çar</string>
+    <string name="day_of_week_medium_thursday">Per</string>
+    <string name="day_of_week_medium_friday">Cum</string>
+    <string name="day_of_week_medium_saturday">Cmt</string>
+
+    <string name="day_of_week_short_sunday">Paz</string>
+    <string name="day_of_week_short_monday">Pzt</string>
+    <string name="day_of_week_short_tuesday">Sal</string>
+    <string name="day_of_week_short_wednesday">Çar</string>
+    <string name="day_of_week_short_thursday">Per</string>
+    <string name="day_of_week_short_friday">Cum</string>
+    <string name="day_of_week_short_saturday">Cmt</string>
+
+    <string name="day_of_week_shortest_sunday">P</string>
+    <string name="day_of_week_shortest_monday">P</string>
+    <string name="day_of_week_shortest_tuesday">S</string>
+    <string name="day_of_week_shortest_wednesday">Ç</string>
+    <string name="day_of_week_shortest_thursday">P</string>
+    <string name="day_of_week_shortest_friday">C</string>
+    <string name="day_of_week_shortest_saturday">C</string>
+
+    <string name="am">AM</string>
+    <string name="pm">PM</string>
+    <string name="yesterday">Dün</string>
+    <string name="today">Bugün</string>
+    <string name="tomorrow">Yarın</string>
+
+    <string name="hour_minute_24">%H:%M</string>
+    <string name="hour_minute_ampm">%-l:%M %p</string>
+    <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
+    <string name="twelve_hour_time_format">h:mm a</string>
+    <string name="twenty_four_hour_time_format">HH:mm</string>
+    <string name="numeric_date">%d %m %Y</string>
+    <string name="numeric_date_format">dd MM yyyy</string>
+    <string name="numeric_date_template">"%s %s %s"</string>
+    <string name="month_day_year">%d %B %Y</string>
+    <string name="time_of_day">%H:%M:%S</string>
+    <string name="date_and_time">%H:%M:%S %d %b %Y</string>
+    <string name="date_time">%2$s %1$s</string>
+    <string name="time_date">%1$s %3$s</string>
+    <string name="abbrev_month_day_year">%d %b %Y</string>
+    <string name="month_day">%d %B</string>
+    <string name="month">%-B</string>
+    <string name="month_year">%B %Y</string>
+    <string name="abbrev_month_day">%d %b</string>
+    <string name="abbrev_month">%-b</string>
+    <string name="abbrev_month_year">%b %Y</string>
+    <string name="time1_time2">%1$s - %2$s</string>
+    <string name="date1_date2">%2$s - %5$s</string>
+    <string name="numeric_md1_md2">%3$s.%2$s - %8$s.%7$s</string>
+    <string name="numeric_wday1_md1_wday2_md2">%3$s.%2$s %1$s - %8$s.%7$s %6$s</string>
+    <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">%3$s.%2$s.%4$s %1$s - %8$s.%7$s.%9$s %6$s</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s.%2$s.%4$s %1$s - %10$s %8$s.%7$s.%9$s %6$s</string>
+    <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %3$s/%2$s %1$s - %10$s %8$s/%7$s %6$s</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s %1$s - %6$s %5$s %4$s</string>
+    <string name="wday1_date1_wday2_date2">%2$s %1$s - %5$s %4$s</string>
+    <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string>
+    <string name="time_wday_date">%1$s %3$s %2$s</string>
+    <string name="wday_date">%3$s %2$s</string>
+    <string name="time_wday">%1$s %2$s</string>
+    <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string>
+    <string name="same_year_wday1_md1_wday2_md2">%3$s %2$s %1$s - %8$s %7$s %6$s</string>
+    <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string>
+    <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s %1$s - %10$s %8$s %7$s %9$s %6$s</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s %1$s - %10$s %8$s %7$s %9$s %6$s</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">%3$s %2$s %4$s %1$s - %8$s %7$s %9$s %6$s</string>
+    <string name="same_month_md1_md2">%3$s-%8$s %2$s</string>
+    <string name="same_month_wday1_md1_wday2_md2">%3$s %2$s %1$s - %8$s %7$s %6$s</string>
+    <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
+    <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s %9$s %1$s - %8$s %7$s y %6$s</string>
+    <string name="short_format_month">%b</string>
+</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d070107..10308b6 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -350,6 +350,12 @@
         <attr name="progressBarStyleSmallTitle" format="reference" />
         <!-- Large ProgressBar style. This is a large circular progress bar. -->
         <attr name="progressBarStyleLarge" format="reference" />
+        <!-- Inverse ProgressBar style. This is a medium circular progress bar. -->
+        <attr name="progressBarStyleInverse" format="reference" />
+        <!-- Small inverse ProgressBar style. This is a small circular progress bar. -->
+        <attr name="progressBarStyleSmallInverse" format="reference" />
+        <!-- Large inverse ProgressBar style. This is a large circular progress bar. -->
+        <attr name="progressBarStyleLargeInverse" format="reference" /> 
         <!-- Default SeekBar style. -->
         <attr name="seekBarStyle" format="reference" />
         <!-- Default RatingBar style. -->
@@ -2873,6 +2879,11 @@
              results for "bo", it would not be queried again for "bob".
              The default value is <code>false</code>. <i>Optional attribute.</i>. -->
         <attr name="queryAfterZeroResults" format="boolean" />
+        
+        <!-- If provided, this string will be used to describe the searchable item in the
+             searchable items settings within system search settings. <i>Optional
+             attribute.</i> -->
+        <attr name="searchSettingsDescription" format="string" />
 
     </declare-styleable>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 14d8dbe..148935f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1120,11 +1120,18 @@
   <public type="attr" name="smallScreens" />
   <public type="attr" name="normalScreens" />
   <public type="attr" name="largeScreens" />
+  <public type="attr" name="progressBarStyleInverse" />
+  <public type="attr" name="progressBarStyleSmallInverse" />
+  <public type="attr" name="progressBarStyleLargeInverse" /> 
+  <public type="attr" name="searchSettingsDescription" />
 
   <public-padding type="attr" name="donut_resource_pad" end="0x0101029f" />
 
   <public-padding type="id" name="donut_resource_pad" end="0x01020040" />
   
+  <public type="style" name="Widget.ProgressBar.Inverse" id="0x0103005b" />
+  <public type="style" name="Widget.ProgressBar.Large.Inverse" id="0x0103005c" />
+  <public type="style" name="Widget.ProgressBar.Small.Inverse" id="0x0103005d" /> 
   <public-padding type="style" name="donut_resource_pad" end="0x01030070" />
   
   <public-padding type="string" name="donut_resource_pad" end="0x01040030" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9f1ed40..1ce9c76 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1786,6 +1786,11 @@
     <!-- See EXTMEDIA_FORMAT.    This is the button text to format the sd card. -->
     <string name="extmedia_format_button_format">Format</string>
 
+    <!-- Title of notification shown when ADB is actively connected to the phone. -->
+    <string name="adb_active_notification_title">USB debugging connected</string>
+    <!-- Message of notification shown when ADB is actively connected to the phone. -->
+    <string name="adb_active_notification_message">A computer is connected to your phone.</string>
+    
     <!-- Used to replace %s in urls retreived from the signin server with locales.  For Some        -->
     <!-- devices we don't support all the locales we ship to and need to replace the '%s' with a    -->
     <!-- locale string based on mcc values.  By default (0-length string) we don't replace the %s   -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 648a7dd..7d235ec 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -243,7 +243,7 @@
 
     <style name="Widget.ProgressBar">
         <item name="android:indeterminateOnly">true</item>
-        <item name="android:indeterminateDrawable">@android:drawable/progress_medium</item>
+        <item name="android:indeterminateDrawable">@android:drawable/progress_medium_white</item>
         <item name="android:indeterminateBehavior">repeat</item>
         <item name="android:indeterminateDuration">3500</item>
         <item name="android:minWidth">48dip</item>
@@ -253,7 +253,7 @@
     </style>
 
     <style name="Widget.ProgressBar.Large">
-        <item name="android:indeterminateDrawable">@android:drawable/progress_large</item>
+        <item name="android:indeterminateDrawable">@android:drawable/progress_large_white</item>
         <item name="android:minWidth">76dip</item>
         <item name="android:maxWidth">76dip</item>
         <item name="android:minHeight">76dip</item>
@@ -261,13 +261,25 @@
     </style>
     
     <style name="Widget.ProgressBar.Small">
-        <item name="android:indeterminateDrawable">@android:drawable/progress_small</item>
+        <item name="android:indeterminateDrawable">@android:drawable/progress_small_white</item>
         <item name="android:minWidth">16dip</item>
         <item name="android:maxWidth">16dip</item>
         <item name="android:minHeight">16dip</item>
         <item name="android:maxHeight">16dip</item>
     </style>
 
+    <style name="Widget.ProgressBar.Inverse">
+        <item name="android:indeterminateDrawable">@android:drawable/progress_medium</item>
+    </style>
+
+    <style name="Widget.ProgressBar.Large.Inverse">
+        <item name="android:indeterminateDrawable">@android:drawable/progress_large</item>
+    </style>
+
+    <style name="Widget.ProgressBar.Small.Inverse">
+        <item name="android:indeterminateDrawable">@android:drawable/progress_small</item>
+    </style> 
+    
     <style name="Widget.ProgressBar.Small.Title">
         <item name="android:indeterminateDrawable">@android:drawable/progress_small_titlebar</item>
     </style>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index f37d514..bd6e1df 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -154,6 +154,9 @@
         <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small</item>
         <item name="progressBarStyleSmallTitle">@android:style/Widget.ProgressBar.Small.Title</item>
         <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large</item>
+        <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar.Inverse</item>
+		<item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item>
+	    <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item> 
         <item name="seekBarStyle">@android:style/Widget.SeekBar</item>
         <item name="ratingBarStyle">@android:style/Widget.RatingBar</item>
         <item name="ratingBarStyleIndicator">@android:style/Widget.RatingBar.Indicator</item>
@@ -233,6 +236,13 @@
         <item name="listViewStyle">@android:style/Widget.ListView.White</item>
         <item name="listDivider">@drawable/divider_horizontal_bright</item>
         <item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator.White</item>
+        
+        <item name="progressBarStyle">@android:style/Widget.ProgressBar.Inverse</item>
+		<item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item>
+		<item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item>
+		<item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item>
+		<item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item>
+		<item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item> 
     </style>
     
     <!-- Variant of the light theme with no title bar -->
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 2b24ef2..778c903 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -57,7 +57,9 @@
         mBitmap = patch.mBitmap;
         mChunk = patch.mChunk;
         mSrcName = patch.mSrcName;
-        mPaint = new Paint(patch.mPaint);
+        if (patch.mPaint != null) {
+            mPaint = new Paint(patch.mPaint);
+        }
         validateNinePatchChunk(mBitmap.ni(), mChunk);
     }
 
@@ -120,7 +122,6 @@
     
     public native static boolean isNinePatchChunk(byte[] chunk);
 
-    private final Rect   mRect = new Rect();
     private final Bitmap mBitmap;
     private final byte[] mChunk;
     private Paint        mPaint;
diff --git a/graphics/java/android/graphics/drawable/Animatable.java b/graphics/java/android/graphics/drawable/Animatable.java
new file mode 100644
index 0000000..9dc62c3
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/Animatable.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable;
+
+/**
+ * Interface that drawables suporting animations should implement.
+ */
+public interface Animatable {
+    /**
+     * Starts the drawable's animation.
+     */
+    void start();
+
+    /**
+     * Stops the drawable's animation.
+     */
+    void stop();
+
+    /**
+     * Indicates whether the animation is running.
+     * 
+     * @return True if the animation is running, false otherwise.
+     */
+    boolean isRunning();
+}
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index 08d295d..ac96f20 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -35,11 +35,14 @@
 /**
  * @hide
  */
-public class AnimatedRotateDrawable extends Drawable implements Drawable.Callback, Runnable {
+public class AnimatedRotateDrawable extends Drawable implements Drawable.Callback, Runnable,
+        Animatable {
+
     private AnimatedRotateState mState;
     private boolean mMutated;
     private float mCurrentDegrees;
     private float mIncrement;
+    private boolean mRunning;
 
     public AnimatedRotateDrawable() {
         this(null);
@@ -80,10 +83,24 @@
         drawable.draw(canvas);
 
         canvas.restoreToCount(saveCount);
-        
-        nextFrame();
     }
-    
+
+    public void start() {
+        if (!mRunning) {
+            mRunning = true;
+            nextFrame();
+        }
+    }
+
+    public void stop() {
+        mRunning = false;
+        unscheduleSelf(this);
+    }
+
+    public boolean isRunning() {
+        return mRunning;
+    }
+
     private void nextFrame() {
         unscheduleSelf(this);
         scheduleSelf(this, SystemClock.uptimeMillis() + mState.mFrameDuration);
@@ -96,8 +113,8 @@
         if (mCurrentDegrees > (360.0f - mIncrement)) {
             mCurrentDegrees = 0.0f;
         }
-        nextFrame();
         invalidateSelf();
+        nextFrame();
     }
     
     @Override
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index bab1703..68718c9 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -71,7 +71,7 @@
  * @attr ref android.R.styleable#AnimationDrawableItem_duration
  * @attr ref android.R.styleable#AnimationDrawableItem_drawable
  */
-public class AnimationDrawable extends DrawableContainer implements Runnable {
+public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable {
     private final AnimationState mAnimationState;
     private int mCurFrame = -1;
     private boolean mMutated;
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index f8b88d0..376b1df 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -234,8 +234,10 @@
     @Override
     public Drawable mutate() {
         if (!mMutated && super.mutate() == this) {
-            for (Drawable child : mDrawableContainerState.mDrawables) {
-                child.mutate();
+            final int N = mDrawableContainerState.getChildCount();
+            final Drawable[] drawables = mDrawableContainerState.getChildren();
+            for (int i = 0; i < N; i++) {
+                drawables[i].mutate();
             }
             mMutated = true;
         }
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
index ca50a5e..21cb73b 100644
--- a/include/tts/TtsEngine.h
+++ b/include/tts/TtsEngine.h
@@ -133,16 +133,26 @@
     // @return TTS_SUCCESS, or TTS_FAILURE
     virtual tts_result setLanguage(const char *lang, const char *country, const char *variant);
 
-    // Retrieve the currently set language, or an empty "value" if no language
-    // has been set.
-    // @param[out]   value pointer to the retrieved language value
-    // @param[inout] iosize in: stores the size available to store the language
-    //                         value in *value
-    //                      out: stores the size required to hold the language
-    //                         value if  getLanguage() returned
-    //                         TTS_PROPERTY_SIZE_TOO_SMALL, unchanged otherwise.
-    // @return TTS_SUCCESS, or TTS_PROPERTY_SIZE_TOO_SMALL, or TTS_FAILURE
-    virtual tts_result getLanguage(char *value, size_t *iosize);
+    // Retrieve the currently set language, country and variant, or empty strings if none of
+    // parameters have been set. Language and country are represented by their 3-letter ISO code
+    // @param[out]   pointer to the retrieved 3-letter code language value
+    // @param[out]   pointer to the retrieved 3-letter code country value
+    // @param[out]   pointer to the retrieved variant value
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    virtual tts_result getLanguage(char *language, char *country, char *variant);
+
+    // Notifies the engine what audio parameters should be used for the synthesis.
+    // This is meant to be used as a hint, the engine implementation will set the output values
+    // to those of the synthesis format, based on a given hint.
+    // @param[inout] encoding in: the desired audio sample format
+    //                         out: the format used by the TTS engine
+    // @param[inout] rate in: the desired audio sample rate
+    //                         out: the sample rate used by the TTS engine
+    // @param[inout] channels in: the desired number of audio channels
+    //                         out: the number of channels used by the TTS engine
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    virtual tts_result setAudioFormat(AudioSystem::audio_format& encoding, uint32_t& rate,
+            int& channels);
 
     // Set a property for the the TTS engine
     // "size" is the maximum size of "value" for properties "property"
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index 5c41ead..eb4151a 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -1441,7 +1441,7 @@
  * This is the beginning of information about an entry in the resource
  * table.  It holds the reference to the name of this entry, and is
  * immediately followed by one of:
- *   * A Res_value structures, if FLAG_COMPLEX is -not- set.
+ *   * A Res_value structure, if FLAG_COMPLEX is -not- set.
  *   * An array of ResTable_map structures, if FLAG_COMPLEX is set.
  *     These supply a set of name/value mappings of data.
  */
@@ -1843,6 +1843,8 @@
     status_t parsePackage(
         const ResTable_package* const pkg, const Header* const header);
 
+    void print_value(const Package* pkg, const Res_value& value) const;
+    
     mutable Mutex               mLock;
 
     status_t                    mError;
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 324111b..75ca22c 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -499,7 +499,8 @@
     }
 
 #ifdef WITH_A2DP
-    LOGD("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(), IPCThreadState::self()->getCallingPid());
+    LOGV("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(),
+            IPCThreadState::self()->getCallingPid());
     if (mode == AudioSystem::MODE_NORMAL && 
             (mask & AudioSystem::ROUTE_BLUETOOTH_A2DP)) {
         AutoMutex lock(&mLock);
@@ -893,7 +894,7 @@
             }
             LOGV("mA2dpDisableCount decremented to %d", mA2dpDisableCount);
         } else {
-            LOGE("mA2dpDisableCount is already zero");
+            LOGV("mA2dpDisableCount is already zero");
         }
     }
 }
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 0e998bf..ef4a8ea 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -771,10 +771,11 @@
             dirty.orSelf(layer->visibleRegionScreen);
             layer->contentDirty = false;
         } else {
-            // compute the exposed region
-            // dirty = what's visible now - what's wasn't covered before
-            //       = what's visible now & what's was covered before
-            dirty = visibleRegion.intersect(layer->coveredRegionScreen);            
+            /* compute the exposed region:
+             *    exposed = what's VISIBLE and NOT COVERED now 
+             *    but was COVERED before
+             */
+            dirty = (visibleRegion - coveredRegion) & layer->coveredRegionScreen;
         }
         dirty.subtractSelf(aboveOpaqueLayers);
 
@@ -783,7 +784,7 @@
 
         // updade aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer
         aboveOpaqueLayers.orSelf(opaqueRegion);
-        aboveCoveredLayers.orSelf(bounds);
+        aboveCoveredLayers.orSelf(visibleRegion);
         
         // Store the visible region is screen space
         layer->setVisibleRegion(visibleRegion);
diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp
index bb22dab..a481ce7 100644
--- a/libs/ui/Camera.cpp
+++ b/libs/ui/Camera.cpp
@@ -149,21 +149,21 @@
 status_t Camera::setPreviewDisplay(const sp<Surface>& surface)
 {
     LOGV("setPreviewDisplay");
-    if (surface == 0) {
-        LOGE("app passed NULL surface");
-        return NO_INIT;
-    }
     sp <ICamera> c = mCamera;
     if (c == 0) return NO_INIT;
-    return c->setPreviewDisplay(surface->getISurface());
+    if (surface != 0) {
+        return c->setPreviewDisplay(surface->getISurface());
+    } else {
+        LOGD("app passed NULL surface");
+        return c->setPreviewDisplay(0);
+    }
 }
 
 status_t Camera::setPreviewDisplay(const sp<ISurface>& surface)
 {
     LOGV("setPreviewDisplay");
     if (surface == 0) {
-        LOGE("app passed NULL surface");
-        return NO_INIT;
+        LOGD("app passed NULL surface");
     }
     sp <ICamera> c = mCamera;
     if (c == 0) return NO_INIT;
@@ -171,7 +171,7 @@
 }
 
 
-// start preview mode, must call setPreviewDisplay first
+// start preview mode
 status_t Camera::startPreview()
 {
     LOGV("startPreview");
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 7a33220..4a5063a 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -3845,7 +3845,7 @@
                             & Res_value::COMPLEX_RADIX_MASK];
     printf("%f", value);
     
-    if (isFraction) {
+    if (!isFraction) {
         switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
             case Res_value::COMPLEX_UNIT_PX: printf("px"); break;
             case Res_value::COMPLEX_UNIT_DIP: printf("dp"); break;
@@ -3864,6 +3864,49 @@
     }
 }
 
+void ResTable::print_value(const Package* pkg, const Res_value& value) const
+{
+    if (value.dataType == Res_value::TYPE_NULL) {
+        printf("(null)\n");
+    } else if (value.dataType == Res_value::TYPE_REFERENCE) {
+        printf("(reference) 0x%08x\n", value.data);
+    } else if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
+        printf("(attribute) 0x%08x\n", value.data);
+    } else if (value.dataType == Res_value::TYPE_STRING) {
+        size_t len;
+        const char16_t* str = pkg->header->values.stringAt(
+                value.data, &len);
+        if (str == NULL) {
+            printf("(string) null\n");
+        } else {
+            printf("(string) \"%s\"\n",
+                    String8(str, len).string());
+        } 
+    } else if (value.dataType == Res_value::TYPE_FLOAT) {
+        printf("(float) %g\n", *(const float*)&value.data);
+    } else if (value.dataType == Res_value::TYPE_DIMENSION) {
+        printf("(dimension) ");
+        print_complex(value.data, false);
+        printf("\n");
+    } else if (value.dataType == Res_value::TYPE_FRACTION) {
+        printf("(fraction) ");
+        print_complex(value.data, true);
+        printf("\n");
+    } else if (value.dataType >= Res_value::TYPE_FIRST_COLOR_INT
+            || value.dataType <= Res_value::TYPE_LAST_COLOR_INT) {
+        printf("(color) #%08x\n", value.data);
+    } else if (value.dataType == Res_value::TYPE_INT_BOOLEAN) {
+        printf("(boolean) %s\n", value.data ? "true" : "false");
+    } else if (value.dataType >= Res_value::TYPE_FIRST_INT
+            || value.dataType <= Res_value::TYPE_LAST_INT) {
+        printf("(int) 0x%08x or %d\n", value.data, value.data);
+    } else {
+        printf("(unknown type) t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)\n",
+               (int)value.dataType, (int)value.data,
+               (int)value.size, (int)value.res0);
+    }
+}
+
 void ResTable::print(bool inclValues) const
 {
     if (mError != 0) {
@@ -3985,27 +4028,31 @@
                             continue;
                         }
                         
-                        const Res_value* value = NULL;
+                        uint16_t esize = dtohs(ent->size);
+                        if ((esize&0x3) != 0) {
+                            printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize);
+                            continue;
+                        }
+                        if ((thisOffset+esize) > typeSize) {
+                            printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n",
+                                   (void*)entriesStart, (void*)thisOffset,
+                                   (void*)esize, (void*)typeSize);
+                            continue;
+                        }
+                            
+                        const Res_value* valuePtr = NULL;
+                        const ResTable_map_entry* bagPtr = NULL;
+                        Res_value value;
                         if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
                             printf("<bag>");
+                            bagPtr = (const ResTable_map_entry*)ent;
                         } else {
-                            uint16_t esize = dtohs(ent->size);
-                            if ((esize&0x3) != 0) {
-                                printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize);
-                                continue;
-                            }
-                            if ((thisOffset+esize) > typeSize) {
-                                printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n",
-                                       (void*)entriesStart, (void*)thisOffset,
-                                       (void*)esize, (void*)typeSize);
-                                continue;
-                            }
-                            
-                            value = (const Res_value*)
+                            valuePtr = (const Res_value*)
                                 (((const uint8_t*)ent) + esize);
+                            value.copyFrom_dtoh(*valuePtr);
                             printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
-                                   (int)value->dataType, (int)dtohl(value->data),
-                                   (int)dtohs(value->size), (int)value->res0);
+                                   (int)value.dataType, (int)value.data,
+                                   (int)value.size, (int)value.res0);
                         }
                         
                         if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
@@ -4014,44 +4061,23 @@
                         printf("\n");
                         
                         if (inclValues) {
-                            if (value != NULL) {
+                            if (valuePtr != NULL) {
                                 printf("          ");
-                                if (value->dataType == Res_value::TYPE_NULL) {
-                                    printf("(null)\n");
-                                } else if (value->dataType == Res_value::TYPE_REFERENCE) {
-                                    printf("(reference) 0x%08x\n", value->data);
-                                } else if (value->dataType == Res_value::TYPE_ATTRIBUTE) {
-                                    printf("(attribute) 0x%08x\n", value->data);
-                                } else if (value->dataType == Res_value::TYPE_STRING) {
-                                    size_t len;
-                                    const char16_t* str = pkg->header->values.stringAt(
-                                            value->data, &len);
-                                    if (str == NULL) {
-                                        printf("(string) null\n");
-                                    } else {
-                                        printf("(string) \"%s\"\n",
-                                                String8(str, len).string());
-                                    } 
-                                } else if (value->dataType == Res_value::TYPE_FLOAT) {
-                                    printf("(float) %g\n", *(const float*)&value->data);
-                                } else if (value->dataType == Res_value::TYPE_DIMENSION) {
-                                    printf("(dimension) ");
-                                    print_complex(value->data, false);
-                                    printf("\n");
-                                } else if (value->dataType == Res_value::TYPE_FRACTION) {
-                                    printf("(fraction) ");
-                                    print_complex(value->data, true);
-                                    printf("\n");
-                                } else if (value->dataType >= Res_value::TYPE_FIRST_COLOR_INT
-                                        || value->dataType <= Res_value::TYPE_LAST_COLOR_INT) {
-                                    printf("(color) #%08x\n", value->data);
-                                } else if (value->dataType == Res_value::TYPE_INT_BOOLEAN) {
-                                    printf("(boolean) %s\n", value->data ? "true" : "false");
-                                } else if (value->dataType >= Res_value::TYPE_FIRST_INT
-                                        || value->dataType <= Res_value::TYPE_LAST_INT) {
-                                    printf("(int) 0x%08x or %d\n", value->data, value->data);
-                                } else {
-                                    printf("(unknown type)\n");
+                                print_value(pkg, value);
+                            } else if (bagPtr != NULL) {
+                                const int N = dtohl(bagPtr->count);
+                                const ResTable_map* mapPtr = (const ResTable_map*)
+                                        (((const uint8_t*)ent) + esize);
+                                printf("          Parent=0x%08x, Count=%d\n",
+                                    dtohl(bagPtr->parent.ident), N);
+                                for (int i=0; i<N; i++) {
+                                    printf("          #%i (Key=0x%08x): ",
+                                        i, dtohl(mapPtr->name.ident));
+                                    value.copyFrom_dtoh(mapPtr->value);
+                                    print_value(pkg, value);
+                                    const size_t size = dtohs(mapPtr->value.size);
+                                    mapPtr = (ResTable_map*)(((const uint8_t*)mapPtr)
+                                            + size + sizeof(*mapPtr)-sizeof(mapPtr->value));
                                 }
                             }
                         }
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index edd1ea0..4a51e31 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -617,6 +617,9 @@
             synchronized(mListeners) {
                 mListeners.remove(this);
             }
+            if (mListener != null) {
+                mListener.asBinder().unlinkToDeath(this, 0);
+            }
         }
     }
 
diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/location/java/com/android/internal/location/LocationProviderProxy.java
index bd7088c..4ae424a 100644
--- a/location/java/com/android/internal/location/LocationProviderProxy.java
+++ b/location/java/com/android/internal/location/LocationProviderProxy.java
@@ -53,6 +53,12 @@
         }
     }
 
+    public void unlinkProvider() {
+        if (mProvider != null) {
+            mProvider.asBinder().unlinkToDeath(this, 0);
+        }
+    }
+
     public String getName() {
         return mName;
     }
@@ -255,5 +261,6 @@
     public void binderDied() {
         Log.w(TAG, "Location Provider " + mName + " died");
         mDead = true;
+        mProvider.asBinder().unlinkToDeath(this, 0);
     }
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index fd3a4ba..e76967d 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -501,4 +501,6 @@
       "http://sridharg.googlejunta.com/yslau/stress_media/mp3_regular.mp3";
   public static final String STREAM_MPEG4_QVGA_128k = 
       "http://sridharg.googlejunta.com/yslau/stress_media/mpeg4_qvga_24fps.3gp";
+  public static final int STREAM_H264_480_360_1411k_DURATION = 46000;
+  public static final int VIDEO_H263_AAC_DURATION = 501000;
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
index 12eacd3..ae9e102 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
@@ -19,6 +19,7 @@
 import android.test.InstrumentationTestRunner;
 import android.test.InstrumentationTestSuite;
 import com.android.mediaframeworktest.stress.MediaRecorderStressTest;
+import com.android.mediaframeworktest.stress.MediaPlayerStressTest;
 
 import junit.framework.TestSuite;
 
@@ -28,6 +29,7 @@
     public TestSuite getAllTests() {
         TestSuite suite = new InstrumentationTestSuite(this);
         suite.addTestSuite(MediaRecorderStressTest.class);
+        suite.addTestSuite(MediaPlayerStressTest.class);
         return suite;
     }
 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
new file mode 100644
index 0000000..5e213d7
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaPlayerStressTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.stress;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+
+import android.hardware.Camera;
+import android.media.MediaPlayer;
+import android.media.MediaRecorder;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import com.android.mediaframeworktest.MediaNames;
+
+import java.util.Random;
+
+/**
+ * Junit / Instrumentation test case for the media player
+ */
+public class MediaPlayerStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {    
+    private String TAG = "MediaPlayerStressTest";
+    private MediaRecorder mRecorder;
+    private Camera mCamera;
+
+    private static final int NUMBER_OF_RANDOM_REPOSITION_AND_PLAY = 10;
+    private static final int NUMBER_OF_RANDOM_REPOSITION_AND_PLAY_SHORT = 5;
+    private static final int NUMBER_OF_STRESS_LOOPS = 1000;
+    private static final int PLAYBACK_END_TOLERANCE = 5000;
+    private static final int WAIT_UNTIL_PLAYBACK_FINISH = 515000 ;
+
+    public MediaPlayerStressTest() {
+        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+    }
+
+    protected void setUp() throws Exception {
+        getActivity();
+        super.setUp();
+    }
+
+    @LargeTest
+    public void testStressHWDecoderRelease() throws Exception {
+        SurfaceHolder mSurfaceHolder;
+        long randomseed = System.currentTimeMillis(); 
+        Random generator = new Random(randomseed);
+        Log.v(TAG, "Random seed: " + randomseed);
+        int video_duration = MediaNames.STREAM_H264_480_360_1411k_DURATION;
+        int random_play_time;
+
+        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+        try {
+            for (int i = 0; i < NUMBER_OF_STRESS_LOOPS; i++) {
+                MediaPlayer mp = new MediaPlayer();
+                mp.setDataSource(MediaNames.STREAM_H264_480_360_1411k);
+                mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
+                mp.prepare();
+                mp.start();
+                // seek and play
+                for (int j = 0; j < generator.nextInt(10); j++) {
+                    random_play_time =
+                        generator.nextInt(MediaNames.STREAM_H264_480_360_1411k_DURATION / 2);
+                    Log.v(TAG, "Play time = " + random_play_time);
+                    Thread.sleep(random_play_time);
+                    int seek_time = MediaNames.STREAM_H264_480_360_1411k_DURATION / 2;
+                    Log.v(TAG, "Seek time = " + seek_time);
+                    mp.seekTo(seek_time);
+                }
+                mp.release();
+            }
+
+        } catch (Exception e) {
+            Log.v(TAG, e.toString());
+        }
+    }
+
+    @LargeTest
+    public void testStressGetCurrentPosition() throws Exception {
+        SurfaceHolder mSurfaceHolder;
+        long randomseed = System.currentTimeMillis(); 
+        Random generator = new Random(randomseed);
+        Log.v(TAG, "Random seed: " + randomseed);
+        int video_duration = MediaNames.VIDEO_H263_AAC_DURATION;
+        int random_play_time = 0;
+        int random_seek_time = 0;
+
+        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+        try {
+            for (int i = 0; i < NUMBER_OF_STRESS_LOOPS; i++) {
+                MediaPlayer mp = new MediaPlayer();
+                mp.setDataSource(MediaNames.VIDEO_H263_AMR);
+                mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
+                mp.prepare();
+                mp.start();
+                // Random seek and play
+                for (int j = 0; j < generator.nextInt(10); j++) {
+                    random_play_time =
+                        generator.nextInt(video_duration / 2);
+                    Log.v(TAG, "Play time = " + random_play_time);
+                    Thread.sleep(random_play_time);
+                    random_seek_time =
+                        generator.nextInt(video_duration / 2);
+                    Log.v(TAG, "Seek time = " + random_seek_time);
+                    mp.seekTo(random_seek_time);
+                }
+                //wait until the movie finish and check the current position
+                //Make sure the wait time is long enough
+                long wait_until_playback_finish = video_duration - random_seek_time + PLAYBACK_END_TOLERANCE * 2;
+                Thread.sleep(wait_until_playback_finish);
+                Log.v(TAG, "CurrentPosition = " + mp.getCurrentPosition());
+                if ( mp.isPlaying() || mp.getCurrentPosition() > (video_duration + PLAYBACK_END_TOLERANCE)){
+                    assertTrue("Current PlayTime greater than duration", false);
+                }
+                mp.release();
+            }
+
+        } catch (Exception e) {
+            Log.v(TAG, e.toString());
+        }
+    }
+}
+
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index c283418..f8adaa1 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -41,4 +41,7 @@
     <bool name="def_usb_mass_storage_enabled">true</bool>
     <bool name="def_wifi_on">false</bool>
     <bool name="def_networks_available_notification_on">true</bool>
+    
+    <bool name="def_backup_enabled">false</bool>
+    <string name="def_backup_transport"></string>
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 660b469..6dd1175 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -64,7 +64,7 @@
 
     private static final String TAG = "SettingsProvider";
     private static final String DATABASE_NAME = "settings.db";
-    private static final int DATABASE_VERSION = 34;
+    private static final int DATABASE_VERSION = 35;
 
     private Context mContext;
 
@@ -386,6 +386,20 @@
             upgradeVersion = 34;
         }
 
+        if (upgradeVersion == 34) {
+            db.beginTransaction();
+            try {
+                SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+                        + " VALUES(?,?);");
+                loadSecure35Settings(stmt);
+                stmt.close();
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+            upgradeVersion = 35;
+        }
+        
         if (upgradeVersion != currentVersion) {
             Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion
                     + ", must wipe the settings provider");
@@ -690,9 +704,19 @@
         loadSetting(stmt, Settings.Secure.ALLOW_MOCK_LOCATION,
                 "1".equals(SystemProperties.get("ro.allow.mock.location")) ? 1 : 0);
 
+        loadSecure35Settings(stmt);
+        
         stmt.close();
     }
 
+    private void loadSecure35Settings(SQLiteStatement stmt) {
+        loadBooleanSetting(stmt, Settings.Secure.BACKUP_ENABLED,
+                R.bool.def_backup_enabled);
+        
+        loadStringSetting(stmt, Settings.Secure.BACKUP_TRANSPORT,
+                R.string.def_backup_transport);
+    }
+    
     private void loadSetting(SQLiteStatement stmt, String key, Object value) {
         stmt.bindString(1, key);
         stmt.bindString(2, value.toString());
diff --git a/packages/TtsService/AndroidManifest.xml b/packages/TtsService/AndroidManifest.xml
index 1dc25c6..fab2534 100755
--- a/packages/TtsService/AndroidManifest.xml
+++ b/packages/TtsService/AndroidManifest.xml
@@ -12,4 +12,5 @@
         </service>
     </application>
     <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 </manifest>
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index 8537cae..a55b704 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -199,6 +199,7 @@
         if (wav == NULL) {
             delete pForAfter;
             LOGV("Null: speech has completed");
+            return TTS_CALLBACK_HALT;
         }
         if (bufferSize > 0){
             fwrite(wav, 1, bufferSize, pForAfter->outputFile);
@@ -213,8 +214,12 @@
         // this struct was allocated in the original android_tts_SynthProxy_speak call,
         // all processing matching this call is now done.
         LOGV("Speech synthesis done.");
-        delete pForAfter;
-        pForAfter = NULL;
+        if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY) {
+            // only delete for direct playback. When writing to a file, we still have work to do
+            // in android_tts_SynthProxy_synthesizeToFile. The struct will be deleted there.
+            delete pForAfter;
+            pForAfter = NULL;
+        }
         return TTS_CALLBACK_HALT;
     }
 
@@ -278,6 +283,33 @@
 }
 
 
+static int
+android_tts_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint jniData,
+        jstring language, jstring country, jstring variant)
+{
+    int result = TTS_LANG_NOT_SUPPORTED;
+
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_isLanguageAvailable(): invalid JNI data");
+        return result;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    const char *langNativeString = env->GetStringUTFChars(language, 0);
+    const char *countryNativeString = env->GetStringUTFChars(country, 0);
+    const char *variantNativeString = env->GetStringUTFChars(variant, 0);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        result = pSynthData->mNativeSynthInterface->isLanguageAvailable(langNativeString,
+                countryNativeString, variantNativeString);
+    }
+    env->ReleaseStringUTFChars(language, langNativeString);
+    env->ReleaseStringUTFChars(country, countryNativeString);
+    env->ReleaseStringUTFChars(variant, variantNativeString);
+    return result;
+}
+
+
 static void
 android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
         jstring language, jstring country, jstring variant)
@@ -370,7 +402,6 @@
 }
 
 
-// TODO: Refactor this to get rid of any assumptions about sample rate, etc.
 static void
 android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
         jstring textJavaString, jstring filenameJavaString)
@@ -381,6 +412,21 @@
     }
 
     SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    if (!pSynthData->mNativeSynthInterface) {
+        LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid engine handle");
+        return;
+    }
+
+    // Retrieve audio parameters before writing the file header
+    AudioSystem::audio_format encoding = DEFAULT_TTS_FORMAT;
+    uint32_t rate = DEFAULT_TTS_RATE;
+    int channels = DEFAULT_TTS_NB_CHANNELS;
+    pSynthData->mNativeSynthInterface->setAudioFormat(encoding, rate, channels);
+
+    if ((encoding != AudioSystem::PCM_16_BIT) && (encoding != AudioSystem::PCM_8_BIT)) {
+        LOGE("android_tts_SynthProxy_synthesizeToFile(): engine uses invalid format");
+        return;
+    }
 
     const char *filenameNativeString =
             env->GetStringUTFChars(filenameJavaString, 0);
@@ -392,6 +438,12 @@
 
     pForAfter->outputFile = fopen(filenameNativeString, "wb");
 
+    if (pForAfter->outputFile == NULL) {
+        LOGE("android_tts_SynthProxy_synthesizeToFile(): error creating output file");
+        delete pForAfter;
+        return;
+    }
+
     // Write 44 blank bytes for WAV header, then come back and fill them in
     // after we've written the audio data
     char header[44];
@@ -400,10 +452,8 @@
     unsigned int unique_identifier;
 
     // TODO check return codes
-    if (pSynthData->mNativeSynthInterface) {
-        pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, pSynthData->mBufferSize,
-                (void *)pForAfter);
-    }
+    pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer,
+            pSynthData->mBufferSize, (void *)pForAfter);
 
     long filelen = ftell(pForAfter->outputFile);
 
@@ -425,12 +475,14 @@
 
     ((uint32_t *)(&header[16]))[0] = 16;  // size of fmt
 
+    int sampleSizeInByte = (encoding == AudioSystem::PCM_16_BIT ? 2 : 1);
+
     ((unsigned short *)(&header[20]))[0] = 1;  // format
-    ((unsigned short *)(&header[22]))[0] = 1;  // channels
-    ((uint32_t *)(&header[24]))[0] = 22050;  // samplerate
-    ((uint32_t *)(&header[28]))[0] = 44100;  // byterate
-    ((unsigned short *)(&header[32]))[0] = 2;  // block align
-    ((unsigned short *)(&header[34]))[0] = 16;  // bits per sample
+    ((unsigned short *)(&header[22]))[0] = channels;  // channels
+    ((uint32_t *)(&header[24]))[0] = rate;  // samplerate
+    ((uint32_t *)(&header[28]))[0] = rate * sampleSizeInByte * channels;// byterate
+    ((unsigned short *)(&header[32]))[0] = sampleSizeInByte * channels;  // block align
+    ((unsigned short *)(&header[34]))[0] = sampleSizeInByte * 8;  // bits per sample
 
     header[36] = 'd';
     header[37] = 'a';
@@ -446,6 +498,9 @@
     fflush(pForAfter->outputFile);
     fclose(pForAfter->outputFile);
 
+    delete pForAfter;
+    pForAfter = NULL;
+
     env->ReleaseStringUTFChars(textJavaString, textNativeString);
     env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString);
 }
@@ -473,8 +528,8 @@
 
     if (pSynthData->mNativeSynthInterface) {
         const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
-        pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer, pSynthData->mBufferSize,
-                (void *)pForAfter);
+        pSynthData->mNativeSynthInterface->synthesizeText(textNativeString, pSynthData->mBuffer,
+                pSynthData->mBufferSize, (void *)pForAfter);
         env->ReleaseStringUTFChars(textJavaString, textNativeString);
     }
 }
@@ -533,23 +588,34 @@
 }
 
 
-JNIEXPORT jstring JNICALL
+static jobjectArray
 android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
 {
     if (jniData == 0) {
         LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data");
-        return env->NewStringUTF("");
+        return NULL;
     }
 
     SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    size_t bufSize = 100;
-    char buf[bufSize];
-    memset(buf, 0, bufSize);
-    // TODO check return codes
+
     if (pSynthData->mNativeSynthInterface) {
-        pSynthData->mNativeSynthInterface->getLanguage(buf, &bufSize);
+        size_t bufSize = 100;
+        char lang[bufSize];
+        char country[bufSize];
+        char variant[bufSize];
+        memset(lang, 0, bufSize);
+        memset(country, 0, bufSize);
+        memset(variant, 0, bufSize);
+        jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3,
+                env->FindClass("java/lang/String"), env->NewStringUTF(""));
+        pSynthData->mNativeSynthInterface->getLanguage(lang, country, variant);
+        env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang));
+        env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country));
+        env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant));
+        return retLocale;
+    } else {
+        return NULL;
     }
-    return env->NewStringUTF(buf);
 }
 
 
@@ -587,6 +653,10 @@
         "(ILjava/lang/String;Ljava/lang/String;)V",
         (void*)android_tts_SynthProxy_synthesizeToFile
     },
+    {   "native_isLanguageAvailable",
+        "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+        (void*)android_tts_SynthProxy_isLanguageAvailable
+    },
     {   "native_setLanguage",
         "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
         (void*)android_tts_SynthProxy_setLanguage
@@ -608,7 +678,7 @@
         (void*)android_tts_SynthProxy_playAudioBuffer
     },
     {   "native_getLanguage",
-        "(I)Ljava/lang/String;",
+        "(I)[Ljava/lang/String;",
         (void*)android_tts_SynthProxy_getLanguage
     },
     {   "native_getRate",
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
index a8eaaa43..91fe3b7 100755
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ b/packages/TtsService/src/android/tts/SynthProxy.java
@@ -68,12 +68,20 @@
     // TODO add IPA methods
 
     /**
-     * Sets the language
+     * Queries for language support.
+     * Return codes are defined in android.speech.tts.TextToSpeech
+     */
+    public int isLanguageAvailable(String language, String country, String variant) {
+        return native_isLanguageAvailable(mJniData, language, country, variant);
+    }
+
+    /**
+     * Sets the language.
      */
     public void setLanguage(String language, String country, String variant) {
         native_setLanguage(mJniData, language, country, variant);
     }
-    
+
     /**
      * Loads the language: it's not set, but prepared for use later.
      */
@@ -82,42 +90,42 @@
     }
 
     /**
-     * Sets the speech rate
+     * Sets the speech rate.
      */
     public final void setSpeechRate(int speechRate) {
         native_setSpeechRate(mJniData, speechRate);
     }
 
     /**
-     * Sets the pitch of the synthesized voice
+     * Sets the pitch of the synthesized voice.
      */
     public final void setPitch(int pitch) {
         native_setPitch(mJniData, pitch);
     }
 
     /**
-     * Plays the given audio buffer
+     * Plays the given audio buffer.
      */
     public void playAudioBuffer(int bufferPointer, int bufferSize) {
         native_playAudioBuffer(mJniData, bufferPointer, bufferSize);
     }
 
     /**
-     * Gets the currently set language
+     * Returns the currently set language, country and variant information.
      */
-    public String getLanguage() {
+    public String[] getLanguage() {
         return native_getLanguage(mJniData);
     }
 
     /**
-     * Gets the currently set rate
+     * Gets the currently set rate.
      */
     public int getRate() {
         return native_getRate(mJniData);
     }
 
     /**
-     * Shuts down the native synthesizer
+     * Shuts down the native synthesizer.
      */
     public void shutdown()  {
         native_shutdown(mJniData);
@@ -154,20 +162,23 @@
 
     private native final void native_synthesizeToFile(int jniData, String text, String filename);
 
+    private native final int  native_isLanguageAvailable(int jniData, String language,
+            String country, String variant);
+
     private native final void native_setLanguage(int jniData, String language, String country,
             String variant);
-    
+
     private native final void native_loadLanguage(int jniData, String language, String country,
             String variant);
 
     private native final void native_setSpeechRate(int jniData, int speechRate);
-    
+
     private native final void native_setPitch(int jniData, int speechRate);
 
     // TODO add buffer format
     private native final void native_playAudioBuffer(int jniData, int bufferPointer, int bufferSize);
 
-    private native final String native_getLanguage(int jniData);
+    private native final String[] native_getLanguage(int jniData);
 
     private native final int native_getRate(int jniData);
 
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index 421b2ca..b5f5b37 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -51,10 +51,13 @@
         public static final int IPA = 1;
         public static final int EARCON = 2;
         public static final int SILENCE = 3;
+        public static final int TEXT_TO_FILE = 5;
+        public static final int IPA_TO_FILE = 6;
         public String mText = null;
         public ArrayList<String> mParams = null;
         public int mType = TEXT;
         public long mDuration = 0;
+        public String mFilename = null;
 
         public SpeechItem(String text, ArrayList<String> params, int itemType) {
             mText = text;
@@ -65,6 +68,14 @@
         public SpeechItem(long silenceTime) {
             mDuration = silenceTime;
         }
+
+        public SpeechItem(String text, ArrayList<String> params, int itemType, String filename) {
+            mText = text;
+            mParams = params;
+            mType = itemType;
+            mFilename = filename;
+        }
+
     }
 
     /**
@@ -91,6 +102,7 @@
     }
 
     private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
+    private static final int MAX_FILENAME_LENGTH = 250;
 
     private static final String ACTION = "android.intent.action.USE_TTS";
     private static final String CATEGORY = "android.intent.category.TTS";
@@ -147,8 +159,7 @@
 
 
     private void setDefaultSettings() {
-        // TODO handle default language
-        setLanguage("eng", "USA", "");
+        setLanguage(this.getDefaultLanguage(), getDefaultCountry(), getDefaultLocVariant());
 
         // speech rate
         setSpeechRate(getDefaultRate());
@@ -217,6 +228,17 @@
     }
 
 
+    private int isLanguageAvailable(String lang, String country, String variant) {
+        Log.v("TTS", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")");
+        return nativeSynth.isLanguageAvailable(lang, country, variant);
+    }
+
+
+    private String[] getLanguage() {
+        return nativeSynth.getLanguage();
+    }
+
+
     private void setLanguage(String lang, String country, String variant) {
         Log.v("TTS", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
         if (isDefaultEnforced()) {
@@ -414,6 +436,26 @@
                         synth.start();
                         return;
                     }
+                    if (params != null){
+                        String language = "";
+                        String country = "";
+                        String variant = "";
+                        for (int i = 0; i < params.size() - 1; i = i + 2){
+                            String param = params.get(i);
+                            if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){
+                                setSpeechRate(Integer.parseInt(params.get(i+1)));
+                            } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){
+                                language = params.get(i+1);
+                            } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){
+                                country = params.get(i+1);
+                            } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){
+                                variant = params.get(i+1);
+                            }
+                        }
+                        if (language.length() > 0){
+                            setLanguage(language, country, variant);
+                        }
+                    }
                     nativeSynth.speak(text);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
@@ -433,6 +475,60 @@
         synth.start();
     }
 
+    private void synthToFileInternalOnly(final String text,
+            final ArrayList<String> params, final String filename) {
+        class SynthThread implements Runnable {
+            public void run() {
+                Log.i("TTS", "Synthesizing to " + filename);
+                boolean synthAvailable = false;
+                try {
+                    synthAvailable = synthesizerLock.tryLock();
+                    if (!synthAvailable) {
+                        Thread.sleep(100);
+                        Thread synth = (new Thread(new SynthThread()));
+                        synth.setPriority(Thread.MIN_PRIORITY);
+                        synth.start();
+                        return;
+                    }
+                    if (params != null){
+                        String language = "";
+                        String country = "";
+                        String variant = "";
+                        for (int i = 0; i < params.size() - 1; i = i + 2){
+                            String param = params.get(i);
+                            if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){
+                                setSpeechRate(Integer.parseInt(params.get(i+1)));
+                            } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){
+                                language = params.get(i+1);
+                            } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){
+                                country = params.get(i+1);
+                            } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){
+                                variant = params.get(i+1);
+                            }
+                        }
+                        if (language.length() > 0){
+                            setLanguage(language, country, variant);
+                        }
+                    }
+                    nativeSynth.synthesizeToFile(text, filename);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                } finally {
+                    // This check is needed because finally will always run;
+                    // even if the
+                    // method returns somewhere in the try block.
+                    if (synthAvailable) {
+                        synthesizerLock.unlock();
+                    }
+                    processSpeechQueue();
+                }
+            }
+        }
+        Thread synth = (new Thread(new SynthThread()));
+        synth.setPriority(Thread.MIN_PRIORITY);
+        synth.start();
+    }
+
     private SoundResource getSoundResource(SpeechItem speechItem) {
         SoundResource sr = null;
         String text = speechItem.mText;
@@ -518,6 +614,9 @@
                     currentSpeechItem = splitCurrentTextIfNeeded(currentSpeechItem);
                     speakInternalOnly(currentSpeechItem.mText,
                             currentSpeechItem.mParams);
+                } else if (currentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
+                    synthToFileInternalOnly(currentSpeechItem.mText,
+                            currentSpeechItem.mParams, currentSpeechItem.mFilename);
                 } else if (currentSpeechItem.mType == SpeechItem.IPA) {
                     // TODO Implement IPA support
                 } else {
@@ -598,35 +697,20 @@
      * @return A boolean that indicates if the synthesis succeeded
      */
     private boolean synthesizeToFile(String text, ArrayList<String> params,
-            String filename, boolean calledFromApi) {
-        // Only stop everything if this is a call made by an outside app trying
-        // to
-        // use the API. Do NOT stop if this is a call from within the service as
-        // clearing the speech queue here would be a mistake.
-        if (calledFromApi) {
-            stop();
+            String filename) {
+        // Don't allow a filename that is too long
+        if (filename.length() > MAX_FILENAME_LENGTH) {
+            return false;
         }
-        Log.i("TTS", "Synthesizing to " + filename);
-        boolean synthAvailable = false;
-        try {
-            synthAvailable = synthesizerLock.tryLock();
-            if (!synthAvailable) {
-                return false;
-            }
-            // Don't allow a filename that is too long
-            // TODO use platform constant
-            if (filename.length() > 250) {
-                return false;
-            }
-            nativeSynth.synthesizeToFile(text, filename);
-        } finally {
-            // This check is needed because finally will always run; even if the
-            // method returns somewhere in the try block.
-            if (synthAvailable) {
-                synthesizerLock.unlock();
-            }
+        // Don't allow anything longer than the max text length; since this
+        // is synthing to a file, don't even bother splitting it.
+        if (text.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){
+            return false;
         }
-        Log.i("TTS", "Completed synthesis for " + filename);
+        mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.TEXT_TO_FILE, filename));
+        if (!mIsSpeaking) {
+            processSpeechQueue();
+        }
         return true;
     }
 
@@ -660,8 +744,7 @@
                 return false;
             }
             // Don't allow a filename that is too long
-            // TODO use platform constant
-            if (filename.length() > 250) {
+            if (filename.length() > MAX_FILENAME_LENGTH) {
                 return false;
             }
             // TODO: Add nativeSynth.synthesizeIpaToFile(text, filename);
@@ -874,6 +957,30 @@
         }
 
         /**
+         * Returns the level of support for the specified language.
+         *
+         * @param lang  the three letter ISO language code.
+         * @param country  the three letter ISO country code.
+         * @param variant  the variant code associated with the country and language pair.
+         * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
+         *      TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE as defined in
+         *      android.speech.tts.TextToSpeech.
+         */
+        public int isLanguageAvailable(String lang, String country, String variant) {
+            return mSelf.isLanguageAvailable(lang, country, variant);
+        }
+
+        /**
+         * Returns the currently set language / country / variant strings representing the
+         * language used by the TTS engine.
+         * @return null is no language is set, or an array of 3 string containing respectively
+         *      the language, country and variant.
+         */
+        public String[] getLanguage() {
+            return mSelf.getLanguage();
+        }
+
+        /**
          * Sets the speech rate for the TTS, which affects the synthesized voice.
          *
          * @param lang  the three letter ISO language code.
@@ -904,7 +1011,7 @@
             if (params != null) {
                 speakingParams = new ArrayList<String>(Arrays.asList(params));
             }
-            return mSelf.synthesizeToFile(text, speakingParams, filename, true);
+            return mSelf.synthesizeToFile(text, speakingParams, filename);
         }
 
         /**
diff --git a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
index 2ad218f..7dd9d9e 100644
--- a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
@@ -78,20 +78,10 @@
 
     /**
      * Sends a command with arguments to the service through the control socket.
-     * Each argument is sent as a C-style zero-terminated string.
      */
     public void sendCommand(String ...args) throws IOException {
         OutputStream out = getControlSocketOutput();
         for (String arg : args) outputString(out, arg);
-        checkSocketResult();
-    }
-
-    /**
-     * Sends a command with arguments to the service through the control socket.
-     */
-    public void sendCommand2(String ...args) throws IOException {
-        OutputStream out = getControlSocketOutput();
-        for (String arg : args) outputString2(out, arg);
         out.write(END_OF_ARGUMENTS);
         out.flush();
         checkSocketResult();
@@ -128,8 +118,9 @@
 
                         if (data == 0) {
                             // re-establish the connection:
-                            // synchronized here so that checkSocketResult() returns
-                            // when new mKeepaliveSocket is available for next cmd
+                            // synchronized here so that checkSocketResult()
+                            // returns when new mKeepaliveSocket is available for
+                            // next cmd
                             synchronized (this) {
                                 setResultAndCloseControlSocket((byte) data);
                                 s = mKeepaliveSocket = createServiceSocket();
@@ -244,12 +235,6 @@
     }
 
     private void outputString(OutputStream out, String s) throws IOException {
-        out.write(s.getBytes());
-        out.write(0);
-        out.flush();
-    }
-
-    private void outputString2(OutputStream out, String s) throws IOException {
         byte[] bytes = s.getBytes();
         out.write(bytes.length);
         out.write(bytes);
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java
new file mode 100644
index 0000000..6abf81c
--- /dev/null
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vpn;
+
+import android.net.vpn.L2tpIpsecPskProfile;
+
+import java.io.IOException;
+
+/**
+ * The service that manages the preshared key based L2TP-over-IPSec VPN
+ * connection.
+ */
+class L2tpIpsecPskService extends VpnService<L2tpIpsecPskProfile> {
+    private static final String IPSEC_DAEMON = "racoon";
+
+    @Override
+    protected void connect(String serverIp, String username, String password)
+            throws IOException {
+        String hostIp = getHostIp();
+        L2tpIpsecPskProfile p = getProfile();
+
+        // IPSEC
+        AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON);
+        ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT,
+                p.getPresharedKey());
+
+        sleep(2000); // 2 seconds
+
+        // L2TP
+        MtpdHelper.sendCommand(this, L2tpService.L2TP_DAEMON, serverIp,
+                L2tpService.L2TP_PORT,
+                (p.isSecretEnabled() ? p.getSecretString() : null),
+                username, password);
+    }
+}
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
index 877fa6b..bd14110 100644
--- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
@@ -22,7 +22,7 @@
 import java.io.IOException;
 
 /**
- * The service that manages the L2TP-over-IPSec VPN connection.
+ * The service that manages the certificate based L2TP-over-IPSec VPN connection.
  */
 class L2tpIpsecService extends VpnService<L2tpIpsecProfile> {
     private static final String IPSEC_DAEMON = "racoon";
@@ -34,11 +34,10 @@
 
         // IPSEC
         AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON);
-        ipsecService.sendCommand(
-                String.format("SETKEY %s %s", hostIp, serverIp));
-        ipsecService.sendCommand(String.format("SET_CERTS %s %s %s %s",
-                serverIp, getCaCertPath(), getUserCertPath(),
-                getUserkeyPath()));
+        ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT,
+                getUserkeyPath(), getUserCertPath(), getCaCertPath());
+
+        sleep(2000); // 2 seconds
 
         // L2TP
         L2tpIpsecProfile p = getProfile();
diff --git a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
index 6160900..16d253a 100644
--- a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
+++ b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
@@ -38,7 +38,7 @@
         addPppArguments(vpnService, args, serverIp, username, password);
 
         AndroidServiceProxy mtpd = vpnService.startService(MTPD_SERVICE);
-        mtpd.sendCommand2(args.toArray(new String[args.size()]));
+        mtpd.sendCommand(args.toArray(new String[args.size()]));
     }
 
     private static void addPppArguments(VpnService<?> vpnService,
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
index 44127ff..6e5d46b 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
@@ -24,14 +24,12 @@
 import android.net.vpn.VpnManager;
 import android.net.vpn.VpnProfile;
 import android.net.vpn.VpnState;
-import android.os.FileObserver;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.Log;
 
-import java.io.File;
 import java.io.IOException;
 import java.net.InetAddress;
-import java.net.InetSocketAddress;
 import java.net.NetworkInterface;
 import java.net.Socket;
 import java.util.ArrayList;
@@ -43,21 +41,18 @@
  */
 abstract class VpnService<E extends VpnProfile> {
     private static final int NOTIFICATION_ID = 1;
-    private static final String PROFILES_ROOT = VpnManager.PROFILES_PATH + "/";
-    public static final String DEFAULT_CONFIG_PATH = "/etc";
 
-    private static final int DNS_TIMEOUT = 3000; // ms
     private static final String DNS1 = "net.dns1";
     private static final String DNS2 = "net.dns2";
+    private static final String VPN_DNS1 = "vpn.dns1";
+    private static final String VPN_DNS2 = "vpn.dns2";
+    private static final String VPN_UP = "vpn.up";
+    private static final String VPN_IS_UP = "1";
+    private static final String VPN_IS_DOWN = "0";
+
     private static final String REMOTE_IP = "net.ipremote";
     private static final String DNS_DOMAIN_SUFFICES = "net.dns.search";
-    private static final String SERVER_IP = "net.vpn.server_ip";
 
-    private static final int VPN_TIMEOUT = 30000; // milliseconds
-    private static final int ONE_SECOND = 1000; // milliseconds
-    private static final int FIVE_SECOND = 5000; // milliseconds
-
-    private static final String LOGWRAPPER = "/system/bin/logwrapper";
     private final String TAG = VpnService.class.getSimpleName();
 
     E mProfile;
@@ -76,13 +71,6 @@
 
     private long mStartTime; // VPN connection start time
 
-    // monitors if the VPN connection is sucessfully established
-    private FileMonitor mConnectMonitor;
-
-    // watch dog timer; fired up if the connection cannot be established within
-    // VPN_TIMEOUT
-    private Object mWatchdog;
-
     // for helping managing multiple Android services
     private ServiceHelper mServiceHelper = new ServiceHelper();
 
@@ -110,19 +98,6 @@
         return mServiceHelper.startService(serviceName);
     }
 
-    protected String getPppOptionFilePath() throws IOException {
-        String subpath = getProfileSubpath("/ppp/peers");
-        String[] kids = new File(subpath).list();
-        if ((kids == null) || (kids.length == 0)) {
-            throw new IOException("no option file found in " + subpath);
-        }
-        if (kids.length > 1) {
-            Log.w(TAG, "more than one option file found in " + subpath
-                    + ", arbitrarily choose " + kids[0]);
-        }
-        return subpath + "/" + kids[0];
-    }
-
     /**
      * Returns the VPN profile associated with the connection.
      */
@@ -131,23 +106,6 @@
     }
 
     /**
-     * Returns the profile path where configuration files reside.
-     */
-    protected String getProfilePath() throws IOException {
-        String path = PROFILES_ROOT + mProfile.getId();
-        File dir = new File(path);
-        if (!dir.exists()) throw new IOException("Profile dir does not exist");
-        return path;
-    }
-
-    /**
-     * Returns the path where default configuration files reside.
-     */
-    protected String getDefaultConfigPath() throws IOException {
-        return DEFAULT_CONFIG_PATH;
-    }
-
-    /**
      * Returns the host IP for establishing the VPN connection.
      */
     protected String getHostIp() throws IOException {
@@ -178,14 +136,6 @@
     }
 
     /**
-     * Returns the path of the script file that is executed when the VPN
-     * connection is established.
-     */
-    protected String getConnectMonitorFile() {
-        return "/etc/ppp/ip-up-vpn";
-    }
-
-    /**
      * Sets the system property. The method is blocked until the value is
      * settled in.
      * @param name the name of the property
@@ -222,10 +172,10 @@
         broadcastConnectivity(VpnState.CONNECTING);
 
         String serverIp = getIp(getProfile().getServerName());
-        setSystemProperty(SERVER_IP, serverIp);
-        onBeforeConnect();
 
+        onBeforeConnect();
         connect(serverIp, username, password);
+        waitUntilConnectedOrTimedout();
     }
 
     synchronized void onDisconnect(boolean cleanUpServices) {
@@ -259,39 +209,36 @@
         }
     }
 
-    private void createConnectMonitor() {
-        mConnectMonitor = new FileMonitor(getConnectMonitorFile(),
-                new Runnable() {
-                    public void run() {
-                        onConnectMonitorTriggered();
-                    }
-                });
-    }
-
     private void onBeforeConnect() {
         mNotification.disableNotification();
 
-        createConnectMonitor();
-        mConnectMonitor.startWatching();
-        saveOriginalDnsProperties();
-
-        mWatchdog = startTimer(VPN_TIMEOUT, new Runnable() {
-            public void run() {
-                synchronized (VpnService.this) {
-                    if (mState == VpnState.CONNECTING) {
-                        Log.d(TAG, "       watchdog timer is fired !!");
-                        onError();
-                    }
-                }
-            }
-        });
+        SystemProperties.set(VPN_DNS1, "-");
+        SystemProperties.set(VPN_DNS2, "-");
+        SystemProperties.set(VPN_UP, VPN_IS_DOWN);
+        Log.d(TAG, "       VPN UP: " + SystemProperties.get(VPN_UP));
     }
 
-    private synchronized void onConnectMonitorTriggered() {
-        Log.d(TAG, "onConnectMonitorTriggered()");
+    private void waitUntilConnectedOrTimedout() {
+        sleep(2000); // 2 seconds
+        for (int i = 0; i < 60; i++) {
+            if (VPN_IS_UP.equals(SystemProperties.get(VPN_UP))) {
+                onConnected();
+                return;
+            }
+            sleep(500); // 0.5 second
+        }
 
-        stopTimer(mWatchdog);
-        mConnectMonitor.stopWatching();
+        synchronized (this) {
+            if (mState == VpnState.CONNECTING) {
+                Log.d(TAG, "       connecting timed out !!");
+                onError();
+            }
+        }
+    }
+
+    private synchronized void onConnected() {
+        Log.d(TAG, "onConnected()");
+
         saveVpnDnsProperties();
         saveAndSetDomainSuffices();
         startConnectivityMonitor();
@@ -310,8 +257,6 @@
 
         restoreOriginalDnsProperties();
         restoreOriginalDomainSuffices();
-        if (mConnectMonitor != null) mConnectMonitor.stopWatching();
-        if (mWatchdog != null) stopTimer(mWatchdog);
         mState = VpnState.IDLE;
         broadcastConnectivity(VpnState.IDLE);
 
@@ -345,13 +290,6 @@
         }
     }
 
-    private void saveOriginalDnsProperties() {
-        mOriginalDns1 = SystemProperties.get(DNS1);
-        mOriginalDns2 = SystemProperties.get(DNS2);
-        Log.d(TAG, String.format("save original dns prop: %s, %s",
-                mOriginalDns1, mOriginalDns2));
-    }
-
     private void restoreOriginalDnsProperties() {
         // restore only if they are not overridden
         if (mVpnDns1.equals(SystemProperties.get(DNS1))) {
@@ -365,15 +303,21 @@
     }
 
     private void saveVpnDnsProperties() {
-        mVpnDns1 = mVpnDns2 = "";
+        mOriginalDns1 = mOriginalDns2 = "";
         for (int i = 0; i < 10; i++) {
-            mVpnDns1 = SystemProperties.get(DNS1);
-            mVpnDns2 = SystemProperties.get(DNS2);
-            if (mVpnDns1.equals(mOriginalDns1)) {
+            mVpnDns1 = SystemProperties.get(VPN_DNS1);
+            mVpnDns2 = SystemProperties.get(VPN_DNS2);
+            if (mOriginalDns1.equals(mVpnDns1)) {
                 Log.d(TAG, "wait for vpn dns to settle in..." + i);
                 sleep(500);
             } else {
-                Log.d(TAG, String.format("save vpn dns prop: %s, %s",
+                mOriginalDns1 = SystemProperties.get(DNS1);
+                mOriginalDns2 = SystemProperties.get(DNS2);
+                SystemProperties.set(DNS1, mVpnDns1);
+                SystemProperties.set(DNS2, mVpnDns2);
+                Log.d(TAG, String.format("save original dns prop: %s, %s",
+                        mOriginalDns1, mOriginalDns2));
+                Log.d(TAG, String.format("set vpn dns prop: %s, %s",
                         mVpnDns1, mVpnDns2));
                 return;
             }
@@ -381,23 +325,11 @@
         Log.e(TAG, "saveVpnDnsProperties(): DNS not updated??");
     }
 
-    private void restoreVpnDnsProperties() {
-        if (isNullOrEmpty(mVpnDns1) && isNullOrEmpty(mVpnDns2)) {
-            return;
-        }
-        Log.d(TAG, String.format("restore vpn dns prop: %s --> %s",
-                SystemProperties.get(DNS1), mVpnDns1));
-        Log.d(TAG, String.format("restore vpn dns prop: %s --> %s",
-                SystemProperties.get(DNS2), mVpnDns2));
-        SystemProperties.set(DNS1, mVpnDns1);
-        SystemProperties.set(DNS2, mVpnDns2);
-    }
-
     private void saveAndSetDomainSuffices() {
         mOriginalDomainSuffices = SystemProperties.get(DNS_DOMAIN_SUFFICES);
         Log.d(TAG, "save original dns search: " + mOriginalDomainSuffices);
         String list = mProfile.getDomainSuffices();
-        if (!isNullOrEmpty(list)) {
+        if (!TextUtils.isEmpty(list)) {
             SystemProperties.set(DNS_DOMAIN_SUFFICES, list);
         }
     }
@@ -423,7 +355,7 @@
                             if (mState != VpnState.CONNECTED) break;
                             mNotification.update();
                             checkConnectivity();
-                            VpnService.this.wait(ONE_SECOND);
+                            VpnService.this.wait(1000); // 1 second
                         }
                     }
                 } catch (InterruptedException e) {
@@ -446,32 +378,6 @@
         }
     }
 
-    private Object startTimer(final int milliseconds, final Runnable task) {
-        Thread thread = new Thread(new Runnable() {
-            public void run() {
-                Log.d(TAG, "watchdog timer started");
-                Thread t = Thread.currentThread();
-                try {
-                    synchronized (t) {
-                        t.wait(milliseconds);
-                    }
-                    task.run();
-                } catch (InterruptedException e) {
-                    // ignored
-                }
-                Log.d(TAG, "watchdog timer stopped");
-            }
-        });
-        thread.start();
-        return thread;
-    }
-
-    private void stopTimer(Object timer) {
-        synchronized (timer) {
-            timer.notify();
-        }
-    }
-
     private String reallyGetHostIp() throws IOException {
         Enumeration<NetworkInterface> ifces =
                 NetworkInterface.getNetworkInterfaces();
@@ -487,33 +393,13 @@
         throw new IOException("Host IP is not available");
     }
 
-    private String getProfileSubpath(String subpath) throws IOException {
-        String path = getProfilePath() + subpath;
-        if (new File(path).exists()) {
-            return path;
-        } else {
-            Log.w(TAG, "Profile subpath does not exist: " + path
-                    + ", use default one");
-            String path2 = getDefaultConfigPath() + subpath;
-            if (!new File(path2).exists()) {
-                throw new IOException("Profile subpath does not exist at "
-                        + path + " or " + path2);
-            }
-            return path2;
-        }
-    }
-
-    private void sleep(int ms) {
+    protected void sleep(int ms) {
         try {
             Thread.currentThread().sleep(ms);
         } catch (InterruptedException e) {
         }
     }
 
-    private static boolean isNullOrEmpty(String message) {
-        return ((message == null) || (message.length() == 0));
-    }
-
     private InetAddress toInetAddress(int addr) throws IOException {
         byte[] aa = new byte[4];
         for (int i= 0; i < aa.length; i++) {
@@ -564,20 +450,6 @@
         }
     }
 
-    private class FileMonitor extends FileObserver {
-        private Runnable mCallback;
-
-        FileMonitor(String path, Runnable callback) {
-            super(path, CLOSE_NOWRITE);
-            mCallback = callback;
-        }
-
-        @Override
-        public void onEvent(int event, String path) {
-            if ((event & CLOSE_NOWRITE) > 0) mCallback.run();
-        }
-    }
-
     // Helper class for showing, updating notification.
     private class NotificationHelper {
         void update() {
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index c50ae94..131e156 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -29,7 +29,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.ResolveInfo;
-import android.content.pm.PackageItemInfo;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.net.Uri;
@@ -40,6 +39,7 @@
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.TypedValue;
 import android.util.Xml;
 import android.widget.RemoteViews;
 
@@ -56,7 +56,6 @@
 
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.appwidget.IAppWidgetHost;
-import com.android.internal.util.XmlUtils;
 import com.android.internal.util.FastXmlSerializer;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -79,7 +78,7 @@
     static class Provider {
         int uid;
         AppWidgetProviderInfo info;
-        ArrayList<AppWidgetId> instances = new ArrayList();
+        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
         PendingIntent broadcast;
         boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
         
@@ -90,7 +89,7 @@
         int uid;
         int hostId;
         String packageName;
-        ArrayList<AppWidgetId> instances = new ArrayList();
+        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
         IAppWidgetHost callbacks;
         boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
         
@@ -107,10 +106,10 @@
     Context mContext;
     PackageManager mPackageManager;
     AlarmManager mAlarmManager;
-    ArrayList<Provider> mInstalledProviders = new ArrayList();
+    ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
     int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
-    ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList();
-    ArrayList<Host> mHosts = new ArrayList();
+    final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
+    ArrayList<Host> mHosts = new ArrayList<Host>();
     boolean mSafeMode;
 
     AppWidgetService(Context context) {
@@ -174,7 +173,7 @@
             for (int i=0; i<N; i++) {
                 AppWidgetId id = mAppWidgetIds.get(i);
                 pw.print("  ["); pw.print(i); pw.print("] id=");
-                        pw.println(id.appWidgetId);;
+                        pw.println(id.appWidgetId);
                 pw.print("    hostId=");
                         pw.print(id.host.hostId); pw.print(' ');
                         pw.print(id.host.packageName); pw.print('/');
@@ -384,7 +383,7 @@
     public List<AppWidgetProviderInfo> getInstalledProviders() {
         synchronized (mAppWidgetIds) {
             final int N = mInstalledProviders.size();
-            ArrayList<AppWidgetProviderInfo> result = new ArrayList(N);
+            ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
             for (int i=0; i<N; i++) {
                 Provider p = mInstalledProviders.get(i);
                 if (!p.zombie) {
@@ -619,7 +618,6 @@
             // rely on the fact that we've already set it and that
             // PendingIntent.getBroadcast will update the extras.
             boolean alreadyRegistered = p.broadcast != null;
-            int instancesSize = p.instances.size();
             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
             intent.setComponent(p.info.provider);
@@ -695,10 +693,16 @@
 
             TypedArray sa = mContext.getResources().obtainAttributes(attrs,
                     com.android.internal.R.styleable.AppWidgetProviderInfo);
-            info.minWidth = sa.getDimensionPixelSize(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth, 0);
-            info.minHeight = sa.getDimensionPixelSize(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight, 0);
+            
+            // These dimensions has to be resolved in the application's context.
+            // We simply send back the raw complex data, which will be
+            // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
+            TypedValue value = sa.peekValue(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
+            info.minWidth = value != null ? value.data : 0; 
+            value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
+            info.minHeight = value != null ? value.data : 0;
+                    
             info.updatePeriodMillis = sa.getInt(
                     com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
             info.initialLayout = sa.getResourceId(
@@ -773,10 +777,12 @@
         if (real.exists()) {
             readStateFromFileLocked(real);
             if (temp.exists()) {
+                //noinspection ResultOfMethodCallIgnored
                 temp.delete();
             }
         } else if (temp.exists()) {
             readStateFromFileLocked(temp);
+            //noinspection ResultOfMethodCallIgnored
             temp.renameTo(real);
         }
     }
@@ -792,18 +798,23 @@
             // use the temporary one until it's fully written, create an empty file
             // for real, which will we'll shortly delete.
             try {
+                //noinspection ResultOfMethodCallIgnored
                 real.createNewFile();
             } catch (IOException e) {
+                // Ignore
             }
         }
 
         if (temp.exists()) {
+            //noinspection ResultOfMethodCallIgnored
             temp.delete();
         }
 
         writeStateToFileLocked(temp);
 
+        //noinspection ResultOfMethodCallIgnored
         real.delete();
+        //noinspection ResultOfMethodCallIgnored
         temp.renameTo(real);
     }
 
@@ -866,8 +877,10 @@
                     stream.close();
                 }
             } catch (IOException ex) {
+                // Ignore
             }
             if (file.exists()) {
+                //noinspection ResultOfMethodCallIgnored
                 file.delete();
             }
         }
@@ -885,7 +898,7 @@
 
             int type;
             int providerIndex = 0;
-            HashMap<Integer,Provider> loadedProviders = new HashMap();
+            HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
             do {
                 type = parser.next();
                 if (type == XmlPullParser.START_TAG) {
@@ -986,6 +999,7 @@
                 stream.close();
             }
         } catch (IOException e) {
+            // Ignore
         }
 
         if (success) {
@@ -1081,7 +1095,7 @@
     // TODO: If there's a better way of matching an intent filter against the
     // packages for a given package, use that.
     void updateProvidersForPackageLocked(String pkgName) {
-        HashSet<String> keep = new HashSet();
+        HashSet<String> keep = new HashSet<String>();
         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
         List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
                 PackageManager.GET_META_DATA);
@@ -1103,7 +1117,6 @@
                     if (parsed != null) {
                         keep.add(ai.name);
                         // Use the new AppWidgetProviderInfo.
-                        AppWidgetProviderInfo oldInfo = p.info;
                         p.info = parsed.info;
                         // If it's enabled
                         final int M = p.instances.size();
diff --git a/services/java/com/android/server/AttributeCache.java b/services/java/com/android/server/AttributeCache.java
index 459ae52..81378dc 100644
--- a/services/java/com/android/server/AttributeCache.java
+++ b/services/java/com/android/server/AttributeCache.java
@@ -17,56 +17,36 @@
 
 package com.android.server;
 
-import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.Intent;
-import android.content.BroadcastReceiver;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.provider.Settings;
-import android.util.Config;
-import android.util.Log;
+import android.util.SparseArray;
 
+import java.util.HashMap;
 import java.util.WeakHashMap;
 
-public final class AttributeCache extends BroadcastReceiver {
+/**
+ * TODO: This should be better integrated into the system so it doesn't need
+ * special calls from the activity manager to clear it.
+ */
+public final class AttributeCache {
     private static AttributeCache sInstance = null;
     
     private final Context mContext;
-    private final WeakHashMap<Key, Entry> mMap =
-            new WeakHashMap<Key, Entry>();
-    private final WeakHashMap<String, Context> mContexts =
-            new WeakHashMap<String, Context>();
+    private final WeakHashMap<String, Package> mPackages =
+            new WeakHashMap<String, Package>();
+    private final Configuration mConfiguration = new Configuration();
     
-    final static class Key {
-        public final String packageName;
-        public final int resId;
-        public final int[] styleable;
+    public final static class Package {
+        public final Context context;
+        private final SparseArray<HashMap<int[], Entry>> mMap
+                = new SparseArray<HashMap<int[], Entry>>();
         
-        public Key(String inPackageName, int inResId, int[] inStyleable) {
-            packageName = inPackageName;
-            resId = inResId;
-            styleable = inStyleable;
-        }
-        
-        @Override public boolean equals(Object obj) {
-            try {
-                if (obj != null) {
-                    Key other = (Key)obj;
-                    return packageName.equals(other.packageName)
-                            && resId == other.resId
-                            && styleable == other.styleable;
-                }
-            } catch (ClassCastException e) {
-            }
-            return false;
-        }
-
-        @Override public int hashCode() {
-            return packageName.hashCode() + resId;
+        public Package(Context c) {
+            context = c;
         }
     }
     
@@ -94,36 +74,68 @@
         mContext = context;
     }
     
+    public void removePackage(String packageName) {
+        synchronized (this) {
+            mPackages.remove(packageName);
+        }
+    }
+    
+    public void updateConfiguration(Configuration config) {
+        synchronized (this) {
+            int changes = mConfiguration.updateFrom(config);
+            if ((changes & ~(ActivityInfo.CONFIG_FONT_SCALE |
+                    ActivityInfo.CONFIG_KEYBOARD_HIDDEN |
+                    ActivityInfo.CONFIG_ORIENTATION)) != 0) {
+                // The configurations being masked out are ones that commonly
+                // change so we don't want flushing the cache... all others
+                // will flush the cache.
+                mPackages.clear();
+            }
+        }
+    }
+    
     public Entry get(String packageName, int resId, int[] styleable) {
         synchronized (this) {
-            Key key = new Key(packageName, resId, styleable);
-            Entry ent = mMap.get(key);
-            if (ent != null) {
-                return ent;
-            }
-            Context context = mContexts.get(packageName);
-            if (context == null) {
+            Package pkg = mPackages.get(packageName);
+            HashMap<int[], Entry> map = null;
+            Entry ent = null;
+            if (pkg != null) {
+                map = pkg.mMap.get(resId);
+                if (map != null) {
+                    ent = map.get(styleable);
+                    if (ent != null) {
+                        return ent;
+                    }
+                }
+            } else {
+                Context context;
                 try {
                     context = mContext.createPackageContext(packageName, 0);
                     if (context == null) {
                         return null;
                     }
-                    mContexts.put(packageName, context);
                 } catch (PackageManager.NameNotFoundException e) {
                     return null;
                 }
+                pkg = new Package(context);
+                mPackages.put(packageName, pkg);
             }
+            
+            if (map == null) {
+                map = new HashMap<int[], Entry>();
+                pkg.mMap.put(resId, map);
+            }
+            
             try {
-                ent = new Entry(context,
-                        context.obtainStyledAttributes(resId, styleable));
-                mMap.put(key, ent);
+                ent = new Entry(pkg.context,
+                        pkg.context.obtainStyledAttributes(resId, styleable));
+                map.put(styleable, ent);
             } catch (Resources.NotFoundException e) {
                 return null;
             }
+            
             return ent;
         }
     }
-    @Override public void onReceive(Context context, Intent intent) {
-    }
 }
 
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 2b9ac4d..953e401 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -33,6 +33,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
 import android.net.Uri;
+import android.provider.Settings;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -42,7 +43,6 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.SystemProperties;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -74,8 +74,9 @@
     private static final String TAG = "BackupManagerService";
     private static final boolean DEBUG = true;
 
-    // Default time to wait after data changes before we back up the data
-    private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
+    // How often we perform a backup pass.  Privileged external callers can
+    // trigger an immediate pass.
+    private static final long BACKUP_INTERVAL = 60 * 60 * 1000;
 
     private static final int MSG_RUN_BACKUP = 1;
     private static final int MSG_RUN_FULL_BACKUP = 2;
@@ -86,10 +87,11 @@
 
     private Context mContext;
     private PackageManager mPackageManager;
-    private final IActivityManager mActivityManager;
+    private IActivityManager mActivityManager;
+    private boolean mEnabled;   // access to this is synchronized on 'this'
     private final BackupHandler mBackupHandler = new BackupHandler();
     // map UIDs to the set of backup client services within that UID's app set
-    private SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
+    private final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
         = new SparseArray<HashSet<ApplicationInfo>>();
     // set of backup services that have pending changes
     private class BackupRequest {
@@ -128,7 +130,6 @@
     private volatile boolean mClearingData;
 
     // Transport bookkeeping
-    static private final String BACKUP_TRANSPORT_PROPERTY = "persist.service.bkup.trans";
     private final HashMap<String,IBackupTransport> mTransports
             = new HashMap<String,IBackupTransport>();
     private String mCurrentTransport;
@@ -160,6 +161,8 @@
         mActivityManager = ActivityManagerNative.getDefault();
 
         // Set up our bookkeeping
+        mEnabled = Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.BACKUP_ENABLED, 0) != 0;
         mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
         mDataDir = Environment.getDownloadCacheDirectory();
 
@@ -183,9 +186,11 @@
         registerTransport(localName.flattenToShortString(), mLocalTransport);
 
         mGoogleTransport = null;
-        // !!! TODO: set up the default transport name "the right way"
-        mCurrentTransport = SystemProperties.get(BACKUP_TRANSPORT_PROPERTY,
-                "com.google.android.backup/.BackupTransportService");
+        mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
+                Settings.Secure.BACKUP_TRANSPORT);
+        if ("".equals(mCurrentTransport)) {
+            mCurrentTransport = null;
+        }
         if (DEBUG) Log.v(TAG, "Starting with transport " + mCurrentTransport);
 
         // Attach to the Google backup transport.  When this comes up, it will set
@@ -197,7 +202,7 @@
         context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE);
 
         // Now that we know about valid backup participants, parse any
-        // leftover journal files and schedule a new backup pass
+        // leftover journal files into the pending backup set
         parseLeftoverJournals();
 
         // Register for broadcasts about package install, etc., so we can
@@ -207,7 +212,13 @@
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addDataScheme("package");
         mContext.registerReceiver(mBroadcastReceiver, filter);
-    }
+
+        // Schedule the first backup pass -- okay because no other threads are
+        // running yet
+        if (mEnabled) {
+            scheduleBackupPassLocked(BACKUP_INTERVAL);
+        }
+}
 
     private void makeJournalLocked() {
         try {
@@ -329,35 +340,39 @@
                 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
                 File oldJournal = mJournal;
                 synchronized (mQueueLock) {
-                    if (mPendingBackups.size() == 0) {
-                        Log.v(TAG, "Backup requested but nothing pending");
-                        break;
-                    }
-
-                    for (BackupRequest b: mPendingBackups.values()) {
-                        queue.add(b);
-                    }
-                    Log.v(TAG, "clearing pending backups");
-                    mPendingBackups.clear();
-
-                    // Start a new backup-queue journal file too
-                    if (mJournalStream != null) {
-                        try {
-                            mJournalStream.close();
-                        } catch (IOException e) {
-                            // don't need to do anything
+                    // Do we have any work to do?
+                    if (mPendingBackups.size() > 0) {
+                        for (BackupRequest b: mPendingBackups.values()) {
+                            queue.add(b);
                         }
-                        makeJournalLocked();
-                    }
+                        Log.v(TAG, "clearing pending backups");
+                        mPendingBackups.clear();
 
-                    // At this point, we have started a new journal file, and the old
-                    // file identity is being passed to the backup processing thread.
-                    // When it completes successfully, that old journal file will be
-                    // deleted.  If we crash prior to that, the old journal is parsed
-                    // at next boot and the journaled requests fulfilled.
+                        // Start a new backup-queue journal file too
+                        if (mJournalStream != null) {
+                            try {
+                                mJournalStream.close();
+                            } catch (IOException e) {
+                                // don't need to do anything
+                            }
+                            makeJournalLocked();
+                        }
+
+                        // At this point, we have started a new journal file, and the old
+                        // file identity is being passed to the backup processing thread.
+                        // When it completes successfully, that old journal file will be
+                        // deleted.  If we crash prior to that, the old journal is parsed
+                        // at next boot and the journaled requests fulfilled.
+                        (new PerformBackupThread(transport, queue, oldJournal)).start();
+                    } else {
+                        Log.v(TAG, "Backup requested but nothing pending");
+                    }
                 }
 
-                (new PerformBackupThread(transport, queue, oldJournal)).start();
+                // Schedule the next pass.
+                synchronized (mQueueLock) {
+                    scheduleBackupPassLocked(BACKUP_INTERVAL);
+                }
                 break;
             }
 
@@ -367,6 +382,7 @@
             case MSG_RUN_RESTORE:
             {
                 RestoreParams params = (RestoreParams)msg.obj;
+                Log.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
                 (new PerformRestoreThread(params.transport, params.observer, params.token)).start();
                 break;
             }
@@ -489,8 +505,15 @@
 
     // The queue lock should be held when scheduling a backup pass
     private void scheduleBackupPassLocked(long timeFromNowMillis) {
-        mBackupHandler.removeMessages(MSG_RUN_BACKUP);
-        mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, timeFromNowMillis);
+        // We only schedule backups when we're actually enabled
+        synchronized (this) {
+            if (mEnabled) {
+                mBackupHandler.removeMessages(MSG_RUN_BACKUP);
+                mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, timeFromNowMillis);
+            } else if (DEBUG) {
+                Log.v(TAG, "Disabled, so not scheduling backup pass");
+            }
+        }
     }
 
     // Return the given transport
@@ -612,6 +635,9 @@
         public void run() {
             if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
 
+            // Backups run at background priority
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
             // The package manager doesn't have a proper <application> etc, but since
             // it's running here in the system process we can just set up its agent
             // directly and use a synthetic BackupRequest.  We always run this pass
@@ -803,6 +829,7 @@
         PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer,
                 long restoreSetToken) {
             mTransport = transport;
+            Log.d(TAG, "PerformRestoreThread mObserver=" + mObserver);
             mObserver = observer;
             mToken = restoreSetToken;
 
@@ -816,7 +843,8 @@
 
         @Override
         public void run() {
-            if (DEBUG) Log.v(TAG, "Beginning restore process");
+            if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport
+                    + " mObserver=" + mObserver + " mToken=" + mToken);
             /**
              * Restore sequence:
              *
@@ -977,6 +1005,8 @@
                     Log.e(TAG, "Error finishing restore", e);
                 }
 
+                Log.d(TAG, "finishing restore mObserver=" + mObserver);
+
                 if (mObserver != null) {
                     try {
                         mObserver.restoreFinished(error);
@@ -992,6 +1022,8 @@
             // !!! TODO: actually run the restore through mTransport
             final String packageName = app.packageName;
 
+            Log.d(TAG, "processOneRestore packageName=" + packageName);
+
             // !!! TODO: get the dirs from the transport
             File backupDataName = new File(mDataDir, packageName + ".restore");
             backupDataName.delete();
@@ -1052,7 +1084,7 @@
         // If the caller does not hold the BACKUP permission, it can only request a
         // backup of its own data.
         HashSet<ApplicationInfo> targets;
-        if ((mContext.checkPermission("android.permission.BACKUP", Binder.getCallingPid(),
+        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
                 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
             targets = mBackupParticipants.get(Binder.getCallingUid());
         } else {
@@ -1087,15 +1119,11 @@
 
                 if (DEBUG) {
                     int numKeys = mPendingBackups.size();
-                    Log.d(TAG, "Scheduling backup for " + numKeys + " participants:");
+                    Log.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
                     for (BackupRequest b : mPendingBackups.values()) {
                         Log.d(TAG, "    + " + b + " agent=" + b.appInfo.backupAgentName);
                     }
                 }
-                // Schedule a backup pass in a few minutes.  As backup-eligible data
-                // keeps changing, continue to defer the backup pass until things
-                // settle down, to avoid extra overhead.
-                scheduleBackupPassLocked(COLLECTION_INTERVAL);
             }
         } else {
             Log.w(TAG, "dataChanged but no participant pkg " + packageName);
@@ -1117,7 +1145,7 @@
     // Run a backup pass immediately for any applications that have declared
     // that they have pending updates.
     public void backupNow() throws RemoteException {
-        mContext.enforceCallingPermission("android.permission.BACKUP", "tryBackupNow");
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "backupNow");
 
         if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
         synchronized (mQueueLock) {
@@ -1125,16 +1153,44 @@
         }
     }
 
+    // Enable/disable the backup transport
+    public void setBackupEnabled(boolean enable) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "setBackupEnabled");
+
+        boolean wasEnabled = mEnabled;
+        synchronized (this) {
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
+            mEnabled = enable;
+        }
+
+        synchronized (mQueueLock) {
+            if (enable && !wasEnabled) {
+                // if we've just been enabled, start scheduling backup passes
+                scheduleBackupPassLocked(BACKUP_INTERVAL);
+            } else if (!enable) {
+                // No longer enabled, so stop running backups.
+                mBackupHandler.removeMessages(MSG_RUN_BACKUP);
+            }
+        }
+    }
+
+    // Report whether the backup mechanism is currently enabled
+    public boolean isBackupEnabled() {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
+        return mEnabled;    // no need to synchronize just to read it
+    }
+
     // Report the name of the currently active transport
     public String getCurrentTransport() {
-        mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
-        Log.v(TAG, "getCurrentTransport() returning " + mCurrentTransport);
+        mContext.enforceCallingPermission("android.permission.BACKUP", "getCurrentTransport");
+        Log.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
         return mCurrentTransport;
     }
 
     // Report all known, available backup transports
     public String[] listAllTransports() {
-        mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "listAllTransports");
 
         String[] list = null;
         ArrayList<String> known = new ArrayList<String>();
@@ -1155,14 +1211,15 @@
     // name is not one of the available transports, no action is taken and the method
     // returns null.
     public String selectBackupTransport(String transport) {
-        mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
 
         synchronized (mTransports) {
             String prevTransport = null;
             if (mTransports.get(transport) != null) {
                 prevTransport = mCurrentTransport;
                 mCurrentTransport = transport;
-                SystemProperties.set(BACKUP_TRANSPORT_PROPERTY, transport);
+                Settings.Secure.putString(mContext.getContentResolver(),
+                        Settings.Secure.BACKUP_TRANSPORT, transport);
                 Log.v(TAG, "selectBackupTransport() set " + mCurrentTransport
                         + " returning " + prevTransport);
             } else {
@@ -1208,7 +1265,7 @@
 
     // Hand off a restore session
     public IRestoreSession beginRestoreSession(String transport) {
-        mContext.enforceCallingPermission("android.permission.BACKUP", "beginRestoreSession");
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "beginRestoreSession");
 
         synchronized(this) {
             if (mActiveRestoreSession != null) {
@@ -1234,7 +1291,7 @@
 
         // --- Binder interface ---
         public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
-            mContext.enforceCallingPermission("android.permission.BACKUP",
+            mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
                     "getAvailableRestoreSets");
 
             try {
@@ -1253,7 +1310,9 @@
 
         public int performRestore(long token, IRestoreObserver observer)
                 throws android.os.RemoteException {
-            mContext.enforceCallingPermission("android.permission.BACKUP", "performRestore");
+            mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "performRestore");
+
+            Log.d(TAG, "performRestore token=" + token + " observer=" + observer);
 
             if (mRestoreSets != null) {
                 for (int i = 0; i < mRestoreSets.length; i++) {
@@ -1271,9 +1330,11 @@
         }
 
         public void endRestoreSession() throws android.os.RemoteException {
-            mContext.enforceCallingPermission("android.permission.BACKUP",
+            mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
                     "endRestoreSession");
 
+            Log.d(TAG, "endRestoreSession");
+
             mRestoreTransport.finishRestore();
             mRestoreTransport = null;
             synchronized(BackupManagerService.this) {
@@ -1290,26 +1351,34 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (mQueueLock) {
+            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled"));
+            boolean scheduled = mBackupHandler.hasMessages(MSG_RUN_BACKUP);
+            if (scheduled != mEnabled) {
+                if (mEnabled) {
+                    pw.println("ERROR: backups enabled but none scheduled!");
+                } else {
+                    pw.println("ERROR: backups are scheduled but not enabled!");
+                }
+            }
             pw.println("Available transports:");
             for (String t : listAllTransports()) {
-                pw.println("  " + t);
+                String pad = (t.equals(mCurrentTransport)) ? "  * " : "    ";
+                pw.println(pad + t);
             }
             int N = mBackupParticipants.size();
-            pw.println("Participants:");
+            pw.println("Participants: " + N);
             for (int i=0; i<N; i++) {
                 int uid = mBackupParticipants.keyAt(i);
                 pw.print("  uid: ");
                 pw.println(uid);
                 HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
                 for (ApplicationInfo app: participants) {
-                    pw.print("    ");
-                    pw.println(app.toString());
+                    pw.println("    " + app.toString());
                 }
             }
             pw.println("Pending: " + mPendingBackups.size());
             for (BackupRequest req : mPendingBackups.values()) {
-                pw.print("   ");
-                pw.println(req);
+                pw.println("    " + req);
             }
         }
     }
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
index 3fc1e0e..9b0a2d4 100644
--- a/services/java/com/android/server/HeadsetObserver.java
+++ b/services/java/com/android/server/HeadsetObserver.java
@@ -35,6 +35,7 @@
  */
 class HeadsetObserver extends UEventObserver {
     private static final String TAG = HeadsetObserver.class.getSimpleName();
+    private static final boolean LOG = false;
 
     private static final String HEADSET_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/h2w";
     private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state";
@@ -61,7 +62,7 @@
 
     @Override
     public void onUEvent(UEventObserver.UEvent event) {
-        Log.v(TAG, "Headset UEVENT: " + event.toString());
+        if (LOG) Log.v(TAG, "Headset UEVENT: " + event.toString());
 
         try {
             update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE")));
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index fc37290..0f5b3fd 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -507,6 +507,7 @@
 
     private void removeProvider(LocationProviderProxy provider) {
         mProviders.remove(provider);
+        provider.unlinkProvider();
         mProvidersByName.remove(provider.getName());
     }
 
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 4a2808b..854138c 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -25,15 +25,20 @@
 import android.app.INotificationManager;
 import android.app.ITransientNotification;
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentQueryMap;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.media.AsyncPlayer;
 import android.media.AudioManager;
 import android.net.Uri;
@@ -88,6 +93,12 @@
     private NotificationRecord mVibrateNotification;
     private Vibrator mVibrator = new Vibrator();
 
+    // adb
+    private int mBatteryPlugged;
+    private boolean mAdbEnabled = false;
+    private boolean mAdbNotificationShown = false;
+    private Notification mAdbNotification;
+    
     private ArrayList<NotificationRecord> mNotificationList;
 
     private ArrayList<ToastRecord> mToastQueue;
@@ -297,6 +308,9 @@
                     mBatteryFull = batteryFull;
                     updateLights();
                 }
+                
+                mBatteryPlugged = intent.getIntExtra("plugged", 0);
+                updateAdbNotification();
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
                 Uri uri = intent.getData();
@@ -312,6 +326,31 @@
         }
     };
 
+    class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+        
+        void observe() {
+            ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.ADB_ENABLED), false, this);
+            update();
+        }
+
+        @Override public void onChange(boolean selfChange) {
+            update();
+        }
+
+        public void update() {
+            ContentResolver resolver = mContext.getContentResolver();
+            mAdbEnabled = Settings.Secure.getInt(resolver,
+                        Settings.Secure.ADB_ENABLED, 0) != 0;
+            updateAdbNotification();
+        }
+    }
+    private final SettingsObserver mSettingsObserver;
+    
     NotificationManagerService(Context context, StatusBarService statusBar,
             HardwareService hardware)
     {
@@ -333,6 +372,9 @@
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
         mContext.registerReceiver(mIntentReceiver, filter);
+        
+        mSettingsObserver = new SettingsObserver(mHandler);
+        mSettingsObserver.observe();
     }
 
     // Toasts
@@ -892,6 +934,62 @@
         return -1;
     }
 
+    // This is here instead of StatusBarPolicy because it is an important
+    // security feature that we don't want people customizing the platform
+    // to accidentally lose.
+    private void updateAdbNotification() {
+        if (mAdbEnabled && mBatteryPlugged == BatteryManager.BATTERY_PLUGGED_USB) {
+            if (!mAdbNotificationShown) {
+                NotificationManager notificationManager = (NotificationManager) mContext
+                        .getSystemService(Context.NOTIFICATION_SERVICE);
+                if (notificationManager != null) {
+                    Resources r = mContext.getResources();
+                    CharSequence title = r.getText(
+                            com.android.internal.R.string.adb_active_notification_title);
+                    CharSequence message = r.getText(
+                            com.android.internal.R.string.adb_active_notification_message);
+
+                    if (mAdbNotification == null) {
+                        mAdbNotification = new Notification();
+                        mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_warning;
+                        mAdbNotification.when = 0;
+                        mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT;
+                        mAdbNotification.tickerText = title;
+                        mAdbNotification.defaults |= Notification.DEFAULT_SOUND;
+                    }
+
+                    Intent intent = new Intent(
+                            Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                    // Note: we are hard-coding the component because this is
+                    // an important security UI that we don't want anyone
+                    // intercepting.
+                    intent.setComponent(new ComponentName("com.android.settings",
+                            "com.android.settings.DevelopmentSettings"));
+                    PendingIntent pi = PendingIntent.getActivity(mContext, 0,
+                            intent, 0);
+
+                    mAdbNotification.setLatestEventInfo(mContext, title, message, pi);
+                    
+                    mAdbNotificationShown = true;
+                    notificationManager.notify(
+                            com.android.internal.R.string.adb_active_notification_title,
+                            mAdbNotification);
+                }
+            }
+            
+        } else if (mAdbNotificationShown) {
+            NotificationManager notificationManager = (NotificationManager) mContext
+                    .getSystemService(Context.NOTIFICATION_SERVICE);
+            if (notificationManager != null) {
+                mAdbNotificationShown = false;
+                notificationManager.cancel(
+                        com.android.internal.R.string.adb_active_notification_title);
+            }
+        }
+    }
+    
     // ======================================================================
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index 66fb86d..a1b4c26 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -59,7 +59,14 @@
 
     private List<PackageInfo> mAllPackages;
     private PackageManager mPackageManager;
+    // version & signature info of each app in a restore set
     private HashMap<String, Metadata> mRestoredSignatures;
+    // The version info of each backed-up app as read from the state file
+    private HashMap<String, Metadata> mStateVersions = new HashMap<String, Metadata>();
+
+    private final HashSet<String> mExisting = new HashSet<String>();
+    private int mStoredSdkVersion;
+    private String mStoredIncrementalVersion;
 
     public class Metadata {
         public int versionCode;
@@ -96,35 +103,78 @@
 
         ByteArrayOutputStream bufStream = new ByteArrayOutputStream();  // we'll reuse these
         DataOutputStream outWriter = new DataOutputStream(bufStream);
-        HashSet<String> existing = parseStateFile(oldState);
+        parseStateFile(oldState);
+
+        // If the stored version string differs, we need to re-backup all
+        // of the metadata.  We force this by removing everything from the
+        // "already backed up" map built by parseStateFile().
+        if (mStoredIncrementalVersion == null
+                || !mStoredIncrementalVersion.equals(Build.VERSION.INCREMENTAL)) {
+            Log.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs "
+                    + Build.VERSION.INCREMENTAL + " - rewriting");
+            mExisting.clear();
+        }
 
         try {
             /*
              * Global metadata:
              *
-             * int version -- the SDK version of the OS itself on the device
-             *                that produced this backup set.  Used to reject
-             *                backups from later OSes onto earlier ones.
+             * int SDKversion -- the SDK version of the OS itself on the device
+             *                   that produced this backup set.  Used to reject
+             *                   backups from later OSes onto earlier ones.
+             * String incremental -- the incremental release name of the OS stored in
+             *                       the backup set.
              */
-            if (!existing.contains(GLOBAL_METADATA_KEY)) {
+            if (!mExisting.contains(GLOBAL_METADATA_KEY)) {
                 if (DEBUG) Log.v(TAG, "Storing global metadata key");
                 outWriter.writeInt(Build.VERSION.SDK_INT);
+                outWriter.writeUTF(Build.VERSION.INCREMENTAL);
                 byte[] metadata = bufStream.toByteArray();
                 data.writeEntityHeader(GLOBAL_METADATA_KEY, metadata.length);
                 data.writeEntityData(metadata, metadata.length);
             } else {
                 if (DEBUG) Log.v(TAG, "Global metadata key already stored");
+                // don't consider it to have been skipped/deleted
+                mExisting.remove(GLOBAL_METADATA_KEY);
             }
 
             // For each app we have on device, see if we've backed it up yet.  If not,
             // write its signature block to the output, keyed on the package name.
             for (PackageInfo pkg : mAllPackages) {
                 String packName = pkg.packageName;
-                if (!existing.contains(packName)) {
-                    // We haven't stored this app's signatures yet, so we do that now
+                if (packName.equals(GLOBAL_METADATA_KEY)) {
+                    // We've already handled the metadata key; skip it here
+                    continue;
+                } else {
+                    PackageInfo info = null;
                     try {
-                        PackageInfo info = mPackageManager.getPackageInfo(packName,
+                        info = mPackageManager.getPackageInfo(packName,
                                 PackageManager.GET_SIGNATURES);
+                    } catch (NameNotFoundException e) {
+                        // Weird; we just found it, and now are told it doesn't exist.
+                        // Treat it as having been removed from the device.
+                        mExisting.add(packName);
+                        continue;
+                    }
+
+                    boolean doBackup = false;
+                    if (!mExisting.contains(packName)) {
+                        // We haven't backed up this app before
+                        doBackup = true;
+                    } else {
+                        // We *have* backed this one up before.  Check whether the version
+                        // of the backup matches the version of the current app; if they
+                        // don't match, the app has been updated and we need to store its
+                        // metadata again.  In either case, take it out of mExisting so that
+                        // we don't consider it deleted later.
+                        if (info.versionCode != mStateVersions.get(packName).versionCode) {
+                            doBackup = true;
+                        }
+                        mExisting.remove(packName);
+                    }
+
+                    if (doBackup) {
+                        // We need to store this app's metadata
                         /*
                          * Metadata for each package:
                          *
@@ -132,7 +182,7 @@
                          * byte[] signatures -- [len] flattened Signature[] of the package
                          */
 
-                        // marshall the version code in a canonical form
+                        // marshal the version code in a canonical form
                         bufStream.reset();
                         outWriter.writeInt(info.versionCode);
                         byte[] versionBuf = bufStream.toByteArray();
@@ -150,18 +200,6 @@
                         data.writeEntityHeader(packName, versionBuf.length + sigs.length);
                         data.writeEntityData(versionBuf, versionBuf.length);
                         data.writeEntityData(sigs, sigs.length);
-                    } catch (NameNotFoundException e) {
-                        // Weird; we just found it, and now are told it doesn't exist.
-                        // Treat it as having been removed from the device.
-                        existing.add(packName);
-                    }
-                } else {
-                    // We've already backed up this app.  Remove it from the set so
-                    // we can tell at the end what has disappeared from the device.
-                    // !!! TODO: take out the debugging message
-                    if (DEBUG) Log.v(TAG, "= already backed up metadata for " + packName);
-                    if (!existing.remove(packName)) {
-                        Log.d(TAG, "*** failed to remove " + packName + " from package set!");
                     }
                 }
             }
@@ -169,7 +207,7 @@
             // At this point, the only entries in 'existing' are apps that were
             // mentioned in the saved state file, but appear to no longer be present
             // on the device.  Write a deletion entity for them.
-            for (String app : existing) {
+            for (String app : mExisting) {
                 // !!! TODO: take out this msg
                 if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app);
                 try {
@@ -212,17 +250,21 @@
             DataInputStream in = new DataInputStream(baStream);
 
             if (key.equals(GLOBAL_METADATA_KEY)) {
-                storedSystemVersion = in.readInt();
+                int storedSdkVersion = in.readInt();
                 if (DEBUG) Log.v(TAG, "   storedSystemVersion = " + storedSystemVersion);
                 if (storedSystemVersion > Build.VERSION.SDK_INT) {
                     // returning before setting the sig map means we rejected the restore set
                     Log.w(TAG, "Restore set was from a later version of Android; not restoring");
                     return;
                 }
+                mStoredSdkVersion = storedSdkVersion;
+                mStoredIncrementalVersion = in.readUTF();
                 // !!! TODO: remove this debugging output
                 if (DEBUG) {
                     Log.i(TAG, "Restore set version " + storedSystemVersion
-                            + " is compatible with OS version " + Build.VERSION.SDK_INT);
+                            + " is compatible with OS version " + Build.VERSION.SDK_INT
+                            + " (" + mStoredIncrementalVersion + " vs "
+                            + Build.VERSION.INCREMENTAL + ")");
                 }
             } else {
                 // it's a file metadata record
@@ -299,31 +341,45 @@
     }
 
     // Util: parse out an existing state file into a usable structure
-    private HashSet<String> parseStateFile(ParcelFileDescriptor stateFile) {
-        HashSet<String> set = new HashSet<String>();
+    private void parseStateFile(ParcelFileDescriptor stateFile) {
+        mExisting.clear();
+        mStateVersions.clear();
+        mStoredSdkVersion = 0;
+        mStoredIncrementalVersion = null;
+
         // The state file is just the list of app names we have stored signatures for
+        // with the exception of the metadata block, to which is also appended the
+        // version numbers corresponding with the last time we wrote this PM block.
+        // If they mismatch the current system, we'll re-store the metadata key.
         FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor());
         DataInputStream in = new DataInputStream(instream);
 
         int bufSize = 256;
         byte[] buf = new byte[bufSize];
         try {
-            int nameSize = in.readInt();
-            if (bufSize < nameSize) {
-                bufSize = nameSize + 32;
-                buf = new byte[bufSize];
+            String pkg = in.readUTF();
+            if (pkg.equals(GLOBAL_METADATA_KEY)) {
+                mStoredSdkVersion = in.readInt();
+                mStoredIncrementalVersion = in.readUTF();
+                mExisting.add(GLOBAL_METADATA_KEY);
+            } else {
+                Log.e(TAG, "No global metadata in state file!");
+                return;
             }
-            in.read(buf, 0, nameSize);
-            String pkg = new String(buf, 0, nameSize);
-            set.add(pkg);
+
+            // The global metadata was first; now read all the apps
+            while (true) {
+                pkg = in.readUTF();
+                int versionCode = in.readInt();
+                mExisting.add(pkg);
+                mStateVersions.put(pkg, new Metadata(versionCode, null));
+            }
         } catch (EOFException eof) {
             // safe; we're done
         } catch (IOException e) {
             // whoops, bad state file.  abort.
-            Log.e(TAG, "Unable to read Package Manager state file");
-            return null;
+            Log.e(TAG, "Unable to read Package Manager state file: " + e);
         }
-        return set;
     }
 
     // Util: write out our new backup state file
@@ -333,15 +389,14 @@
 
         try {
             // by the time we get here we know we've stored the global metadata record
-            byte[] metaNameBuf = GLOBAL_METADATA_KEY.getBytes();
-            out.writeInt(metaNameBuf.length);
-            out.write(metaNameBuf);
+            out.writeUTF(GLOBAL_METADATA_KEY);
+            out.writeInt(Build.VERSION.SDK_INT);
+            out.writeUTF(Build.VERSION.INCREMENTAL);
 
             // now write all the app names too
             for (PackageInfo pkg : pkgs) {
-                byte[] pkgNameBuf = pkg.packageName.getBytes();
-                out.writeInt(pkgNameBuf.length);
-                out.write(pkgNameBuf);
+                out.writeUTF(pkg.packageName);
+                out.writeInt(pkg.versionCode);
             }
         } catch (IOException e) {
             Log.e(TAG, "Unable to write package manager state file!");
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 972aa98..4ea2831 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -3292,6 +3292,7 @@
                 if (extras != null) {
                     intent.putExtras(extras);
                 }
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                 am.broadcastIntent(
                     null, intent,
                             null, null, 0, null, null, null, false, false);
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index c5ea5fa9..79d78ad1 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -709,7 +709,10 @@
                     p.awakeOnSet = true;
                 }
             } else {
-                mPokeLocks.remove(token);
+                PokeLock rLock = mPokeLocks.remove(token);
+                if (rLock != null) {
+                    token.unlinkToDeath(rLock, 0);
+                }
             }
 
             int oldPokey = mPokey;
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 5df88b2..f5e2e3d 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -1875,7 +1875,9 @@
         private WifiLock removeLock(IBinder binder) {
             int index = findLockByBinder(binder);
             if (index >= 0) {
-                return mList.remove(index);
+                WifiLock ret = mList.remove(index);
+                ret.unlinkDeathRecipient();
+                return ret;
             } else {
                 return null;
             }
@@ -1987,6 +1989,10 @@
                 binderDied();
             }
         }
+
+        void unlinkDeathRecipient() {
+            mBinder.unlinkToDeath(this, 0);
+        }
     }
 
     private class Multicaster extends DeathRecipient {
@@ -2054,7 +2060,10 @@
 
     private void removeMulticasterLocked(int i, int uid)
     {
-        mMulticasters.remove(i);
+        Multicaster removed = mMulticasters.remove(i);
+        if (removed != null) {
+            removed.unlinkDeathRecipient();
+        }
         if (mMulticasters.size() == 0) {
             WifiNative.startPacketFiltering();
         }
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 5ea7504..2dd70ef 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -2325,6 +2325,14 @@
             if (!mDisplayFrozen) {
                 if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
                     mNextAppTransition = transit;
+                } else if (transit == WindowManagerPolicy.TRANSIT_TASK_OPEN
+                        && mNextAppTransition == WindowManagerPolicy.TRANSIT_TASK_CLOSE) {
+                    // Opening a new task always supersedes a close for the anim.
+                    mNextAppTransition = transit;
+                } else if (transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
+                        && mNextAppTransition == WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE) {
+                    // Opening a new activity always supersedes a close for the anim.
+                    mNextAppTransition = transit;
                 }
                 mAppTransitionReady = false;
                 mAppTransitionTimeout = false;
@@ -3407,7 +3415,10 @@
                 synchronized (mWindowMap) {
                     for (int i=0; i<mRotationWatchers.size(); i++) {
                         if (watcherBinder == mRotationWatchers.get(i).asBinder()) {
-                            mRotationWatchers.remove(i);
+                            IRotationWatcher removed = mRotationWatchers.remove(i);
+                            if (removed != null) {
+                                removed.asBinder().unlinkToDeath(this, 0);
+                            }
                             i--;
                         }
                     }
@@ -3834,7 +3845,7 @@
                 "dispatchPointer " + ev);
 
         Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev,
-                ev, true, false);
+                ev, true, false, pid, uid);
 
         int action = ev.getAction();
 
@@ -4032,7 +4043,7 @@
                 TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">");
 
         Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev,
-                ev, false, false);
+                ev, false, false, pid, uid);
         if (focusObj == null) {
             Log.w(TAG, "No focus window, dropping trackball: " + ev);
             if (qev != null) {
@@ -4103,7 +4114,7 @@
         if (DEBUG_INPUT) Log.v(TAG, "Dispatch key: " + event);
 
         Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null,
-                null, false, false);
+                null, false, false, pid, uid);
         if (focusObj == null) {
             Log.w(TAG, "No focus window, dropping: " + event);
             return INJECT_FAILED;
@@ -4220,10 +4231,14 @@
         KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
                 deviceId, scancode, KeyEvent.FLAG_FROM_SYSTEM);
 
-        int result = dispatchKey(newEvent, Binder.getCallingPid(), Binder.getCallingUid());
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        final int result = dispatchKey(newEvent, pid, uid);
         if (sync) {
-            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
         }
+        Binder.restoreCallingIdentity(ident);
         switch (result) {
             case INJECT_NO_PERMISSION:
                 throw new SecurityException(
@@ -4244,10 +4259,14 @@
      * @return Returns true if event was dispatched, false if it was dropped for any reason
      */
     public boolean injectPointerEvent(MotionEvent ev, boolean sync) {
-        int result = dispatchPointer(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        final int result = dispatchPointer(null, ev, pid, uid);
         if (sync) {
-            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
         }
+        Binder.restoreCallingIdentity(ident);
         switch (result) {
             case INJECT_NO_PERMISSION:
                 throw new SecurityException(
@@ -4268,10 +4287,14 @@
      * @return Returns true if event was dispatched, false if it was dropped for any reason
      */
     public boolean injectTrackballEvent(MotionEvent ev, boolean sync) {
-        int result = dispatchTrackball(null, ev, Binder.getCallingPid(), Binder.getCallingUid());
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        final int result = dispatchTrackball(null, ev, pid, uid);
         if (sync) {
-            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true);
+            mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
         }
+        Binder.restoreCallingIdentity(ident);
         switch (result) {
             case INJECT_NO_PERMISSION:
                 throw new SecurityException(
@@ -4380,7 +4403,7 @@
          */
         Object waitForNextEventTarget(KeyEvent nextKey, QueuedEvent qev,
                 MotionEvent nextMotion, boolean isPointerEvent,
-                boolean failIfTimeout) {
+                boolean failIfTimeout, int callingPid, int callingUid) {
             long startTime = SystemClock.uptimeMillis();
             long keyDispatchingTimeout = 5 * 1000;
             long waitedFor = 0;
@@ -4398,7 +4421,7 @@
                         ", mLastWin=" + mLastWin);
                 if (targetIsNew) {
                     Object target = findTargetWindow(nextKey, qev, nextMotion,
-                            isPointerEvent);
+                            isPointerEvent, callingPid, callingUid);
                     if (target == SKIP_TARGET_TOKEN) {
                         // The user has pressed a special key, and we are
                         // dropping all pending events before it.
@@ -4574,7 +4597,8 @@
         }
 
         Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev,
-                MotionEvent nextMotion, boolean isPointerEvent) {
+                MotionEvent nextMotion, boolean isPointerEvent,
+                int callingPid, int callingUid) {
             mOutsideTouchTargets = null;
 
             if (nextKey != null) {
@@ -4583,9 +4607,16 @@
                 final int repeatCount = nextKey.getRepeatCount();
                 final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP;
                 boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode);
+                
                 if (!dispatch) {
-                    mPolicy.interceptKeyTi(null, keycode,
-                            nextKey.getMetaState(), down, repeatCount);
+                    if (callingUid == 0 ||
+                            mContext.checkPermission(
+                                    android.Manifest.permission.INJECT_EVENTS,
+                                    callingPid, callingUid)
+                                    == PackageManager.PERMISSION_GRANTED) {
+                        mPolicy.interceptKeyTi(null, keycode,
+                                nextKey.getMetaState(), down, repeatCount);
+                    }
                     Log.w(TAG, "Event timeout during app switch: dropping "
                             + nextKey);
                     return SKIP_TARGET_TOKEN;
@@ -4600,9 +4631,16 @@
 
                 wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
 
-                if (mPolicy.interceptKeyTi(focus,
-                        keycode, nextKey.getMetaState(), down, repeatCount)) {
-                    return CONSUMED_EVENT_TOKEN;
+                if (callingUid == 0 ||
+                        (focus != null && callingUid == focus.mSession.mUid) ||
+                        mContext.checkPermission(
+                                android.Manifest.permission.INJECT_EVENTS,
+                                callingPid, callingUid)
+                                == PackageManager.PERMISSION_GRANTED) {
+                    if (mPolicy.interceptKeyTi(focus,
+                            keycode, nextKey.getMetaState(), down, repeatCount)) {
+                        return CONSUMED_EVENT_TOKEN;
+                    }
                 }
 
                 return focus;
@@ -5415,6 +5453,7 @@
             } catch (RemoteException e) {
             }
             synchronized(mWindowMap) {
+                mClient.asBinder().unlinkToDeath(this, 0);
                 mClientDead = true;
                 killSessionLocked();
             }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 6d04b6b..2fe4dd4 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.AttributeCache;
 import com.android.server.IntentResolver;
 import com.android.server.ProcessMap;
 import com.android.server.ProcessStats;
@@ -61,7 +62,6 @@
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -745,6 +745,8 @@
 
     int mFactoryTest;
 
+    boolean mCheckedForSetup;
+    
     /**
      * The time at which we will allow normal application switches again,
      * after a call to {@link #stopAppSwitches()}.
@@ -1774,6 +1776,12 @@
             r.stopped = true;
         }
 
+        // Launch the new version setup screen if needed.  We do this -after-
+        // launching the initial activity (that is, home), so that it can have
+        // a chance to initialize itself while in the background, making the
+        // switch back to it faster and look better.
+        startSetupActivityLocked();
+        
         return true;
     }
 
@@ -2355,6 +2363,96 @@
         }
     }
 
+    private boolean startHomeActivityLocked() {
+        if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
+                && mTopAction == null) {
+            // We are running in factory test mode, but unable to find
+            // the factory test app, so just sit around displaying the
+            // error message and don't try to start anything.
+            return false;
+        }
+        Intent intent = new Intent(
+            mTopAction,
+            mTopData != null ? Uri.parse(mTopData) : null);
+        intent.setComponent(mTopComponent);
+        if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+            intent.addCategory(Intent.CATEGORY_HOME);
+        }
+        ActivityInfo aInfo =
+            intent.resolveActivityInfo(mContext.getPackageManager(),
+                    STOCK_PM_FLAGS);
+        if (aInfo != null) {
+            intent.setComponent(new ComponentName(
+                    aInfo.applicationInfo.packageName, aInfo.name));
+            // Don't do this if the home app is currently being
+            // instrumented.
+            ProcessRecord app = getProcessRecordLocked(aInfo.processName,
+                    aInfo.applicationInfo.uid);
+            if (app == null || app.instrumentationClass == null) {
+                intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+                startActivityLocked(null, intent, null, null, 0, aInfo,
+                        null, null, 0, 0, 0, false, false);
+            }
+        }
+        
+        
+        return true;
+    }
+    
+    /**
+     * Starts the "new version setup screen" if appropriate.
+     */
+    private void startSetupActivityLocked() {
+        // Only do this once per boot.
+        if (mCheckedForSetup) {
+            return;
+        }
+        
+        // We will show this screen if the current one is a different
+        // version than the last one shown, and we are not running in
+        // low-level factory test mode.
+        final ContentResolver resolver = mContext.getContentResolver();
+        if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL &&
+                Settings.Secure.getInt(resolver,
+                        Settings.Secure.DEVICE_PROVISIONED, 0) != 0) {
+            mCheckedForSetup = true;
+            
+            // See if we should be showing the platform update setup UI.
+            Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
+            List<ResolveInfo> ris = mSelf.mContext.getPackageManager()
+                    .queryIntentActivities(intent, PackageManager.GET_META_DATA);
+            
+            // We don't allow third party apps to replace this.
+            ResolveInfo ri = null;
+            for (int i=0; ris != null && i<ris.size(); i++) {
+                if ((ris.get(i).activityInfo.applicationInfo.flags
+                        & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                    ri = ris.get(i);
+                    break;
+                }
+            }
+            
+            if (ri != null) {
+                String vers = ri.activityInfo.metaData != null
+                        ? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION)
+                        : null;
+                if (vers == null && ri.activityInfo.applicationInfo.metaData != null) {
+                    vers = ri.activityInfo.applicationInfo.metaData.getString(
+                            Intent.METADATA_SETUP_VERSION);
+                }
+                String lastVers = Settings.Secure.getString(
+                        resolver, Settings.Secure.LAST_SETUP_SHOWN);
+                if (vers != null && !vers.equals(lastVers)) {
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    intent.setComponent(new ComponentName(
+                            ri.activityInfo.packageName, ri.activityInfo.name));
+                    startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
+                            null, null, 0, 0, 0, false, false);
+                }
+            }
+        }
+    }
+    
     /**
      * Ensure that the top activity in the stack is resumed.
      *
@@ -2376,37 +2474,7 @@
         if (next == null) {
             // There are no more activities!  Let's just start up the
             // Launcher...
-            if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
-                    && mTopAction == null) {
-                // We are running in factory test mode, but unable to find
-                // the factory test app, so just sit around displaying the
-                // error message and don't try to start anything.
-                return false;
-            }
-            Intent intent = new Intent(
-                mTopAction,
-                mTopData != null ? Uri.parse(mTopData) : null);
-            intent.setComponent(mTopComponent);
-            if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
-                intent.addCategory(Intent.CATEGORY_HOME);
-            }
-            ActivityInfo aInfo =
-                intent.resolveActivityInfo(mContext.getPackageManager(),
-                        STOCK_PM_FLAGS);
-            if (aInfo != null) {
-                intent.setComponent(new ComponentName(
-                        aInfo.applicationInfo.packageName, aInfo.name));
-                // Don't do this if the home app is currently being
-                // instrumented.
-                ProcessRecord app = getProcessRecordLocked(aInfo.processName,
-                        aInfo.applicationInfo.uid);
-                if (app == null || app.instrumentationClass == null) {
-                    intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
-                    startActivityLocked(null, intent, null, null, 0, aInfo,
-                            null, null, 0, 0, 0, false, false);
-                }
-            }
-            return true;
+            return startHomeActivityLocked();
         }
 
         next.delayedResume = false;
@@ -10715,6 +10783,10 @@
                         if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
                             uninstallPackageLocked(ssp,
                                     intent.getIntExtra(Intent.EXTRA_UID, -1), false);
+                            AttributeCache ac = AttributeCache.instance();
+                            if (ac != null) {
+                                ac.removePackage(ssp);
+                            }
                         }
                     }
                 }
@@ -11765,6 +11837,11 @@
                 Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
                 broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
                         null, false, false, MY_PID, Process.SYSTEM_UID);
+                
+                AttributeCache ac = AttributeCache.instance();
+                if (ac != null) {
+                    ac.updateConfiguration(mConfiguration);
+                }
             }
         }
         
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index dd3d38c..7a8d4e5e 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -99,7 +99,7 @@
     private IBinder mBatteryIcon;
     private IconData mBatteryData;
     private boolean mBatteryFirst = true;
-    private boolean mBatteryPlugged;
+    private int mBatteryPlugged;
     private int mBatteryLevel;
     private int mBatteryThreshold = 0; // index into mBatteryThresholds
     private int[] mBatteryThresholds = new int[] { 20, 15, -1 };
@@ -108,6 +108,7 @@
     private View mBatteryView;
     private int mBatteryViewSequence;
     private boolean mBatteryShowLowOnEndCall = false;
+    private boolean mSentLowBatteryBroadcast = false;
     private static final boolean SHOW_LOW_BATTERY_WARNING = true;
 
     // phone
@@ -581,7 +582,7 @@
         mBatteryData.iconLevel = intent.getIntExtra("level", 0);
         mService.updateIcon(mBatteryIcon, mBatteryData, null);
 
-        boolean plugged = intent.getIntExtra("plugged", 0) != 0;
+        int plugged = intent.getIntExtra("plugged", 0);
         int level = intent.getIntExtra("level", -1);
         if (false) {
             Log.d(TAG, "updateBattery level=" + level
@@ -592,7 +593,7 @@
                     + " mBatteryFirst=" + mBatteryFirst);
         }
 
-        boolean oldPlugged = mBatteryPlugged;
+        int oldPlugged = mBatteryPlugged;
         int oldThreshold = mBatteryThreshold;
         pickNextBatteryLevel(level);
 
@@ -619,11 +620,12 @@
             Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level
                     + " mBatteryThreshold=" + mBatteryThreshold + " oldThreshold=" + oldThreshold);
         }
-        if (!plugged
-                && ((oldPlugged && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING])
+        if (plugged == 0
+                && ((oldPlugged != 0 && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING])
                     || (mBatteryThreshold > oldThreshold
                         && mBatteryThreshold > BATTERY_THRESHOLD_WARNING))) {
             // Broadcast the low battery warning
+            mSentLowBatteryBroadcast = true;
             mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_LOW));
 
             if (SHOW_LOW_BATTERY_WARNING) {
@@ -639,7 +641,11 @@
                     mBatteryShowLowOnEndCall = true;
                 }
             }
-        } else if (mBatteryThreshold == BATTERY_THRESHOLD_CLOSE_WARNING) {
+        } else if (mBatteryThreshold < BATTERY_THRESHOLD_WARNING) {
+            if (mSentLowBatteryBroadcast == true) {
+                mSentLowBatteryBroadcast = false;
+                mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_OKAY));
+            }
             if (SHOW_LOW_BATTERY_WARNING) {
                 if (mLowBatteryDialog != null) {
                     mLowBatteryDialog.dismiss();
@@ -763,7 +769,7 @@
         }
         if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
             if (mBatteryShowLowOnEndCall) {
-                if (!mBatteryPlugged) {
+                if (mBatteryPlugged == 0) {
                     showLowBatteryWarning();
                 }
                 mBatteryShowLowOnEndCall = false;
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 48cbace..b44168a 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -119,6 +119,7 @@
         public void binderDied() {
             Log.i(TAG, "binder died for pkg=" + pkg);
             disable(0, token, pkg);
+            token.unlinkToDeath(this, 0);
         }
     }
 
@@ -494,6 +495,7 @@
             if (what == 0 || !token.isBinderAlive()) {
                 if (tok != null) {
                     mDisableRecords.remove(i);
+                    tok.token.unlinkToDeath(tok, 0);
                 }
             } else {
                 if (tok == null) {
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index d3942fc..dbe8431 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -701,17 +701,6 @@
     }
 
     /**
-     * Note: calls extractNetworkPortion(), so do not use for
-     * SIM EF[ADN] style records
-     *
-     * Exceptions thrown if extractNetworkPortion(s).length() == 0
-     */
-    public static byte[]
-    networkPortionToCalledPartyBCD(String s) {
-        return numberToCalledPartyBCD(extractNetworkPortion(s));
-    }
-
-    /**
      * Return true iff the network portion of <code>address</code> is,
      * as far as we can tell on the device, suitable for use as an SMS
      * destination address.
@@ -744,12 +733,25 @@
     }
 
     /**
+     * Note: calls extractNetworkPortion(), so do not use for
+     * SIM EF[ADN] style records
+     *
+     * Returns null if network portion is empty.
+     */
+    public static byte[]
+    networkPortionToCalledPartyBCD(String s) {
+        String networkPortion = extractNetworkPortion(s);
+        return numberToCalledPartyBCDHelper(networkPortion, false);
+    }
+
+    /**
      * Same as {@link #networkPortionToCalledPartyBCD}, but includes a
      * one-byte length prefix.
      */
     public static byte[]
     networkPortionToCalledPartyBCDWithLength(String s) {
-        return numberToCalledPartyBCDWithLength(extractNetworkPortion(s));
+        String networkPortion = extractNetworkPortion(s);
+        return numberToCalledPartyBCDHelper(networkPortion, true);
     }
 
     /**
@@ -761,61 +763,46 @@
      */
     public static byte[]
     numberToCalledPartyBCD(String number) {
-        // The extra byte required for '+' is taken into consideration while calculating
-        // length of ret.
-        int size = (hasPlus(number) ? number.length() - 1 : number.length());
-        byte[] ret = new byte[(size + 1) / 2 + 1];
-
-        return numberToCalledPartyBCDHelper(ret, 0, number);
+        return numberToCalledPartyBCDHelper(number, false);
     }
 
     /**
-     * Same as {@link #numberToCalledPartyBCD}, but includes a
-     * one-byte length prefix.
+     * If includeLength is true, prepend a one-byte length value to
+     * the return array.
      */
     private static byte[]
-    numberToCalledPartyBCDWithLength(String number) {
-        // The extra byte required for '+' is taken into consideration while calculating
-        // length of ret.
-        int size = (hasPlus(number) ? number.length() - 1 : number.length());
-        int length = (size + 1) / 2 + 1;
-        byte[] ret = new byte[length + 1];
+    numberToCalledPartyBCDHelper(String number, boolean includeLength) {
+        int numberLenReal = number.length();
+        int numberLenEffective = numberLenReal;
+        boolean hasPlus = number.indexOf('+') != -1;
+        if (hasPlus) numberLenEffective--;
 
-        ret[0] = (byte) (length & 0xff);
-        return numberToCalledPartyBCDHelper(ret, 1, number);
-    }
+        if (numberLenEffective == 0) return null;
 
-    private static boolean
-    hasPlus(String s) {
-      return s.indexOf('+') >= 0;
-    }
+        int resultLen = (numberLenEffective + 1) / 2;  // Encoded numbers require only 4 bits each.
+        int extraBytes = 1;                            // Prepended TOA byte.
+        if (includeLength) extraBytes++;               // Optional prepended length byte.
+        resultLen += extraBytes;
 
-    private static byte[]
-    numberToCalledPartyBCDHelper(byte[] ret, int offset, String number) {
-        if (hasPlus(number)) {
-            number = number.replaceAll("\\+", "");
-            ret[offset] = (byte) TOA_International;
-        } else {
-            ret[offset] = (byte) TOA_Unknown;
+        byte[] result = new byte[resultLen];
+
+        int digitCount = 0;
+        for (int i = 0; i < numberLenReal; i++) {
+            char c = number.charAt(i);
+            if (c == '+') continue;
+            int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
+            result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
+            digitCount++;
         }
 
-        int size = number.length();
-        int curChar = 0;
-        int countFullBytes = ret.length - offset - 1 - ((size - curChar) & 1);
-        for (int i = 1; i < 1 + countFullBytes; i++) {
-            ret[offset + i]
-                    = (byte) ((charToBCD(number.charAt(curChar++)))
-                              | (charToBCD(number.charAt(curChar++))) << 4);
-        }
+        // 1-fill any trailing odd nibble/quartet.
+        if ((digitCount & 0x01) == 1) result[extraBytes + (digitCount >> 1)] |= 0xF0;
 
-        // The left-over octet for odd-length phone numbers should be
-        // filled with 0xf.
-        if (countFullBytes + offset < ret.length - 1) {
-            ret[ret.length - 1]
-                    = (byte) (charToBCD(number.charAt(curChar))
-                              | (0xf << 4));
-        }
-        return ret;
+        int offset = 0;
+        if (includeLength) result[offset++] = (byte)(resultLen - 1);
+        result[offset] = (byte)(hasPlus ? TOA_International : TOA_Unknown);
+
+        return result;
     }
 
     /** all of 'a' up to len must match non-US trunk prefix ('0') */
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index bf5df88..c9dcd8b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -667,7 +667,10 @@
         } catch (RemoteException ex) {
             // the phone process is restarting.
             return CALL_STATE_IDLE;
-        }
+        } catch (NullPointerException ex) {
+          // the phone process is restarting.
+          return CALL_STATE_IDLE;
+      }
     }
 
     /** Data connection activity: No traffic. */
@@ -701,7 +704,10 @@
         } catch (RemoteException ex) {
             // the phone process is restarting.
             return DATA_ACTIVITY_NONE;
-        }
+        } catch (NullPointerException ex) {
+          // the phone process is restarting.
+          return DATA_ACTIVITY_NONE;
+      }
     }
 
     /** Data connection state: Disconnected. IP traffic not available. */
diff --git a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
index 2984fa8..3bbe0e1 100644
--- a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
+++ b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
@@ -187,22 +187,30 @@
     }
 
     /**
-     * Decode the "Extension-media" type for WSP pdu
-     *
-     * @param startIndex The starting position of the "Extension-media" in this pdu
-     *
-     * @return false when error(not a Extension-media) occur
-     *         return value can be retrieved by getValueString() method
-     *         length of data in pdu can be retrieved by getValue32() method
-     */
+    * Decode the "Extension-media" type for WSP PDU.
+    *
+    * @param startIndex The starting position of the "Extension-media" in this PDU.
+    *
+    * @return false on error, such as if there is no Extension-media at startIndex.
+    * Side-effects: updates stringValue (available with getValueString()), which will be
+    * null on error. The length of the data in the PDU is available with getValue32(), 0
+    * on error.
+    */
     public boolean decodeExtensionMedia(int startIndex) {
         int index = startIndex;
-        while (wspData[index] != 0) {
+        dataLength = 0;
+        stringValue = null;
+        int length = wspData.length;
+        boolean rtrn = index < length;
+
+        while (index < length && wspData[index] != 0) {
             index++;
         }
+
         dataLength  = index - startIndex + 1;
         stringValue = new String(wspData, startIndex, dataLength - 1);
-        return true;
+
+        return rtrn;
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 03bdbda..eaeda64 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -83,7 +83,7 @@
     public static final int MESSAGE_TYPE_DELIVER_REPORT = 0x07;
     public static final int MESSAGE_TYPE_SUBMIT_REPORT  = 0x08;
 
-    public byte messageType;
+    public int messageType;
 
     /**
      * 16-bit value indicating the message ID, which increments modulo 65536.
@@ -102,7 +102,7 @@
     public static final int PRIORITY_EMERGENCY     = 0x3;
 
     public boolean priorityIndicatorSet = false;
-    public byte priority = PRIORITY_NORMAL;
+    public int priority = PRIORITY_NORMAL;
 
     /**
      * Supported privacy modes for CDMA SMS messages
@@ -114,7 +114,7 @@
     public static final int PRIVACY_SECRET         = 0x3;
 
     public boolean privacyIndicatorSet = false;
-    public byte privacy = PRIVACY_NOT_RESTRICTED;
+    public int privacy = PRIVACY_NOT_RESTRICTED;
 
     /**
      * Supported alert priority modes for CDMA SMS messages
@@ -139,7 +139,7 @@
     public static final int DISPLAY_MODE_USER           = 0x2;
 
     public boolean displayModeSet = false;
-    public byte displayMode = DISPLAY_MODE_DEFAULT;
+    public int displayMode = DISPLAY_MODE_DEFAULT;
 
     /**
      * Language Indicator values.  NOTE: the spec (3GPP2 C.S0015-B,
@@ -789,7 +789,7 @@
         if (inStream.read(8) != 3) {
             throw new CodingException("MESSAGE_IDENTIFIER subparam size incorrect");
         }
-        bData.messageType = (byte)inStream.read(4);
+        bData.messageType = inStream.read(4);
         bData.messageId = inStream.read(8) << 8;
         bData.messageId |= inStream.read(8);
         bData.hasUserDataHeader = (inStream.read(1) == 1);
@@ -919,6 +919,122 @@
         }
     }
 
+    /**
+     * IS-91 Voice Mail message decoding
+     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
+     * (For character encodings, see TIA/EIA/IS-91, Annex B)
+     *
+     * Protocol Summary: The user data payload may contain 3-14
+     * characters.  The first two characters are parsed as a number
+     * and indicate the number of voicemails.  The third character is
+     * either a SPACE or '!' to indicate normal or urgent priority,
+     * respectively.  Any following characters are treated as normal
+     * text user data payload.
+     *
+     * Note that the characters encoding is 6-bit packed.
+     */
+    private static void decodeIs91VoicemailStatus(BearerData bData)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
+        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
+        int numFields = bData.userData.numFields;
+        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
+            throw new CodingException("IS-91 voicemail status decoding failed");
+        }
+        try {
+            StringBuffer strbuf = new StringBuffer(dataLen);
+            while (inStream.available() >= 6) {
+                strbuf.append(UserData.IA5_MAP[inStream.read(6)]);
+            }
+            String data = strbuf.toString();
+            bData.numberOfMessages = Integer.parseInt(data.substring(0, 2));
+            char prioCode = data.charAt(2);
+            if (prioCode == ' ') {
+                bData.priority = PRIORITY_NORMAL;
+            } else if (prioCode == '!') {
+                bData.priority = PRIORITY_URGENT;
+            } else {
+                throw new CodingException("IS-91 voicemail status decoding failed: " +
+                        "illegal priority setting (" + prioCode + ")");
+            }
+            bData.priorityIndicatorSet = true;
+            bData.userData.payloadStr = data.substring(3, numFields - 3);
+       } catch (java.lang.NumberFormatException ex) {
+            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
+        } catch (java.lang.IndexOutOfBoundsException ex) {
+            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
+        }
+    }
+
+    /**
+     * IS-91 Short Message decoding
+     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
+     * (For character encodings, see TIA/EIA/IS-91, Annex B)
+     *
+     * Protocol Summary: The user data payload may contain 1-14
+     * characters, which are treated as normal text user data payload.
+     * Note that the characters encoding is 6-bit packed.
+     */
+    private static void decodeIs91ShortMessage(BearerData bData)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
+        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
+        int numFields = bData.userData.numFields;
+        if ((dataLen > 14) || (dataLen < numFields)) {
+            throw new CodingException("IS-91 voicemail status decoding failed");
+        }
+        StringBuffer strbuf = new StringBuffer(dataLen);
+        for (int i = 0; i < numFields; i++) {
+            strbuf.append(UserData.IA5_MAP[inStream.read(6)]);
+        }
+        bData.userData.payloadStr = strbuf.toString();
+    }
+
+    /**
+     * IS-91 CLI message (callback number) decoding
+     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
+     *
+     * Protocol Summary: The data payload may contain 1-32 digits,
+     * encoded using standard 4-bit DTMF, which are treated as a
+     * callback number.
+     */
+    private static void decodeIs91Cli(BearerData bData) throws CodingException {
+        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
+        int dataLen = inStream.available() / 4;  // 4-bit packed DTMF digit encoding.
+        int numFields = bData.userData.numFields;
+        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
+            throw new CodingException("IS-91 voicemail status decoding failed");
+        }
+        CdmaSmsAddress addr = new CdmaSmsAddress();
+        addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
+        addr.origBytes = bData.userData.payload;
+        addr.numberOfDigits = (byte)numFields;
+        decodeSmsAddress(addr);
+        bData.callbackNumber = addr;
+    }
+
+    private static void decodeIs91(BearerData bData)
+        throws BitwiseInputStream.AccessException, CodingException
+    {
+        switch (bData.userData.msgType) {
+        case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS:
+            decodeIs91VoicemailStatus(bData);
+            break;
+        case UserData.IS91_MSG_TYPE_CLI:
+            decodeIs91Cli(bData);
+            break;
+        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL:
+        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE:
+            decodeIs91ShortMessage(bData);
+            break;
+        default:
+            throw new CodingException("unsupported IS-91 message type (" +
+                    bData.userData.msgType + ")");
+        }
+    }
+
     private static void decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
         throws BitwiseInputStream.AccessException, CodingException
     {
@@ -987,16 +1103,16 @@
     {
         int paramBytes = inStream.read(8);
         CdmaSmsAddress addr = new CdmaSmsAddress();
-        addr.digitMode = (byte)inStream.read(1);
+        addr.digitMode = inStream.read(1);
         byte fieldBits = 4;
         byte consumedBits = 1;
         if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
             addr.ton = inStream.read(3);
-            addr.numberPlan = (byte)inStream.read(4);
+            addr.numberPlan = inStream.read(4);
             fieldBits = 8;
             consumedBits += 7;
         }
-        addr.numberOfDigits = (byte)inStream.read(8);
+        addr.numberOfDigits = inStream.read(8);
         consumedBits += 8;
         int remainingBits = (paramBytes * 8) - consumedBits;
         int dataBits = addr.numberOfDigits * fieldBits;
@@ -1076,7 +1192,7 @@
         if (inStream.read(8) != 1) {
             throw new CodingException("PRIVACY_INDICATOR subparam size incorrect");
         }
-        bData.privacy = (byte)inStream.read(2);
+        bData.privacy = inStream.read(2);
         inStream.skip(6);
         bData.privacyIndicatorSet = true;
     }
@@ -1097,7 +1213,7 @@
         if (inStream.read(8) != 1) {
             throw new CodingException("DISPLAY_MODE subparam size incorrect");
         }
-        bData.displayMode = (byte)inStream.read(2);
+        bData.displayMode = inStream.read(2);
         inStream.skip(6);
         bData.displayModeSet = true;
     }
@@ -1108,7 +1224,7 @@
         if (inStream.read(8) != 1) {
             throw new CodingException("PRIORITY_INDICATOR subparam size incorrect");
         }
-        bData.priority = (byte)inStream.read(2);
+        bData.priority = inStream.read(2);
         inStream.skip(6);
         bData.priorityIndicatorSet = true;
     }
@@ -1219,7 +1335,18 @@
                 throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
             }
             if (bData.userData != null) {
-                decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
+                if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
+                    if ((foundSubparamMask ^
+                             (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^
+                             (1 << SUBPARAM_USER_DATA))
+                            != 0) {
+                        Log.e(LOG_TAG, "IS-91 must occur without extra subparams (" +
+                              foundSubparamMask + ")");
+                    }
+                    decodeIs91(bData);
+                } else {
+                    decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
+                }
             }
             return bData;
         } catch (BitwiseInputStream.AccessException ex) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
index 917ec9d..4d799665 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
@@ -29,7 +29,7 @@
     static public final int DIGIT_MODE_4BIT_DTMF              = 0x00;
     static public final int DIGIT_MODE_8BIT_CHAR              = 0x01;
 
-    public byte digitMode;
+    public int digitMode;
 
     /**
      * Number Mode Indicator is 1-bit value that indicates whether the
@@ -39,7 +39,7 @@
     static public final int NUMBER_MODE_NOT_DATA_NETWORK      = 0x00;
     static public final int NUMBER_MODE_DATA_NETWORK          = 0x01;
 
-    public byte numberMode;
+    public int numberMode;
 
     /**
      * Number Types for data networks.
@@ -65,7 +65,7 @@
      * This field shall be set to the number of address digits
      * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
      */
-    public byte numberOfDigits;
+    public int numberOfDigits;
 
     /**
      * Numbering Plan identification is a 0 or 4-bit value that
@@ -78,7 +78,7 @@
     //static protected final int NUMBERING_PLAN_TELEX             = 0x4;
     //static protected final int NUMBERING_PLAN_PRIVATE           = 0x9;
 
-    public byte numberPlan;
+    public int numberPlan;
 
     /**
      * NOTE: the parsed string address and the raw byte array values
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index d8a48cc..34cbbfa 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -40,12 +40,26 @@
     public static final int ENCODING_GSM_DCS                    = 0x0A;
 
     /**
+     * IS-91 message types.
+     * (See TIA/EIS/IS-91-A-ENGL 1999, table 3.7.1.1-3)
+     */
+    public static final int IS91_MSG_TYPE_VOICEMAIL_STATUS   = 0x82;
+    public static final int IS91_MSG_TYPE_SHORT_MESSAGE_FULL = 0x83;
+    public static final int IS91_MSG_TYPE_CLI                = 0x84;
+    public static final int IS91_MSG_TYPE_SHORT_MESSAGE      = 0x85;
+
+    /**
      * IA5 data encoding character mappings.
      * (See CCITT Rec. T.50 Tables 1 and 3)
      *
      * Note this mapping is the the same as for printable ASCII
      * characters, with a 0x20 offset, meaning that the ASCII SPACE
      * character occurs with code 0x20.
+     *
+     * Note this mapping is also equivalent to that used by the IS-91
+     * protocol, except for the latter using only 6 bits, and hence
+     * mapping only entries up to the '_' character.
+     *
      */
     public static final char[] IA5_MAP = {
         ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
@@ -96,7 +110,6 @@
     public int msgEncoding;
     public boolean msgEncodingSet = false;
 
-    // XXX needed when encoding is IS91 or DCS (not supported yet):
     public int msgType;
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 346944a..c33d4b6 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -377,10 +377,14 @@
             removeMessages(EVENT_RESTORE_DEFAULT_APN);
             setEnabled(type, false);
             if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
+                mRequestedApnType = Phone.APN_TYPE_DEFAULT;
                 if (dataEnabled[APN_DEFAULT_ID]) {
                     return Phone.APN_ALREADY_ACTIVE;
                 } else {
-                    cleanUpConnection(true, Phone.REASON_DATA_DISABLED);
+                    Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
+                    msg.arg1 = 1; // tearDown is true;
+                    msg.obj = Phone.REASON_DATA_DISABLED;
+                    sendMessage(msg);
                     return Phone.APN_REQUEST_STARTED;
                 }
             } else {
@@ -1235,10 +1239,9 @@
     protected void onRestoreDefaultApn() {
         if (DBG) Log.d(LOG_TAG, "Restore default APN");
         setEnabled(Phone.APN_TYPE_MMS, false);
-
+        mRequestedApnType = Phone.APN_TYPE_DEFAULT;
         if (!isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
             cleanUpConnection(true, Phone.REASON_RESTORE_DEFAULT_APN);
-            mRequestedApnType = Phone.APN_TYPE_DEFAULT;
         }
     }
 
@@ -1395,7 +1398,7 @@
 
     protected void onVoiceCallEnded() {
         if (state == State.CONNECTED) {
-            if (mGsmPhone.mSST.isConcurrentVoiceAndData()) {
+            if (!mGsmPhone.mSST.isConcurrentVoiceAndData()) {
                 startNetStatPoll();
                 phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
             } else {
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index b886410..e25de81 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -92,7 +92,6 @@
     byte[] mEfCfis = null;
 
 
-    String spn;
     int spnDisplayCondition;
     // Numeric network codes listed in TS 51.011 EF[SPDI]
     ArrayList<String> spdiNetworks = null;
diff --git a/test-runner/android/test/MoreAsserts.java b/test-runner/android/test/MoreAsserts.java
index 2e74644..9e0d018 100644
--- a/test-runner/android/test/MoreAsserts.java
+++ b/test-runner/android/test/MoreAsserts.java
@@ -24,6 +24,7 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.ArrayList;
 import java.util.regex.MatchResult;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -316,8 +317,11 @@
      */
     public static void assertContentsInOrder(
             String message, Iterable<?> actual, Object... expected) {
-        Assert.assertEquals(message,
-                Arrays.asList(expected), Lists.newArrayList(actual));
+        ArrayList actualList = new ArrayList();
+        for (Object o : actual) {
+            actualList.add(o);
+        }
+        Assert.assertEquals(message, Arrays.asList(expected), actualList);
     }
 
     /**
diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
index 9188e04..2ff0a6a 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
@@ -30,6 +30,8 @@
 
 import java.util.Iterator;
 
+import java.lang.Integer;
+
 import android.util.Log;
 
 public class CdmaSmsTest extends AndroidTestCase {
@@ -679,4 +681,15 @@
         assertEquals(revBearerData.displayModeSet, true);
         assertEquals(revBearerData.displayMode, bearerData.displayMode);
     }
+
+    @SmallTest
+    public void testIs91() throws Exception {
+        String pdu1 = "000320001001070c2039acc13880";
+        BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1));
+        assertEquals(bd1.callbackNumber.address, "3598271");
+        String pdu4 = "000320001001080c283c314724b34e";
+        BearerData bd4 = BearerData.decode(HexDump.hexStringToByteArray(pdu4));
+        assertEquals(bd4.userData.payloadStr, "ABCDEFG");
+    }
+
 }
diff --git a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java
index f623080..0991e8c 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java
@@ -175,6 +175,7 @@
         assertEquals("+" + PHONE_NUMBER, number);
         c.close();
 
+        /*
         c = mDatabase.query("phones", null,
                 "PHONE_NUMBERS_EQUAL(num, '5551212')", null, null, null, null);
         assertNotNull(c);
@@ -183,6 +184,7 @@
         number = c.getString(c.getColumnIndexOrThrow("num"));
         assertEquals("+" + PHONE_NUMBER, number);
         c.close();
+        */
 
         c = mDatabase.query("phones", null,
                 "PHONE_NUMBERS_EQUAL(num, '011" + PHONE_NUMBER + "')", null, null, null, null);
@@ -203,85 +205,97 @@
         c.close();
     }
 
+
+    private void phoneNumberCompare(String phone1, String phone2, boolean equal)
+        throws Exception {
+        String[] temporalPhoneNumbers = new String[2];
+        temporalPhoneNumbers[0] = phone1;
+        temporalPhoneNumbers[1] = phone2;
+
+        Cursor cursor = mDatabase.rawQuery(
+                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
+                temporalPhoneNumbers);
+        try {
+            assertNotNull(cursor);
+            assertTrue(cursor.moveToFirst());
+            if (equal) {
+                assertEquals(String.format("Unexpectedly, \"%s != %s\".", phone1, phone2),
+                        "equal", cursor.getString(0));
+            } else {
+                assertEquals(String.format("Unexpectedly, \"%s\" == \"%s\".", phone1, phone2),
+                        "not equal", cursor.getString(0));
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private void assertPhoneNumberEqual(String phone1, String phone2) throws Exception {
+        phoneNumberCompare(phone1, phone2, true);
+    }
+
+    private void assertPhoneNumberNotEqual(String phone1, String phone2) throws Exception {
+        phoneNumberCompare(phone1, phone2, false);
+    }
+
     /**
      * Tests international matching issues for the PHONE_NUMBERS_EQUAL function.
      * 
      * @throws Exception
      */
+    @SmallTest
     public void testPhoneNumbersEqualInternationl() throws Exception {
-        Cursor c;
-        String[] phoneNumbers = new String[2];
+        assertPhoneNumberEqual("1", "1");
+        assertPhoneNumberEqual("123123", "123123");
+        assertPhoneNumberNotEqual("123123", "923123");
+        assertPhoneNumberNotEqual("123123", "123129");
+        assertPhoneNumberNotEqual("123123", "1231234");
+        assertPhoneNumberNotEqual("123123", "0123123");
+        assertPhoneNumberEqual("650-253-0000", "6502530000");
+        assertPhoneNumberEqual("650-253-0000", "650 253 0000");
+        assertPhoneNumberEqual("650 253 0000", "6502530000");
+        assertPhoneNumberEqual("+1 650-253-0000", "6502530000");
+        assertPhoneNumberEqual("001 650-253-0000", "6502530000");
+        assertPhoneNumberEqual("0111 650-253-0000", "6502530000");
 
         // Russian trunk digit
-        phoneNumbers[0] = "+79161234567"; // globablly dialable number
-        phoneNumbers[1] = "89161234567"; // in-country dialable number
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("equal", c.getString(0));
-        c.close();
+        assertPhoneNumberEqual("+79161234567", "89161234567");
 
         // French trunk digit
-        phoneNumbers[0] = "+33123456789"; // globablly dialable number
-        phoneNumbers[1] = "0123456789"; // in-country dialable number
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("equal", c.getString(0));
-        c.close();
-
+        assertPhoneNumberEqual("+33123456789", "0123456789");
 
         // Trunk digit for city codes in the Netherlands
-        phoneNumbers[0] = "+31771234567"; // globablly dialable number
-        phoneNumbers[1] = "0771234567"; // in-country dialable number
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("equal", c.getString(0));
-        c.close();
+        assertPhoneNumberEqual("+31771234567", "0771234567");
 
         // Test broken caller ID seen on call from Thailand to the US
-        phoneNumbers[0] = "+66811234567"; // in address book
-        phoneNumbers[1] = "166811234567"; // came in from the network
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("equal", c.getString(0));
-        c.close();
+        assertPhoneNumberEqual("+66811234567", "166811234567");
 
         // Test the same in-country number with different country codes
-        phoneNumbers[0] = "+33123456789";
-        phoneNumbers[1] = "+1123456789";
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("not equal", c.getString(0));
-        c.close();
+        assertPhoneNumberNotEqual("+33123456789", "+1123456789");
 
         // Test one number with country code and the other without
-        phoneNumbers[0] = "5125551212";
-        phoneNumbers[1] = "+15125551212";
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("equal", c.getString(0));
-        c.close();
+        assertPhoneNumberEqual("5125551212", "+15125551212");
 
         // Test two NANP numbers that only differ in the area code
-        phoneNumbers[0] = "5125551212";
-        phoneNumbers[1] = "6505551212";
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("not equal", c.getString(0));
-        c.close();
+        assertPhoneNumberNotEqual("5125551212", "6505551212");
+
+        // Japanese phone numbers
+        assertPhoneNumberEqual("090-1234-5678", "+819012345678");
+        assertPhoneNumberEqual("090(1234)5678", "+819012345678");
+        assertPhoneNumberEqual("090-1234-5678", "+81-90-1234-5678");
+
+        // Equador
+        assertPhoneNumberEqual("+593(800)123-1234", "8001231234");
+        assertPhoneNumberEqual("+593-2-1234-123", "21234123");
+
+        // Two continuous 0 at the beginning of the phone string should not be
+        // treated as trunk prefix.
+        assertPhoneNumberNotEqual("008001231234", "8001231234");
+
+        // Confirm that the bug found before does not re-appear.
+        assertPhoneNumberNotEqual("080-1234-5678", "+819012345678");
     }
 
     @MediumTest
diff --git a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
index e8bd239..577d384 100644
--- a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -93,6 +93,13 @@
             assertEquals(b[i], bRet[i]);
         }
 
+        bRet = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength("+17005550020");
+        assertEquals(8, bRet.length);
+        assertEquals(bRet[0], 7);
+        for (int i = 1; i < 8; i++) {
+            assertEquals(b[i - 1], bRet[i]);
+        }
+
         bRet = PhoneNumberUtils.networkPortionToCalledPartyBCD("7005550020");
         assertEquals("7005550020",
             PhoneNumberUtils.calledPartyBCDToString(bRet, 0, bRet.length));
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
index 0218317..e741177 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileList.java
@@ -23,7 +23,9 @@
 import java.util.Map;
 import java.io.File;
 
+import android.app.AlertDialog;
 import android.app.ListActivity;
+import android.content.DialogInterface;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.ListView;
@@ -31,7 +33,7 @@
 import android.os.Bundle;
 
 
-public abstract class FileList extends ListActivity 
+public abstract class FileList extends ListActivity
 {
 	public boolean onKeyDown(int keyCode, KeyEvent event) {
 		switch (keyCode)
@@ -39,7 +41,7 @@
 			case KeyEvent.KEYCODE_DPAD_LEFT:
 				if (mPath.length() > mBaseLength) {
 					File f = new File(mPath);
-					mFocusFile = f.getName(); 
+					mFocusFile = f.getName();
 					mFocusIndex = 0;
 					f = f.getParentFile();
 					mPath = f.getPath();
@@ -47,7 +49,7 @@
 					return true;
 				}
 				break;
-				
+
 			case KeyEvent.KEYCODE_DPAD_RIGHT:
 				{
 					Map map = (Map) getListView().getItemAtPosition(getListView().getSelectedItemPosition());
@@ -61,24 +63,24 @@
 					}
                     return true;
 				}
-	
+
 			default:
 				break;
 		}
 		return super.onKeyDown(keyCode, event);
 	}
 
-	public void onCreate(Bundle icicle) 
+	public void onCreate(Bundle icicle)
     {
         super.onCreate(icicle);
         setupPath();
         updateList();
     }
-    
+
     protected List getData()
     {
         List myData = new ArrayList<HashMap>();
-        
+
         File f = new File(mPath);
         if (!f.exists()) {
         	addItem(myData, "!LayoutTests path missing!", "");
@@ -103,10 +105,10 @@
 	        	    addItem(myData, files[i], path);
         	}
         }
-        
+
         return myData;
     }
-    
+
     protected void addItem(List<Map> data, String name, String path)
     {
         HashMap temp = new HashMap();
@@ -114,34 +116,58 @@
         temp.put("path", path);
         data.add(temp);
     }
-    
+
     protected void onListItemClick(ListView l, View v, int position, long id)
     {
-    	Map map = (Map) l.getItemAtPosition(position);
-    	String path = (String)map.get("path");
+        Map map = (Map) l.getItemAtPosition(position);
+        final String path = (String)map.get("path");
 
         if ((new File(path)).isDirectory()) {
-            mPath = path;
-            mFocusFile = null;
-            updateList();
+            final CharSequence[] items = {"Open", "Run"};
+            AlertDialog.Builder builder = new AlertDialog.Builder(this);
+            builder.setTitle("Select an Action");
+            builder.setSingleChoiceItems(items, -1,
+                    new DialogInterface.OnClickListener(){
+                public void onClick(DialogInterface dialog, int which) {
+                    switch (which) {
+                        case OPEN_DIRECTORY:
+                            dialog.dismiss();
+                            mPath = path;
+                            mFocusFile = null;
+                            updateList();
+                            break;
+                        case RUN_TESTS:
+                            dialog.dismiss();
+                            processDirectory(path, false);
+                            break;
+                    }
+                }
+            });
+            builder.create().show();
         } else {
             processFile(path, false);
         }
     }
-    
+
+    /*
+     * This function is called when the user has selected a directory in the
+     * list and wants to perform an action on it instead of navigating into
+     * the directory.
+     */
+    abstract void processDirectory(String path, boolean selection);
     /*
      * This function is called when the user has selected a file in the
      * file list. The selected file could be a file or a directory.
      * The flag indicates if this was from a selection or not.
      */
     abstract void processFile(String filename, boolean selection);
-    
+
     /*
      * This function is called when the file list is being built. Return
      * true if the file is to be added to the file list.
      */
     abstract boolean fileFilter(File f);
-    
+
     protected void updateList() {
         setListAdapter(new SimpleAdapter(this,
                 getData(),
@@ -152,16 +178,19 @@
         setTitle(title);
         getListView().setSelection(mFocusIndex);
     }
-    
-    protected void setupPath() 
+
+    protected void setupPath()
     {
     	mPath = "/sdcard/android/layout_tests";
     	mBaseLength = mPath.length();
     }
-    
+
     protected String mPath;
     protected int mBaseLength;
     protected String mFocusFile;
     protected int mFocusIndex;
-  
+    
+    private final static int OPEN_DIRECTORY = 0;
+    private final static int RUN_TESTS = 1;
+
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
new file mode 100644
index 0000000..cc2f1f5
--- /dev/null
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
@@ -0,0 +1,80 @@
+package com.android.dumprendertree;
+
+import android.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+
+public class FsUtils {
+
+    private static final String LOGTAG = "FsUtils";
+    private FsUtils() {
+        //no creation of instances
+    }
+
+    public static void findLayoutTestsRecursively(BufferedOutputStream bos,
+            String dir) throws IOException {
+        Log.v(LOGTAG, "Searching tests under " + dir);
+
+        File d = new File(dir);
+        if (!d.isDirectory()) {
+            throw new AssertionError("A directory expected, but got " + dir);
+        }
+
+        String[] files = d.list();
+        for (int i = 0; i < files.length; i++) {
+            String s = dir + "/" + files[i];
+            if (FileFilter.ignoreTest(s)) {
+                Log.v(LOGTAG, "  Ignoring: " + s);
+                continue;
+            }
+            if (s.toLowerCase().endsWith(".html")
+                    || s.toLowerCase().endsWith(".xml")) {
+                bos.write(s.getBytes());
+                bos.write('\n');
+                continue;
+            }
+
+            File f = new File(s);
+            if (f.isDirectory()) {
+                findLayoutTestsRecursively(bos, s);
+                continue;
+            }
+
+            Log.v(LOGTAG, "Skipping " + s);
+        }
+    }
+
+    public static void updateTestStatus(String statusFile, String s) {
+        try {
+            BufferedOutputStream bos = new BufferedOutputStream(
+                    new FileOutputStream(statusFile));
+            bos.write(s.getBytes());
+            bos.close();
+        } catch (Exception e) {
+            Log.e(LOGTAG, "Cannot update file " + statusFile);
+        }
+    }
+
+    public static String readTestStatus(String statusFile) {
+        // read out the test name it stopped last time.
+        String status = null;
+        File testStatusFile = new File(statusFile);
+        if(testStatusFile.exists()) {
+            try {
+                BufferedReader inReader = new BufferedReader(
+                        new FileReader(testStatusFile));
+                status = inReader.readLine();
+                inReader.close();
+            } catch (IOException e) {
+                Log.e(LOGTAG, "Error reading test status.", e);
+            }
+        }
+        return status;
+    }
+
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index f169a26..a03490d 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -178,15 +178,13 @@
     private void resumeTestList() {
         // read out the test name it stoped last time.
         try {
-            BufferedReader inReader = new BufferedReader(new FileReader(TEST_STATUS_FILE));
-            String line = inReader.readLine();
+            String line = FsUtils.readTestStatus(TEST_STATUS_FILE);
             for (int i = 0; i < mTestList.size(); i++) {
                 if (mTestList.elementAt(i).equals(line)) {
                     mTestList = new Vector<String>(mTestList.subList(i+1, mTestList.size()));
                     break;
                 }
             }
-            inReader.close();
         } catch (Exception e) {
             Log.e(LOGTAG, "Error reading " + TEST_STATUS_FILE);
         }
@@ -204,18 +202,7 @@
             Log.e(LOGTAG, "Fail to delete " + TEST_STATUS_FILE + " : " + e.getMessage());
         }
     }
-  
-    private void updateTestStatus(String s) {
-        // Write TEST_STATUS_FILE
-        try {
-            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(TEST_STATUS_FILE));
-            bos.write(s.getBytes());
-            bos.close();
-        } catch (Exception e) {
-            Log.e(LOGTAG, "Cannot update file " + TEST_STATUS_FILE);
-        }
-    }
-    
+
     private String getResultFile(String test) {
         String shortName = test.substring(0, test.lastIndexOf('.'));
         // Write actual results to result directory.
@@ -392,12 +379,12 @@
         // Run tests.
         for (int i = 0; i < mTestList.size(); i++) {
             String s = mTestList.elementAt(i);
-            updateTestStatus(s);
+            FsUtils.updateTestStatus(TEST_STATUS_FILE, s);
             // Run tests
             runTestAndWaitUntilDone(activity, s, runner.mTimeoutInMillis);
         }
 
-        updateTestStatus("#DONE");
+        FsUtils.updateTestStatus(TEST_STATUS_FILE, "#DONE");
         
         activity.finish();
     }
@@ -424,7 +411,7 @@
         try {
             File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false));
-            findTestsRecursively(bos, getTestPath());
+            FsUtils.findLayoutTestsRecursively(bos, getTestPath());
             bos.flush();
             bos.close();
        } catch (Exception e) {
@@ -432,38 +419,6 @@
        }
     }
 
-    private void findTestsRecursively(BufferedOutputStream bos, String dir) throws IOException {
-         Log.v(LOGTAG, "Searching tests under " + dir);
-         
-         File d = new File(dir);
-         if (!d.isDirectory()) {
-             throw new AssertionError("A directory expected, but got " + dir);
-         }
-         
-         String[] files = d.list();
-         for (int i = 0; i < files.length; i++) {
-             String s = dir + "/" + files[i];
-             if (FileFilter.ignoreTest(s)) {
-                 Log.v(LOGTAG, "  Ignoring: " + s);
-                 continue;
-             }
-             if (s.toLowerCase().endsWith(".html") 
-                 || s.toLowerCase().endsWith(".xml")) {
-                 bos.write(s.getBytes());
-                 bos.write('\n');
-                 continue;
-             }
-             
-             File f = new File(s);
-             if (f.isDirectory()) {
-                 findTestsRecursively(bos, s);
-                 continue;
-             }
-             
-             Log.v(LOGTAG, "Skipping " + s);
-        }
-    }
-    
     // Running all the layout tests at once sometimes
     // causes the dumprendertree to run out of memory.
     // So, additional tests are added to run the tests
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
index c792e8e..cbcac6c 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
@@ -16,18 +16,15 @@
 
 package com.android.dumprendertree;
 
+import dalvik.system.VMRuntime;
+
 import android.app.Instrumentation;
 import android.content.Intent;
-
-import android.util.Log;
-
 import android.os.Bundle;
 import android.os.Debug;
-import android.os.Debug.MemoryInfo;
+import android.os.Process;
 import android.test.ActivityInstrumentationTestCase2;
-
-import com.android.dumprendertree.TestShellActivity;
-import com.android.dumprendertree.TestShellCallback;
+import android.util.Log;
 
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -70,9 +67,7 @@
         TestShellActivity activity = (TestShellActivity) getActivity();
 
         Log.v(LOGTAG, "About to run tests, calling gc first...");
-        Runtime.getRuntime().runFinalization();
-        Runtime.getRuntime().gc();
-        Runtime.getRuntime().gc();
+        freeMem();
 
         // Run tests
         runTestAndWaitUntilDone(activity, runner.mTestPath, runner.mTimeoutInMillis);
@@ -83,46 +78,73 @@
         activity.finish();
     }
 
+    private void freeMem() {
+        Log.v(LOGTAG, "freeMem: calling gc/finalization...");
+        final VMRuntime runtime = VMRuntime.getRuntime();
+
+        runtime.gcSoftReferences();
+        runtime.runFinalizationSync();
+        runtime.gcSoftReferences();
+        runtime.runFinalizationSync();
+        runtime.gcSoftReferences();
+        runtime.runFinalizationSync();
+        Runtime.getRuntime().runFinalization();
+        Runtime.getRuntime().gc();
+        Runtime.getRuntime().gc();
+
+    }
+
+    private void printRow(PrintStream ps, String format, Object...objs) {
+        ps.println(String.format(format, objs));
+    }
+
     private void dumpMemoryInfo() {
         try {
-            Log.v(LOGTAG, "About to dump meminfo, calling gc first...");
-            Runtime.getRuntime().runFinalization();
-            Runtime.getRuntime().gc();
-            Runtime.getRuntime().gc();
-
+            freeMem();
             Log.v(LOGTAG, "Dumping memory information.");
 
             FileOutputStream out = new FileOutputStream(LOAD_TEST_RESULT, true);
             PrintStream ps = new PrintStream(out);
 
-            MemoryInfo mi = new MemoryInfo();
-            Debug.getMemoryInfo(mi);
-
-            //try to fake the dumpsys format
-            //this will eventually be changed to XML
-            String format = "%15s:%9d%9d%9d%9d";
-            String pss =
-              String.format(format, "(Pss)",
-                  mi.nativePss, mi.dalvikPss, mi.otherPss,
-                  mi.nativePss + mi.dalvikPss + mi.otherPss);
-            String sd =
-              String.format(format, "(shared dirty)",
-                  mi.nativeSharedDirty, mi.dalvikSharedDirty, mi.otherSharedDirty,
-                  mi.nativeSharedDirty + mi.dalvikSharedDirty + mi.otherSharedDirty);
-            String pd =
-              String.format(format, "(priv dirty)",
-                  mi.nativePrivateDirty, mi.dalvikPrivateDirty, mi.otherPrivateDirty,
-                  mi.nativePrivateDirty + mi.dalvikPrivateDirty + mi.otherPrivateDirty);
-
             ps.print("\n\n\n");
-            ps.println("** MEMINFO in pid 0 [com.android.dumprendertree] **");
-            ps.println("                   native   dalvik    other    total");
-            ps.println("           size:    12060     5255      N/A    17315");
-            ps.println("      allocated:    12060     5255      N/A    17315");
-            ps.println("           free:    12060     5255      N/A    17315");
-            ps.println(pss);
-            ps.println(sd);
-            ps.println(pd);
+            ps.println("** MEMINFO in pid " + Process.myPid()
+                    + " [com.android.dumprendertree] **");
+            String formatString = "%17s %8s %8s %8s %8s";
+
+            long nativeMax = Debug.getNativeHeapSize() / 1024;
+            long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
+            long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
+            Runtime runtime = Runtime.getRuntime();
+            long dalvikMax = runtime.totalMemory() / 1024;
+            long dalvikFree = runtime.freeMemory() / 1024;
+            long dalvikAllocated = dalvikMax - dalvikFree;
+
+
+            Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
+            Debug.getMemoryInfo(memInfo);
+
+            final int nativeShared = memInfo.nativeSharedDirty;
+            final int dalvikShared = memInfo.dalvikSharedDirty;
+            final int otherShared = memInfo.otherSharedDirty;
+
+            final int nativePrivate = memInfo.nativePrivateDirty;
+            final int dalvikPrivate = memInfo.dalvikPrivateDirty;
+            final int otherPrivate = memInfo.otherPrivateDirty;
+
+            printRow(ps, formatString, "", "native", "dalvik", "other", "total");
+            printRow(ps, formatString, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax);
+            printRow(ps, formatString, "allocated:", nativeAllocated, dalvikAllocated, "N/A",
+                    nativeAllocated + dalvikAllocated);
+            printRow(ps, formatString, "free:", nativeFree, dalvikFree, "N/A",
+                    nativeFree + dalvikFree);
+
+            printRow(ps, formatString, "(Pss):", memInfo.nativePss, memInfo.dalvikPss,
+                    memInfo.otherPss, memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss);
+
+            printRow(ps, formatString, "(shared dirty):", nativeShared, dalvikShared, otherShared,
+                    nativeShared + dalvikShared + otherShared);
+            printRow(ps, formatString, "(priv dirty):", nativePrivate, dalvikPrivate, otherPrivate,
+                    nativePrivate + dalvikPrivate + otherPrivate);
             ps.print("\n\n\n");
             ps.flush();
             ps.close();
@@ -142,7 +164,7 @@
                     LoadTestsAutoTest.this.notifyAll();
                 }
             }
-            
+
             public void timedOut(String url) {
             }
         });
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
index 00e0f89..e15ab65 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
@@ -17,19 +17,23 @@
 package com.android.dumprendertree;
 
 import android.content.Intent;
-import android.net.Uri;
 import android.os.Bundle;
 import android.util.Log;
 
+import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 
 public class Menu extends FileList {
-    
-    public void onCreate(Bundle icicle) 
-    {
+
+    private static final int MENU_START = 0x01;
+    private static String LOGTAG = "MenuActivity";
+    static final String LAYOUT_TESTS_LIST_FILE = "/sdcard/android/layout_tests_list.txt";
+
+    public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
     }
-    
+
     boolean fileFilter(File f) {
     	if (f.getName().startsWith("."))
     		return false;
@@ -41,14 +45,36 @@
     		return true;
     	return false;
     }
-    
-    void processFile(String filename, boolean selection)
-    {        
+
+    void processFile(String filename, boolean selection) {
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setClass(this, TestShellActivity.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         intent.putExtra(TestShellActivity.TEST_URL, "file://" + filename);
         startActivity(intent);
     }
+
+    @Override
+    void processDirectory(String path, boolean selection) {
+        generateTestList(path);
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.setClass(this, TestShellActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+        intent.putExtra(TestShellActivity.UI_AUTO_TEST, LAYOUT_TESTS_LIST_FILE);
+        startActivity(intent);
+    }
+
+    private void generateTestList(String path) {
+        try {
+            File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
+            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false));
+            FsUtils.findLayoutTestsRecursively(bos, path);
+            bos.flush();
+            bos.close();
+       } catch (Exception e) {
+           Log.e(LOGTAG, "Error when creating test list: " + e.getMessage());
+       }
+    }
+
 }
 
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
index 16973be..de39800 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
@@ -45,7 +45,7 @@
 
         //always try to resume first, hence cleaning up status will be the
         //responsibility of driver scripts
-        String lastUrl = readTestStatus();
+        String lastUrl = FsUtils.readTestStatus(TEST_STATUS_FILE);
         if(lastUrl != null && !TEST_DONE.equals(lastUrl))
             fastForward(listReader, lastUrl);
 
@@ -62,7 +62,7 @@
                 continue;
             start = System.currentTimeMillis();
             Log.v(LOGTAG, "Testing URL: " + url);
-            updateTestStatus(url);
+            FsUtils.updateTestStatus(TEST_STATUS_FILE, url);
             activity.reset();
             //use message to send new URL to avoid interacting with
             //WebView in non-UI thread
@@ -92,7 +92,7 @@
             System.gc();
             System.gc();
         }
-        updateTestStatus(TEST_DONE);
+        FsUtils.updateTestStatus(TEST_STATUS_FILE, TEST_DONE);
         activity.finish();
         listReader.close();
     }
@@ -122,35 +122,6 @@
         }
     }
 
-    private void updateTestStatus(String s) {
-        // write last tested url into status file
-        try {
-            BufferedOutputStream bos = new BufferedOutputStream(
-                    new FileOutputStream(TEST_STATUS_FILE));
-            bos.write(s.getBytes());
-            bos.close();
-        } catch (IOException e) {
-            Log.e(LOGTAG, "Cannot update file " + TEST_STATUS_FILE, e);
-        }
-    }
-
-    private String readTestStatus() {
-        // read out the test name it stopped last time.
-        String status = null;
-        File testStatusFile = new File(TEST_STATUS_FILE);
-        if(testStatusFile.exists()) {
-            try {
-                BufferedReader inReader = new BufferedReader(
-                        new FileReader(testStatusFile));
-                status = inReader.readLine();
-                inReader.close();
-            } catch (IOException e) {
-                Log.e(LOGTAG, "Error reading test status.", e);
-            }
-        }
-        return status;
-    }
-
     private void fastForward(BufferedReader testListReader, String lastUrl) {
         //fastforward the BufferedReader to the position right after last url
         if(lastUrl == null)
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 1ba291c..0d22eca 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -17,7 +17,10 @@
 package com.android.dumprendertree;
 
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.DialogInterface.OnClickListener;
 import android.graphics.Bitmap;
 import android.net.http.SslError;
 import android.os.Bundle;
@@ -35,21 +38,24 @@
 import android.webkit.WebViewClient;
 import android.widget.LinearLayout;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
 import java.util.Vector;
 
 public class TestShellActivity extends Activity implements LayoutTestController {
-    
+
     static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP}
-    
+
     public class AsyncHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
             if (msg.what == MSG_TIMEOUT) {
                 mTimedOut = true;
-                mCallback.timedOut(mWebView.getUrl());
+                if(mCallback != null)
+                    mCallback.timedOut(mWebView.getUrl());
                 requestWebKitData();
                 return;
             } else if (msg.what == MSG_WEBKIT_DATA) {
@@ -63,10 +69,10 @@
 
     public void requestWebKitData() {
         Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA);
-        
+
         if (mRequestedWebKitData)
             throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl());
-        
+
         mRequestedWebKitData = true;
         switch (mDumpDataType) {
             case DUMP_AS_TEXT:
@@ -79,12 +85,12 @@
                 finished();
                 break;
         }
-    } 
+    }
 
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        
+
         LinearLayout contentView = new LinearLayout(this);
         contentView.setOrientation(LinearLayout.VERTICAL);
         setContentView(contentView);
@@ -133,59 +139,122 @@
         mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController");
         mWebView.addJavascriptInterface(mCallbackProxy, "eventSender");
         contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0f));
- 
+
         mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
-            
+
         mHandler = new AsyncHandler();
-        
+
         Intent intent = getIntent();
         if (intent != null) {
             executeIntent(intent);
         }
     }
-    
+
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
         executeIntent(intent);
     }
-    
+
     private void executeIntent(Intent intent) {
         resetTestStatus();
         if (!Intent.ACTION_VIEW.equals(intent.getAction())) {
             return;
         }
-        
+
         mTestUrl = intent.getStringExtra(TEST_URL);
-        if (mTestUrl == null)
+        if (mTestUrl == null) {
+            mUiAutoTestPath = intent.getStringExtra(UI_AUTO_TEST);
+            if(mUiAutoTestPath != null) {
+                beginUiAutoTest();
+            }
             return;
-        
+        }
+
         mResultFile = intent.getStringExtra(RESULT_FILE);
         mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0);
 
         Log.v(LOGTAG, "  Loading " + mTestUrl);
         mWebView.loadUrl(mTestUrl);
-            
+
         if (mTimeoutInMillis > 0) {
             // Create a timeout timer
             Message m = mHandler.obtainMessage(MSG_TIMEOUT);
             mHandler.sendMessageDelayed(m, mTimeoutInMillis);
         }
     }
-    
+
+    private void beginUiAutoTest() {
+        try {
+            mTestListReader = new BufferedReader(
+                    new FileReader(mUiAutoTestPath));
+        } catch (IOException ioe) {
+            Log.e(LOGTAG, "Failed to open test list for read.", ioe);
+            finishUiAutoTest();
+            return;
+        }
+        moveToNextTest();
+    }
+
+    private void finishUiAutoTest() {
+        try {
+            if(mTestListReader != null)
+                mTestListReader.close();
+        } catch (IOException ioe) {
+            Log.w(LOGTAG, "Failed to close test list file.", ioe);
+        }
+        finished();
+    }
+
+    private void moveToNextTest() {
+        String url = null;
+        try {
+            url = mTestListReader.readLine();
+        } catch (IOException ioe) {
+            Log.e(LOGTAG, "Failed to read next test.", ioe);
+            finishUiAutoTest();
+            return;
+        }
+        if (url == null) {
+            mUiAutoTestPath = null;
+            finishUiAutoTest();
+            AlertDialog.Builder builder = new AlertDialog.Builder(this);
+            builder.setMessage("All tests finished. Exit?")
+                   .setCancelable(false)
+                   .setPositiveButton("Yes", new OnClickListener(){
+                       public void onClick(DialogInterface dialog, int which) {
+                           TestShellActivity.this.finish();
+                       }
+                   })
+                   .setNegativeButton("No", new OnClickListener(){
+                       public void onClick(DialogInterface dialog, int which) {
+                           dialog.cancel();
+                       }
+                   });
+            builder.create().show();
+            return;
+        }
+        url = "file://" + url;
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+        intent.putExtra(TestShellActivity.TEST_URL, url);
+        intent.putExtra(TIMEOUT_IN_MILLIS, 10000);
+        executeIntent(intent);
+    }
+
     @Override
     protected void onStop() {
         super.onStop();
         mWebView.stopLoading();
     }
-    
+
     @Override
     protected void onDestroy() {
         super.onDestroy();
         mWebView.destroy();
         mWebView = null;
     }
-    
+
     @Override
     public void onLowMemory() {
         super.onLowMemory();
@@ -199,13 +268,13 @@
             finished();
             return;
         }
-        
+
         try {
             File parentDir = new File(mResultFile).getParentFile();
             if (!parentDir.exists()) {
                 parentDir.mkdirs();
             }
-            
+
             FileOutputStream os = new FileOutputStream(mResultFile);
             if (timeout) {
                 Log.w("Layout test: Timeout", mResultFile);
@@ -222,22 +291,27 @@
             os.flush();
             os.close();
         } catch (IOException ex) {
-            Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage());          
+            Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage());
         }
 
         finished();
     }
-    
+
     public void setCallback(TestShellCallback callback) {
         mCallback = callback;
     }
-    
+
     public void finished() {
-        if (mCallback != null) {
-            mCallback.finished();
+        if (mUiAutoTestPath != null) {
+            //don't really finish here
+            moveToNextTest();
+        } else {
+            if (mCallback != null) {
+                mCallback.finished();
+            }
         }
     }
-   
+
     public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) {
         mDefaultDumpDataType = defaultDumpDataType;
     }
@@ -257,7 +331,7 @@
         String url = mWebView.getUrl();
         Log.v(LOGTAG, "waitUntilDone called: " + url);
     }
-    
+
     public void notifyDone() {
         String url = mWebView.getUrl();
         Log.v(LOGTAG, "notifyDone called: " + url);
@@ -266,7 +340,7 @@
             mChromeClient.onProgressChanged(mWebView, 100);
         }
     }
-    
+
     public void display() {
         mWebView.invalidate();
     }
@@ -332,7 +406,7 @@
     }
 
     public void queueScript(String scriptToRunInCurrentContext) {
-        mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext);     
+        mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext);
     }
 
     public void repaintSweepHorizontally() {
@@ -359,7 +433,7 @@
     public void testRepaint() {
         mWebView.invalidate();
     }
-    
+
     private final WebChromeClient mChromeClient = new WebChromeClient() {
         @Override
         public void onProgressChanged(WebView view, int newProgress) {
@@ -406,7 +480,7 @@
             result.confirm();
             return true;
         }
-        
+
         @Override
         public boolean onJsConfirm(WebView view, String url, String message,
                 JsResult result) {
@@ -419,7 +493,7 @@
             result.confirm();
             return true;
         }
-        
+
         @Override
         public boolean onJsPrompt(WebView view, String url, String message,
                 String defaultValue, JsPromptResult result) {
@@ -435,7 +509,7 @@
             return true;
         }
     };
-    
+
     private void resetTestStatus() {
         mWaitUntilDone = false;
         mDumpDataType = mDefaultDumpDataType;
@@ -444,17 +518,19 @@
         mRequestedWebKitData = false;
         mEventSender.resetMouse();
     }
-    
+
     private WebView mWebView;
     private WebViewEventSender mEventSender;
     private AsyncHandler mHandler;
     private TestShellCallback mCallback;
 
     private CallbackProxy mCallbackProxy;
-        
+
     private String mTestUrl;
     private String mResultFile;
     private int mTimeoutInMillis;
+    private String mUiAutoTestPath;
+    private BufferedReader mTestListReader;
 
     // States
     private boolean mTimedOut;
@@ -472,13 +548,14 @@
     private Vector mWebHistory;
 
     static final String TIMEOUT_STR = "**Test timeout";
-    
+
     static final int MSG_TIMEOUT = 0;
     static final int MSG_WEBKIT_DATA = 1;
 
     static final String LOGTAG="TestShell";
-    
+
     static final String TEST_URL = "TestUrl";
     static final String RESULT_FILE = "ResultFile";
     static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis";
+    static final String UI_AUTO_TEST = "UiAutoTest";
 }