am 0edbc0eb: Import translations. DO NOT MERGE

* commit '0edbc0eb76eb0b493f9cd9971d41543ec79136d5':
  Import translations. DO NOT MERGE
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 43a163d..6382cee 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -858,6 +858,9 @@
      */
     public Intent getAssistIntent(Context context, int userHandle) {
         try {
+            if (mService == null) {
+                return null;
+            }
             ComponentName comp = mService.getAssistIntent(userHandle);
             if (comp == null) {
                 return null;
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index cb61a71..24fd2e4 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -224,6 +224,22 @@
         }
     }
 
+    /**
+     * Gets a list of all the appWidgetIds that are bound to the current host
+     *
+     * @hide
+     */
+    public int[] getAppWidgetIds() {
+        try {
+            if (sService == null) {
+                bindService();
+            }
+            return sService.getAppWidgetIdsForHost(mHostId);
+        } catch (RemoteException e) {
+            throw new RuntimeException("system server dead?", e);
+        }
+    }
+
     private static void checkCallerIsSystem() {
         int uid = Process.myUid();
         if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 977b461..e4b4b97 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -58,6 +58,7 @@
 import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
@@ -155,7 +156,7 @@
 
     private SyncStorageEngine mSyncStorageEngine;
 
-    // @GuardedBy("mSyncQueue")
+    @GuardedBy("mSyncQueue")
     private final SyncQueue mSyncQueue;
 
     protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList();
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 10e7bff..bdc5a3f 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 
@@ -74,7 +75,7 @@
 
     private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day
 
-    // @VisibleForTesting
+    @VisibleForTesting
     static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4;
 
     /** Enum value for a sync start event. */
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 6def4a1..a07a865 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -34,6 +34,7 @@
 import android.util.SparseArray;
 import android.util.Xml;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.FastXmlSerializer;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
@@ -77,15 +78,15 @@
 
     private final Object mServicesLock = new Object();
 
-    // @GuardedBy("mServicesLock")
+    @GuardedBy("mServicesLock")
     private boolean mPersistentServicesFileDidNotExist;
-    // @GuardedBy("mServicesLock")
+    @GuardedBy("mServicesLock")
     private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>();
 
     private static class UserServices<V> {
-        // @GuardedBy("mServicesLock")
+        @GuardedBy("mServicesLock")
         public final Map<V, Integer> persistentServices = Maps.newHashMap();
-        // @GuardedBy("mServicesLock")
+        @GuardedBy("mServicesLock")
         public Map<V, ServiceInfo<V>> services = null;
     }
 
diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java
index 0138b1c..2fd52b8 100644
--- a/core/java/android/hardware/display/WifiDisplay.java
+++ b/core/java/android/hardware/display/WifiDisplay.java
@@ -107,6 +107,15 @@
                 && Objects.equal(mDeviceAlias, other.mDeviceAlias);
     }
 
+    /**
+     * Returns true if the other display is not null and has the same address as this one.
+     * Can be used to perform identity comparisons on displays ignoring properties
+     * that might change during a connection such as the name or alias.
+     */
+    public boolean hasSameAddress(WifiDisplay other) {
+        return other != null && mDeviceAddress.equals(other.mDeviceAddress);
+    }
+
     @Override
     public int hashCode() {
         // The address on its own should be sufficiently unique for hashing purposes.
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index f07002e..6f1cc94 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -930,11 +930,13 @@
      */
     public void onConfigureWindow(Window win, boolean isFullscreen,
             boolean isCandidatesOnly) {
-        if (isFullscreen) {
-            mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT);
-        } else {
-            mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
+        final int currentHeight = mWindow.getWindow().getAttributes().height;
+        final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT;
+        if (mIsInputViewShown && currentHeight != newHeight) {
+            Log.w(TAG, "Window size has been changed. This may cause jankiness of resizing window: "
+                    + currentHeight + " -> " + newHeight);
         }
+        mWindow.getWindow().setLayout(MATCH_PARENT, newHeight);
     }
     
     /**
@@ -997,10 +999,11 @@
     }
     
     void updateExtractFrameVisibility() {
-        int vis;
+        final int vis;
         if (isFullscreenMode()) {
             vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
-            mExtractFrame.setVisibility(View.VISIBLE);
+            // "vis" should be applied for the extract frame as well in the fullscreen mode.
+            mExtractFrame.setVisibility(vis);
         } else {
             vis = View.VISIBLE;
             mExtractFrame.setVisibility(View.GONE);
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index 874e80a..8dc900e 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -351,6 +351,8 @@
         DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
 
         if (dhcpAction == DhcpAction.START) {
+            /* Stop any existing DHCP daemon before starting new */
+            NetworkUtils.stopDhcp(mInterfaceName);
             if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName);
             success = NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal);
             mDhcpInfo = dhcpInfoInternal;
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 446bbf0..c757605 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -21,6 +21,7 @@
 import android.os.SystemClock;
 import android.util.SparseBooleanArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Objects;
 
@@ -190,14 +191,14 @@
         return clone;
     }
 
-    // @VisibleForTesting
+    @VisibleForTesting
     public NetworkStats addIfaceValues(
             String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
         return addValues(
                 iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L);
     }
 
-    // @VisibleForTesting
+    @VisibleForTesting
     public NetworkStats addValues(String iface, int uid, int set, int tag, long rxBytes,
             long rxPackets, long txBytes, long txPackets, long operations) {
         return addValues(new Entry(
@@ -269,7 +270,7 @@
         return size;
     }
 
-    // @VisibleForTesting
+    @VisibleForTesting
     public int internalSize() {
         return iface.length;
     }
@@ -335,7 +336,7 @@
      * Find first stats index that matches the requested parameters, starting
      * search around the hinted index as an optimization.
      */
-    // @VisibleForTesting
+    @VisibleForTesting
     public int findIndexHinted(String iface, int uid, int set, int tag, int hintIndex) {
         for (int offset = 0; offset < size; offset++) {
             final int halfOffset = offset / 2;
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index d8e53d5..d3839ad 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -33,6 +33,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Objects;
 
 /**
@@ -63,7 +64,7 @@
 
     private static boolean sForceAllNetworkTypes = false;
 
-    // @VisibleForTesting
+    @VisibleForTesting
     public static void forceAllNetworkTypes() {
         sForceAllNetworkTypes = true;
     }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 88529f8..1bada67 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -22,6 +22,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.io.File;
 
 /**
@@ -47,7 +49,7 @@
 
     private static final Object sLock = new Object();
 
-    // @GuardedBy("sLock")
+    @GuardedBy("sLock")
     private static volatile StorageVolume sPrimaryVolume;
 
     private static StorageVolume getPrimaryVolume() {
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 3e90dfc..ec660ee 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -15,6 +15,9 @@
  */
 
 package android.os;
+
+import dalvik.system.CloseGuard;
+
 import java.io.Closeable;
 import java.io.File;
 import java.io.FileDescriptor;
@@ -31,12 +34,16 @@
  */
 public class ParcelFileDescriptor implements Parcelable, Closeable {
     private final FileDescriptor mFileDescriptor;
-    private boolean mClosed;
-    //this field is to create wrapper for ParcelFileDescriptor using another
-    //PartialFileDescriptor but avoid invoking close twice
-    //consider ParcelFileDescriptor A(fileDescriptor fd),  ParcelFileDescriptor B(A)
-    //in this particular case fd.close might be invoked twice.
-    private final ParcelFileDescriptor mParcelDescriptor;
+
+    /**
+     * Wrapped {@link ParcelFileDescriptor}, if any. Used to avoid
+     * double-closing {@link #mFileDescriptor}.
+     */
+    private final ParcelFileDescriptor mWrapped;
+
+    private volatile boolean mClosed;
+
+    private final CloseGuard mGuard = CloseGuard.get();
 
     /**
      * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied
@@ -289,13 +296,15 @@
         if (mClosed) {
             throw new IllegalStateException("Already closed");
         }
-        if (mParcelDescriptor != null) {
-            int fd = mParcelDescriptor.detachFd();
+        if (mWrapped != null) {
+            int fd = mWrapped.detachFd();
             mClosed = true;
+            mGuard.close();
             return fd;
         }
         int fd = getFd();
         mClosed = true;
+        mGuard.close();
         Parcel.clearFileDescriptor(mFileDescriptor);
         return fd;
     }
@@ -307,15 +316,16 @@
      * @throws IOException
      *             If an error occurs attempting to close this ParcelFileDescriptor.
      */
+    @Override
     public void close() throws IOException {
-        synchronized (this) {
-            if (mClosed) return;
-            mClosed = true;
-        }
-        if (mParcelDescriptor != null) {
+        if (mClosed) return;
+        mClosed = true;
+        mGuard.close();
+
+        if (mWrapped != null) {
             // If this is a proxy to another file descriptor, just call through to its
             // close method.
-            mParcelDescriptor.close();
+            mWrapped.close();
         } else {
             Parcel.closeFileDescriptor(mFileDescriptor);
         }
@@ -374,6 +384,9 @@
 
     @Override
     protected void finalize() throws Throwable {
+        if (mGuard != null) {
+            mGuard.warnIfOpen();
+        }
         try {
             if (!mClosed) {
                 close();
@@ -384,21 +397,22 @@
     }
 
     public ParcelFileDescriptor(ParcelFileDescriptor descriptor) {
-        super();
-        mParcelDescriptor = descriptor;
-        mFileDescriptor = mParcelDescriptor.mFileDescriptor;
+        mWrapped = descriptor;
+        mFileDescriptor = mWrapped.mFileDescriptor;
+        mGuard.open("close");
     }
 
-    /*package */ParcelFileDescriptor(FileDescriptor descriptor) {
-        super();
+    /** {@hide} */
+    public ParcelFileDescriptor(FileDescriptor descriptor) {
         if (descriptor == null) {
             throw new NullPointerException("descriptor must not be null");
         }
+        mWrapped = null;
         mFileDescriptor = descriptor;
-        mParcelDescriptor = null;
+        mGuard.open("close");
     }
 
-    /* Parcelable interface */
+    @Override
     public int describeContents() {
         return Parcelable.CONTENTS_FILE_DESCRIPTOR;
     }
@@ -408,6 +422,7 @@
      * If {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set in flags,
      * the file descriptor will be closed after a copy is written to the Parcel.
      */
+    @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeFileDescriptor(mFileDescriptor);
         if ((flags&PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) {
@@ -421,12 +436,14 @@
 
     public static final Parcelable.Creator<ParcelFileDescriptor> CREATOR
             = new Parcelable.Creator<ParcelFileDescriptor>() {
+        @Override
         public ParcelFileDescriptor createFromParcel(Parcel in) {
             return in.readFileDescriptor();
         }
+
+        @Override
         public ParcelFileDescriptor[] newArray(int size) {
             return new ParcelFileDescriptor[size];
         }
     };
-
 }
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index ed51818..0ca9183 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -44,6 +44,7 @@
     public static final long TRACE_TAG_AUDIO = 1L << 8;
     public static final long TRACE_TAG_VIDEO = 1L << 9;
     public static final long TRACE_TAG_CAMERA = 1L << 10;
+    private static final long TRACE_TAG_NOT_READY = 1L << 63;
 
     public static final int TRACE_FLAGS_START_BIT = 1;
     public static final String[] TRACE_TAGS = {
@@ -53,11 +54,8 @@
 
     public static final String PROPERTY_TRACE_TAG_ENABLEFLAGS = "debug.atrace.tags.enableflags";
 
-    // This works as a "not ready" flag because TRACE_TAG_ALWAYS is always set.
-    private static final long TRACE_FLAGS_NOT_READY = 0;
-
     // Must be volatile to avoid word tearing.
-    private static volatile long sEnabledTags = TRACE_FLAGS_NOT_READY;
+    private static volatile long sEnabledTags = TRACE_TAG_NOT_READY;
 
     private static native long nativeGetEnabledTags();
     private static native void nativeTraceCounter(long tag, String name, int value);
@@ -99,7 +97,7 @@
      */
     private static long cacheEnabledTags() {
         long tags = nativeGetEnabledTags();
-        if (tags == TRACE_FLAGS_NOT_READY) {
+        if (tags == TRACE_TAG_NOT_READY) {
             Log.w(TAG, "Unexpected value from nativeGetEnabledTags: " + tags);
             // keep going
         }
@@ -115,7 +113,7 @@
      */
     public static boolean isTagEnabled(long traceTag) {
         long tags = sEnabledTags;
-        if (tags == TRACE_FLAGS_NOT_READY) {
+        if (tags == TRACE_TAG_NOT_READY) {
             tags = cacheEnabledTags();
         }
         return (tags & traceTag) != 0;
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 1060bd8..bcce61d 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -607,6 +607,30 @@
     }
 
     /**
+     * Return given duration in a human-friendly format. For example, "4
+     * minutes" or "1 second". Returns only largest meaningful unit of time,
+     * from seconds up to hours.
+     *
+     * @hide
+     */
+    public static CharSequence formatDuration(long millis) {
+        final Resources res = Resources.getSystem();
+        if (millis >= HOUR_IN_MILLIS) {
+            final int hours = (int) ((millis + 1800000) / HOUR_IN_MILLIS);
+            return res.getQuantityString(
+                    com.android.internal.R.plurals.duration_hours, hours, hours);
+        } else if (millis >= MINUTE_IN_MILLIS) {
+            final int minutes = (int) ((millis + 30000) / MINUTE_IN_MILLIS);
+            return res.getQuantityString(
+                    com.android.internal.R.plurals.duration_minutes, minutes, minutes);
+        } else {
+            final int seconds = (int) ((millis + 500) / SECOND_IN_MILLIS);
+            return res.getQuantityString(
+                    com.android.internal.R.plurals.duration_seconds, seconds, seconds);
+        }
+    }
+
+    /**
      * Formats an elapsed time in the form "MM:SS" or "H:MM:SS"
      * for display on the call-in-progress screen.
      * @param elapsedSeconds the elapsed time in seconds.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ff44475..1747627 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -623,6 +623,7 @@
  * @attr ref android.R.styleable#View_hapticFeedbackEnabled
  * @attr ref android.R.styleable#View_keepScreenOn
  * @attr ref android.R.styleable#View_layerType
+ * @attr ref android.R.styleable#View_layoutDirection
  * @attr ref android.R.styleable#View_longClickable
  * @attr ref android.R.styleable#View_minHeight
  * @attr ref android.R.styleable#View_minWidth
@@ -660,6 +661,7 @@
  * @attr ref android.R.styleable#View_soundEffectsEnabled
  * @attr ref android.R.styleable#View_tag
  * @attr ref android.R.styleable#View_textAlignment
+ * @attr ref android.R.styleable#View_textDirection
  * @attr ref android.R.styleable#View_transformPivotX
  * @attr ref android.R.styleable#View_transformPivotY
  * @attr ref android.R.styleable#View_translationX
@@ -5854,6 +5856,7 @@
      *   {@link #LAYOUT_DIRECTION_RTL},
      *   {@link #LAYOUT_DIRECTION_INHERIT} or
      *   {@link #LAYOUT_DIRECTION_LOCALE}.
+     *
      * @attr ref android.R.styleable#View_layoutDirection
      *
      * @hide
@@ -5909,6 +5912,8 @@
      *
      * For compatibility, this will return {@link #LAYOUT_DIRECTION_LTR} if API version
      * is lower than {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}.
+     *
+     * @attr ref android.R.styleable#View_layoutDirection
      */
     @ViewDebug.ExportedProperty(category = "layout", mapping = {
         @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "RESOLVED_DIRECTION_LTR"),
@@ -16627,6 +16632,8 @@
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE}
      *
+     * @attr ref android.R.styleable#View_textDirection
+     *
      * @hide
      */
     @ViewDebug.ExportedProperty(category = "text", mapping = {
@@ -16656,6 +16663,8 @@
      * Resolution will be done if the value is set to TEXT_DIRECTION_INHERIT. The resolution
      * proceeds up the parent chain of the view to get the value. If there is no parent, then it will
      * return the default {@link #TEXT_DIRECTION_FIRST_STRONG}.
+     *
+     * @attr ref android.R.styleable#View_textDirection
      */
     public void setTextDirection(int textDirection) {
         if (getRawTextDirection() != textDirection) {
@@ -16684,6 +16693,8 @@
      * {@link #TEXT_DIRECTION_LTR},
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE}
+     *
+     * @attr ref android.R.styleable#View_textDirection
      */
     public int getTextDirection() {
         return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED_MASK) >> PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
@@ -16816,6 +16827,8 @@
      * {@link #TEXT_ALIGNMENT_VIEW_START},
      * {@link #TEXT_ALIGNMENT_VIEW_END}
      *
+     * @attr ref android.R.styleable#View_textAlignment
+     *
      * @hide
      */
     @ViewDebug.ExportedProperty(category = "text", mapping = {
@@ -16879,6 +16892,8 @@
      * {@link #TEXT_ALIGNMENT_TEXT_END},
      * {@link #TEXT_ALIGNMENT_VIEW_START},
      * {@link #TEXT_ALIGNMENT_VIEW_END}
+     *
+     * @attr ref android.R.styleable#View_textAlignment
      */
     @ViewDebug.ExportedProperty(category = "text", mapping = {
             @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"),
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b1a44c5..85972c3 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -291,6 +291,7 @@
         mErrorWasChanged = true;
 
         if (mError == null) {
+            setErrorIcon(null);
             if (mErrorPopup != null) {
                 if (mErrorPopup.isShowing()) {
                     mErrorPopup.dismiss();
@@ -299,21 +300,24 @@
                 mErrorPopup = null;
             }
 
-            setErrorIcon(null);
-        } else if (mTextView.isFocused()) {
-            showError();
+        } else {
             setErrorIcon(icon);
+            if (mTextView.isFocused()) {
+                showError();
+            }
         }
     }
 
     private void setErrorIcon(Drawable icon) {
-        final Drawables dr = mTextView.mDrawables;
-        if (dr != null) {
-            mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
-                    dr.mDrawableBottom);
-        } else {
-            mTextView.setCompoundDrawables(null, null, icon, null);
+        Drawables dr = mTextView.mDrawables;
+        if (dr == null) {
+            mTextView.mDrawables = dr = new Drawables();
         }
+        dr.setErrorDrawable(icon, mTextView);
+
+        mTextView.resetResolvedDrawables();
+        mTextView.invalidate();
+        mTextView.requestLayout();
     }
 
     private void hideError() {
@@ -321,15 +325,13 @@
             if (mErrorPopup.isShowing()) {
                 mErrorPopup.dismiss();
             }
-
-            setErrorIcon(null);
         }
 
         mShowErrorAfterAttach = false;
     }
 
     /**
-     * Returns the Y offset to make the pointy top of the error point
+     * Returns the X offset to make the pointy top of the error point
      * at the middle of the error icon.
      */
     private int getErrorX() {
@@ -340,8 +342,23 @@
         final float scale = mTextView.getResources().getDisplayMetrics().density;
 
         final Drawables dr = mTextView.mDrawables;
-        return mTextView.getWidth() - mErrorPopup.getWidth() - mTextView.getPaddingRight() -
-                (dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f);
+
+        final int layoutDirection = mTextView.getLayoutDirection();
+        int errorX;
+        int offset;
+        switch (layoutDirection) {
+            default:
+            case View.LAYOUT_DIRECTION_LTR:
+                offset = - (dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f);
+                errorX = mTextView.getWidth() - mErrorPopup.getWidth() -
+                        mTextView.getPaddingRight() + offset;
+                break;
+            case View.LAYOUT_DIRECTION_RTL:
+                offset = (dr != null ? dr.mDrawableSizeLeft : 0) / 2 - (int) (25 * scale + 0.5f);
+                errorX = mTextView.getPaddingLeft() + offset;
+                break;
+        }
+        return errorX;
     }
 
     /**
@@ -358,16 +375,27 @@
                 mTextView.getCompoundPaddingBottom() - compoundPaddingTop;
 
         final Drawables dr = mTextView.mDrawables;
-        int icontop = compoundPaddingTop +
-                (vspace - (dr != null ? dr.mDrawableHeightRight : 0)) / 2;
+
+        final int layoutDirection = mTextView.getLayoutDirection();
+        int height;
+        switch (layoutDirection) {
+            default:
+            case View.LAYOUT_DIRECTION_LTR:
+                height = (dr != null ? dr.mDrawableHeightRight : 0);
+                break;
+            case View.LAYOUT_DIRECTION_RTL:
+                height = (dr != null ? dr.mDrawableHeightLeft : 0);
+                break;
+        }
+
+        int icontop = compoundPaddingTop + (vspace - height) / 2;
 
         /*
          * The "2" is the distance between the point and the top edge
          * of the background.
          */
         final float scale = mTextView.getResources().getDisplayMetrics().density;
-        return icontop + (dr != null ? dr.mDrawableHeightRight : 0) - mTextView.getHeight() -
-                (int) (2 * scale + 0.5f);
+        return icontop + height - mTextView.getHeight() - (int) (2 * scale + 0.5f);
     }
 
     void createInputContentTypeIfNeeded() {
@@ -3726,7 +3754,7 @@
             super(v, width, height);
             mView = v;
             // Make sure the TextView has a background set as it will be used the first time it is
-            // shown and positionned. Initialized with below background, which should have
+            // shown and positioned. Initialized with below background, which should have
             // dimensions identical to the above version for this to work (and is more likely).
             mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId,
                     com.android.internal.R.styleable.Theme_errorMessageBackground);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5d90400..0a16a66 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -284,15 +284,144 @@
     private TextUtils.TruncateAt mEllipsize;
 
     static class Drawables {
+        final static int DRAWABLE_NONE = -1;
+        final static int DRAWABLE_RIGHT = 0;
+        final static int DRAWABLE_LEFT = 1;
+
         final Rect mCompoundRect = new Rect();
+
         Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight,
-                mDrawableStart, mDrawableEnd;
+                mDrawableStart, mDrawableEnd, mDrawableError, mDrawableTemp;
+
         int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight,
-                mDrawableSizeStart, mDrawableSizeEnd;
+                mDrawableSizeStart, mDrawableSizeEnd, mDrawableSizeError, mDrawableSizeTemp;
+
         int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight,
-                mDrawableHeightStart, mDrawableHeightEnd;
+                mDrawableHeightStart, mDrawableHeightEnd, mDrawableHeightError, mDrawableHeightTemp;
+
         int mDrawablePadding;
+
+        int mDrawableSaved = DRAWABLE_NONE;
+
+        public void resolveWithLayoutDirection(int layoutDirection) {
+            switch(layoutDirection) {
+                case LAYOUT_DIRECTION_RTL:
+                    if (mDrawableStart != null) {
+                        mDrawableRight = mDrawableStart;
+
+                        mDrawableSizeRight = mDrawableSizeStart;
+                        mDrawableHeightRight = mDrawableHeightStart;
+                    }
+                    if (mDrawableEnd != null) {
+                        mDrawableLeft = mDrawableEnd;
+
+                        mDrawableSizeLeft = mDrawableSizeEnd;
+                        mDrawableHeightLeft = mDrawableHeightEnd;
+                    }
+                    break;
+
+                case LAYOUT_DIRECTION_LTR:
+                default:
+                    if (mDrawableStart != null) {
+                        mDrawableLeft = mDrawableStart;
+
+                        mDrawableSizeLeft = mDrawableSizeStart;
+                        mDrawableHeightLeft = mDrawableHeightStart;
+                    }
+                    if (mDrawableEnd != null) {
+                        mDrawableRight = mDrawableEnd;
+
+                        mDrawableSizeRight = mDrawableSizeEnd;
+                        mDrawableHeightRight = mDrawableHeightEnd;
+                    }
+                    break;
+            }
+            applyErrorDrawableIfNeeded(layoutDirection);
+            updateDrawablesLayoutDirection(layoutDirection);
+        }
+
+        private void updateDrawablesLayoutDirection(int layoutDirection) {
+            if (mDrawableLeft != null) {
+                mDrawableLeft.setLayoutDirection(layoutDirection);
+            }
+            if (mDrawableRight != null) {
+                mDrawableRight.setLayoutDirection(layoutDirection);
+            }
+            if (mDrawableTop != null) {
+                mDrawableTop.setLayoutDirection(layoutDirection);
+            }
+            if (mDrawableBottom != null) {
+                mDrawableBottom.setLayoutDirection(layoutDirection);
+            }
+        }
+
+        public void setErrorDrawable(Drawable dr, TextView tv) {
+            if (mDrawableError != dr && mDrawableError != null) {
+                mDrawableError.setCallback(null);
+            }
+            mDrawableError = dr;
+
+            final Rect compoundRect = mCompoundRect;
+            int[] state = tv.getDrawableState();
+
+            if (mDrawableError != null) {
+                mDrawableError.setState(state);
+                mDrawableError.copyBounds(compoundRect);
+                mDrawableError.setCallback(tv);
+                mDrawableSizeError = compoundRect.width();
+                mDrawableHeightError = compoundRect.height();
+            } else {
+                mDrawableSizeError = mDrawableHeightError = 0;
+            }
+        }
+
+        private void applyErrorDrawableIfNeeded(int layoutDirection) {
+            // first restore the initial state if needed
+            switch (mDrawableSaved) {
+                case DRAWABLE_LEFT:
+                    mDrawableLeft = mDrawableTemp;
+                    mDrawableSizeLeft = mDrawableSizeTemp;
+                    mDrawableHeightLeft = mDrawableHeightTemp;
+                    break;
+                case DRAWABLE_RIGHT:
+                    mDrawableRight = mDrawableTemp;
+                    mDrawableSizeRight = mDrawableSizeTemp;
+                    mDrawableHeightRight = mDrawableHeightTemp;
+                    break;
+                case DRAWABLE_NONE:
+                default:
+            }
+            // then, if needed, assign the Error drawable to the correct location
+            if (mDrawableError != null) {
+                switch(layoutDirection) {
+                    case LAYOUT_DIRECTION_RTL:
+                        mDrawableSaved = DRAWABLE_LEFT;
+
+                        mDrawableTemp = mDrawableLeft;
+                        mDrawableSizeTemp = mDrawableSizeLeft;
+                        mDrawableHeightTemp = mDrawableHeightLeft;
+
+                        mDrawableLeft = mDrawableError;
+                        mDrawableSizeLeft = mDrawableSizeError;
+                        mDrawableHeightLeft = mDrawableHeightError;
+                        break;
+                    case LAYOUT_DIRECTION_LTR:
+                    default:
+                        mDrawableSaved = DRAWABLE_RIGHT;
+
+                        mDrawableTemp = mDrawableRight;
+                        mDrawableSizeTemp = mDrawableSizeRight;
+                        mDrawableHeightTemp = mDrawableHeightRight;
+
+                        mDrawableRight = mDrawableError;
+                        mDrawableSizeRight = mDrawableSizeError;
+                        mDrawableHeightRight = mDrawableHeightError;
+                        break;
+                }
+            }
+        }
     }
+
     Drawables mDrawables;
 
     private CharWrapper mCharWrapper;
@@ -8229,9 +8358,8 @@
 
     TextDirectionHeuristic getTextDirectionHeuristic() {
         if (hasPasswordTransformationMethod()) {
-            // TODO: take care of the content direction to show the password text and dots justified
-            // to the left or to the right
-            return TextDirectionHeuristics.LOCALE;
+            // passwords fields should be LTR
+            return TextDirectionHeuristics.LTR;
         }
 
         // Always need to resolve layout direction first
@@ -8264,63 +8392,10 @@
             return;
         }
         mLastLayoutDirection = layoutDirection;
-        // No drawable to resolve
-        if (mDrawables == null) {
-            return;
-        }
-        // No relative drawable to resolve
-        if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) {
-            return;
-        }
 
-        Drawables dr = mDrawables;
-        switch(layoutDirection) {
-            case LAYOUT_DIRECTION_RTL:
-                if (dr.mDrawableStart != null) {
-                    dr.mDrawableRight = dr.mDrawableStart;
-
-                    dr.mDrawableSizeRight = dr.mDrawableSizeStart;
-                    dr.mDrawableHeightRight = dr.mDrawableHeightStart;
-                }
-                if (dr.mDrawableEnd != null) {
-                    dr.mDrawableLeft = dr.mDrawableEnd;
-
-                    dr.mDrawableSizeLeft = dr.mDrawableSizeEnd;
-                    dr.mDrawableHeightLeft = dr.mDrawableHeightEnd;
-                }
-                break;
-
-            case LAYOUT_DIRECTION_LTR:
-            default:
-                if (dr.mDrawableStart != null) {
-                    dr.mDrawableLeft = dr.mDrawableStart;
-
-                    dr.mDrawableSizeLeft = dr.mDrawableSizeStart;
-                    dr.mDrawableHeightLeft = dr.mDrawableHeightStart;
-                }
-                if (dr.mDrawableEnd != null) {
-                    dr.mDrawableRight = dr.mDrawableEnd;
-
-                    dr.mDrawableSizeRight = dr.mDrawableSizeEnd;
-                    dr.mDrawableHeightRight = dr.mDrawableHeightEnd;
-                }
-                break;
-        }
-        updateDrawablesLayoutDirection(dr, layoutDirection);
-    }
-
-    private void updateDrawablesLayoutDirection(Drawables dr, int layoutDirection) {
-        if (dr.mDrawableLeft != null) {
-            dr.mDrawableLeft.setLayoutDirection(layoutDirection);
-        }
-        if (dr.mDrawableRight != null) {
-            dr.mDrawableRight.setLayoutDirection(layoutDirection);
-        }
-        if (dr.mDrawableTop != null) {
-            dr.mDrawableTop.setLayoutDirection(layoutDirection);
-        }
-        if (dr.mDrawableBottom != null) {
-            dr.mDrawableBottom.setLayoutDirection(layoutDirection);
+        // Resolve drawables
+        if (mDrawables != null) {
+            mDrawables.resolveWithLayoutDirection(layoutDirection);
         }
     }
 
@@ -8328,6 +8403,7 @@
      * @hide
      */
     protected void resetResolvedDrawables() {
+        super.resetResolvedDrawables();
         mLastLayoutDirection = -1;
     }
 
diff --git a/core/java/com/android/internal/annotations/GuardedBy.java b/core/java/com/android/internal/annotations/GuardedBy.java
new file mode 100644
index 0000000..fc61945
--- /dev/null
+++ b/core/java/com/android/internal/annotations/GuardedBy.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation type used to mark a method or field that can only be accessed when
+ * holding the referenced lock.
+ */
+@Target({ ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.CLASS)
+public @interface GuardedBy {
+    String value();
+}
diff --git a/core/java/com/android/internal/annotations/Immutable.java b/core/java/com/android/internal/annotations/Immutable.java
new file mode 100644
index 0000000..b424275
--- /dev/null
+++ b/core/java/com/android/internal/annotations/Immutable.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation type used to mark a class which is immutable.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface Immutable {
+}
diff --git a/core/java/com/android/internal/annotations/VisibleForTesting.java b/core/java/com/android/internal/annotations/VisibleForTesting.java
new file mode 100644
index 0000000..bc3121c
--- /dev/null
+++ b/core/java/com/android/internal/annotations/VisibleForTesting.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes that the class, method or field has its visibility relaxed so
+ * that unit tests can access it.
+ * <p/>
+ * The <code>visibility</code> argument can be used to specific what the original
+ * visibility should have been if it had not been made public or package-private for testing.
+ * The default is to consider the element private.
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface VisibleForTesting {
+    /**
+     * Intended visibility if the element had not been made public or package-private for
+     * testing.
+     */
+    enum Visibility {
+        /** The element should be considered protected. */
+        PROTECTED,
+        /** The element should be considered package-private. */
+        PACKAGE,
+        /** The element should be considered private. */
+        PRIVATE
+    }
+
+    /**
+     * Intended visibility if the element had not been made public or package-private for testing.
+     * If not specified, one should assume the element originally intended to be private.
+     */
+    Visibility visibility() default Visibility.PRIVATE;
+}
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index cfb16fa..b63ad62 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -38,6 +38,7 @@
     void deleteHost(int hostId);
     void deleteAllHosts();
     RemoteViews getAppWidgetViews(int appWidgetId);
+    int[] getAppWidgetIdsForHost(int hostId);
 
     //
     // for AppWidgetManager
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 8b222f0..c517a68 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -25,6 +25,7 @@
 import android.os.StrictMode;
 import android.os.SystemClock;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ProcFileReader;
 
 import java.io.File;
@@ -53,7 +54,7 @@
         this(new File("/proc/"));
     }
 
-    // @VisibleForTesting
+    @VisibleForTesting
     public NetworkStatsFactory(File procRoot) {
         mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
         mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index e7c4c23..9537ac4 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -122,9 +122,9 @@
     return (jboolean)(::wifi_start_supplicant(p2pSupported) == 0);
 }
 
-static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject)
+static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject, jboolean p2pSupported)
 {
-    return (jboolean)(::wifi_stop_supplicant() == 0);
+    return (jboolean)(::wifi_stop_supplicant(p2pSupported) == 0);
 }
 
 static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject, jstring jIface)
@@ -204,7 +204,7 @@
     { "isDriverLoaded", "()Z",  (void *)android_net_wifi_isDriverLoaded },
     { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
     { "startSupplicant", "(Z)Z",  (void *)android_net_wifi_startSupplicant },
-    { "killSupplicant", "()Z",  (void *)android_net_wifi_killSupplicant },
+    { "killSupplicant", "(Z)Z",  (void *)android_net_wifi_killSupplicant },
     { "connectToSupplicant", "(Ljava/lang/String;)Z",
             (void *)android_net_wifi_connectToSupplicant },
     { "closeSupplicantConnection", "(Ljava/lang/String;)V",
diff --git a/core/res/res/drawable-hdpi/ic_coins_l.png b/core/res/res/drawable-hdpi/ic_coins_l.png
new file mode 100644
index 0000000..e1e3e2a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_coins_l.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error.9.png
new file mode 100644
index 0000000..8b43f4e
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above.9.png
new file mode 100644
index 0000000..20e9002
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_dark.9.png
new file mode 100644
index 0000000..b5f397c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_light.9.png
new file mode 100644
index 0000000..a04d695
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_dark.9.png
new file mode 100644
index 0000000..8567b1f
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_light.9.png
new file mode 100644
index 0000000..7d1754c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-ldpi/popup_inline_error.9.png b/core/res/res/drawable-ldrtl-ldpi/popup_inline_error.9.png
new file mode 100644
index 0000000..d2efb62
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-ldpi/popup_inline_error.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-ldpi/popup_inline_error_above.9.png b/core/res/res/drawable-ldrtl-ldpi/popup_inline_error_above.9.png
new file mode 100644
index 0000000..04d200d
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-ldpi/popup_inline_error_above.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error.9.png
new file mode 100644
index 0000000..27e8d4f
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above.9.png
new file mode 100644
index 0000000..4ae2b91
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_dark.9.png
new file mode 100644
index 0000000..8cc3b69
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_light.9.png
new file mode 100644
index 0000000..7a84200
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_dark.9.png
new file mode 100644
index 0000000..8fc2e2e
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_light.9.png
new file mode 100644
index 0000000..687a691
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error.9.png
new file mode 100644
index 0000000..db91a56
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above.9.png
new file mode 100644
index 0000000..90820b5
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_dark.9.png
new file mode 100644
index 0000000..5989975
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_light.9.png
new file mode 100644
index 0000000..3b3f87d3
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_dark.9.png
new file mode 100644
index 0000000..75baba2
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_light.9.png
new file mode 100644
index 0000000..6c0203d
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_coins_l.png b/core/res/res/drawable-mdpi/ic_coins_l.png
new file mode 100644
index 0000000..a6d7abb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_coins_l.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_coins_l.png b/core/res/res/drawable-xhdpi/ic_coins_l.png
new file mode 100644
index 0000000..84e7e723
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_coins_l.png
Binary files differ
diff --git a/core/res/res/layout/keyguard_pin_view.xml b/core/res/res/layout/keyguard_pin_view.xml
index e494b69..6a3b9e6 100644
--- a/core/res/res/layout/keyguard_pin_view.xml
+++ b/core/res/res/layout/keyguard_pin_view.xml
@@ -39,6 +39,7 @@
        android:layout_height="0dp"
        android:orientation="vertical"
        android:layout_weight="1"
+       android:layoutDirection="ltr"
        >
        <LinearLayout
           android:layout_width="match_parent"
diff --git a/core/res/res/layout/keyguard_sim_pin_view.xml b/core/res/res/layout/keyguard_sim_pin_view.xml
index 026b025..6e6fe08 100644
--- a/core/res/res/layout/keyguard_sim_pin_view.xml
+++ b/core/res/res/layout/keyguard_sim_pin_view.xml
@@ -44,6 +44,7 @@
        android:layout_height="0dp"
        android:orientation="vertical"
        android:layout_weight="1"
+       android:layoutDirection="ltr"
        >
        <LinearLayout
            android:layout_width="match_parent"
diff --git a/core/res/res/layout/keyguard_sim_puk_view.xml b/core/res/res/layout/keyguard_sim_puk_view.xml
index 28a9f9a..0412fdc 100644
--- a/core/res/res/layout/keyguard_sim_puk_view.xml
+++ b/core/res/res/layout/keyguard_sim_puk_view.xml
@@ -45,6 +45,7 @@
        android:layout_height="0dp"
        android:orientation="vertical"
        android:layout_weight="1"
+       android:layoutDirection="ltr"
        >
        <LinearLayout
            android:layout_width="match_parent"
diff --git a/core/res/res/layout/sms_short_code_confirmation_dialog.xml b/core/res/res/layout/sms_short_code_confirmation_dialog.xml
index ec39d97..d82f560 100644
--- a/core/res/res/layout/sms_short_code_confirmation_dialog.xml
+++ b/core/res/res/layout/sms_short_code_confirmation_dialog.xml
@@ -30,9 +30,9 @@
         style="?android:attr/textAppearanceMedium"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingLeft="16dip"
-        android:paddingRight="16dip"
-        android:paddingTop="8dip"
+        android:paddingLeft="20dip"
+        android:paddingRight="20dip"
+        android:paddingTop="16dip"
         android:paddingBottom="16dip" />
 
     <TableLayout android:id="@+id/sms_short_code_detail_layout"
@@ -51,7 +51,7 @@
                 android:layout_height="wrap_content"
                 android:paddingLeft="8dip"
                 android:paddingRight="8dip"
-                android:src="@null" />
+                android:src="@drawable/ic_coins_l" />
             <TextView android:id="@+id/sms_short_code_detail_message"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content" />
@@ -60,14 +60,19 @@
         <TableRow
             android:layout_width="wrap_content"
             android:layout_height="wrap_content" >
-
-            <CheckBox android:id="@+id/sms_short_code_remember_choice_checkbox"
-                android:layout_width="wrap_content"
+            <RelativeLayout android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:paddingRight="8dip" />
+                android:paddingTop="12dip"
+                android:paddingLeft="8dip" >
+            <CheckBox android:id="@+id/sms_short_code_remember_choice_checkbox"
+                android:paddingTop="11dip"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+            </RelativeLayout>
             <TextView android:id="@+id/sms_short_code_remember_choice_text"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:paddingTop="18dip"
                 android:text="@string/sms_short_code_remember_choice" />
         </TableRow>
 
@@ -77,6 +82,7 @@
 
             <Space android:layout_gravity="fill" />
             <TextView android:id="@+id/sms_short_code_remember_undo_instruction"
+                android:paddingTop="10dip"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content" />
         </TableRow>
diff --git a/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png b/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png
new file mode 100644
index 0000000..e3f3144
--- /dev/null
+++ b/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 51d23e8..e71f3c5 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"weke"</string>
     <string name="year" msgid="4001118221013892076">"jaar"</string>
     <string name="years" msgid="6881577717993213522">"jaar"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Videoprobleem"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Hierdie video is nie geldig vir stroming na hierdie toestel nie."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Kan nie hierdie video speel nie."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index f846ffd..11c11e7 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"ሳምንቶች"</string>
     <string name="year" msgid="4001118221013892076">"ዓመት"</string>
     <string name="years" msgid="6881577717993213522">"ዓመታት"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"የቪዲዮ ችግር"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"ይቅርታ፣ ይህ ቪዲዮ በዚህ መሣሪያ ለመልቀቅ ትክክል አይደለም።"</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"ይሄን ቪዲዮ ማጫወት አልተቻለም።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index a7c0c50..e78d15b 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"أسابيع"</string>
     <string name="year" msgid="4001118221013892076">"سنة"</string>
     <string name="years" msgid="6881577717993213522">"أعوام"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"مشكلة في الفيديو"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"عذرًا، هذا الفيديو غير صالح للبث على هذا الجهاز."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"لا يمكنك تشغيل هذا الفيديو."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 6ae68f9..34c98bb 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"тыд."</string>
     <string name="year" msgid="4001118221013892076">"год"</string>
     <string name="years" msgid="6881577717993213522">"г."</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Праблема з відэа"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Відэа не падыходзіць для патокавай перадачы на ​​гэту прыладу."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Немагчыма прайграць гэта відэа."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 838f0cf..1df2a788 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"седмици"</string>
     <string name="year" msgid="4001118221013892076">"година"</string>
     <string name="years" msgid="6881577717993213522">"години"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Проблем с видеоклипа"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Този видеоклип не е валиден за поточно предаване към това устройство."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Този видеоклип не може да се пусне."</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index fdc9506..20ae9dd 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"setmanes"</string>
     <string name="year" msgid="4001118221013892076">"any"</string>
     <string name="years" msgid="6881577717993213522">"anys"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Problema amb el vídeo"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Aquest vídeo no és vàlid per a la reproducció en aquest dispositiu."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"No es pot reproduir aquest vídeo."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 0afc74f..53003f9 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"týd."</string>
     <string name="year" msgid="4001118221013892076">"rokem"</string>
     <string name="years" msgid="6881577717993213522">"lety"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Potíže s videem"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Toto video nelze přenášet datovým proudem do tohoto zařízení."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Toto video nelze přehrát."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index b0fcf8b..61b4e14 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"uger"</string>
     <string name="year" msgid="4001118221013892076">"år"</string>
     <string name="years" msgid="6881577717993213522">"år"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Videoproblem"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Denne video kan ikke streames på denne enhed."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Videoen kan ikke afspilles."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 631537e..6355f8e 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"Wochen"</string>
     <string name="year" msgid="4001118221013892076">"Jahr"</string>
     <string name="years" msgid="6881577717993213522">"Jahre"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Videoprobleme"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Dieses Video ist nicht für Streaming auf diesem Gerät gültig."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Video kann nicht wiedergegeben werden."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 069c5d6..9b350159 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"εβδομάδες"</string>
     <string name="year" msgid="4001118221013892076">"έτος"</string>
     <string name="years" msgid="6881577717993213522">"έτη"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Πρόβλημα με το βίντεο"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Αυτό το βίντεο δεν είναι έγκυρο για ροή σε αυτή τη συσκευή."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Δεν μπορείτε να αναπαράγετε αυτό το βίντεο."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 888e42e..5ceae63 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -993,6 +993,18 @@
     <string name="weeks" msgid="6509623834583944518">"weeks"</string>
     <string name="year" msgid="4001118221013892076">"year"</string>
     <string name="years" msgid="6881577717993213522">"years"</string>
+  <plurals name="duration_seconds">
+    <item quantity="one" msgid="6962015528372969481">"1 second"</item>
+    <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> seconds"</item>
+  </plurals>
+  <plurals name="duration_minutes">
+    <item quantity="one" msgid="4915414002546085617">"1 minute"</item>
+    <item quantity="other" msgid="3165187169224908775">"<xliff:g id="COUNT">%d</xliff:g> minutes"</item>
+  </plurals>
+  <plurals name="duration_hours">
+    <item quantity="one" msgid="8917467491248809972">"1 hour"</item>
+    <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> hours"</item>
+  </plurals>
     <string name="VideoView_error_title" msgid="3534509135438353077">"Video problem"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"This video isn\'t valid for streaming to this device."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Can\'t play this video."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 47d436d..a25239b 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"semanas"</string>
     <string name="year" msgid="4001118221013892076">"año"</string>
     <string name="years" msgid="6881577717993213522">"años"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Problemas de video"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"No es posible transmitir este video al dispositivo."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"No se puede reproducir el video."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index c129483..fb8548d 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"semanas"</string>
     <string name="year" msgid="4001118221013892076">"año"</string>
     <string name="years" msgid="6881577717993213522">"años"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Incidencias con el vídeo"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Este vídeo no se puede transmitir al dispositivo."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"No se puede reproducir el vídeo."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 5fb21d4..0aa10f6 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"nädalat"</string>
     <string name="year" msgid="4001118221013892076">"aasta"</string>
     <string name="years" msgid="6881577717993213522">"aastat"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Probleem videoga"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"See video ei sobi voogesituseks selles seadmes."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Videot ei saa esitada."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index d5e624d0..95d3bfa 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"هفته"</string>
     <string name="year" msgid="4001118221013892076">"سال"</string>
     <string name="years" msgid="6881577717993213522">"سال"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"مشکل در ویدئو"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"متأسفیم، این ویدئو برای پخش جریانی با این دستگاه معتبر نیست."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"پخش این ویدئو ممکن نیست."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 2b08bea..7a2eb8a 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"viikkoa"</string>
     <string name="year" msgid="4001118221013892076">"vuosi"</string>
     <string name="years" msgid="6881577717993213522">"vuotta"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Video-ongelma"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Tätä videota ei voi suoratoistaa tällä laitteella."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Videota ei voida toistaa."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 479fe18..5bcfd7e 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"semaines"</string>
     <string name="year" msgid="4001118221013892076">"année"</string>
     <string name="years" msgid="6881577717993213522">"années"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Problème vidéo"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Impossible de lire cette vidéo en streaming sur cet appareil."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Impossible de lire la vidéo."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 65aa563..ad4eec5 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"सप्ताह"</string>
     <string name="year" msgid="4001118221013892076">"वर्ष"</string>
     <string name="years" msgid="6881577717993213522">"वर्ष"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"वीडियो समस्‍याएं"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"यह वीडियो इस उपकरण पर स्ट्रीमिंग के लिए मान्‍य नहीं है."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"यह वीडियो नहीं चलाया जा सकता."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index e279216..73e6d32 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"tjedna"</string>
     <string name="year" msgid="4001118221013892076">"godina"</string>
     <string name="years" msgid="6881577717993213522">"godina"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Problem s videozapisom"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Ovaj videozapis nije valjan za streaming na ovaj uređaj."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Ovaj videozapis nije moguće reproducirati."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 88f4046..8935319 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"hét"</string>
     <string name="year" msgid="4001118221013892076">"év"</string>
     <string name="years" msgid="6881577717993213522">"év"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Videoprobléma"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Ezt a videót nem lehet megjeleníteni ezen az eszközön."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Nem lehet lejátszani ezt a videót."</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index b5dfcd5..aafb275 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"minggu"</string>
     <string name="year" msgid="4001118221013892076">"tahun"</string>
     <string name="years" msgid="6881577717993213522">"tahun"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Masalah video"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Video ini tidak valid untuk pengaliran ke perangkat ini."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Tidak dapat memutar video ini."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 0edb0c1..022b6d9 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"settimane"</string>
     <string name="year" msgid="4001118221013892076">"anno"</string>
     <string name="years" msgid="6881577717993213522">"anni"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Problemi video"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Questo video non è valido per lo streaming su questo dispositivo."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Impossibile riprodurre il video."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index bb6a3ac..89ecf6aa 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"שבועות"</string>
     <string name="year" msgid="4001118221013892076">"שנה"</string>
     <string name="years" msgid="6881577717993213522">"שנים"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"בעיה בווידאו"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"סרטון זה אינו חוקי להעברה כמדיה זורמת למכשיר זה."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"לא ניתן להפעיל סרטון זה."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 8af0fed..02126f0 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"週間"</string>
     <string name="year" msgid="4001118221013892076">"年"</string>
     <string name="years" msgid="6881577717993213522">"年"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"動画の問題"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"この動画はこの端末にストリーミングできません。"</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"この動画を再生できません。"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 37c6b01..7dc7253 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"주"</string>
     <string name="year" msgid="4001118221013892076">"년"</string>
     <string name="years" msgid="6881577717993213522">"년"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"영상 문제"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"이 기기로 스트리밍하기에 적합하지 않은 동영상입니다."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"동영상을 재생할 수 없습니다."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index f2ad504..2528e94 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"sav."</string>
     <string name="year" msgid="4001118221013892076">"metai"</string>
     <string name="years" msgid="6881577717993213522">"metai"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Vaizdo įrašo problema"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Šis vaizdo įrašas netinkamas srautiniu būdu perduoti į šį įrenginį."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Negalima paleisti šio vaizdo įrašo."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index ee0b023..b602d16 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"nedēļas"</string>
     <string name="year" msgid="4001118221013892076">"gads"</string>
     <string name="years" msgid="6881577717993213522">"gadi"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Video problēma"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Šis video nav derīgs straumēšanai uz šo ierīci."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Nevar atskaņot šo video."</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index e89f70f..75a03f0 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"minggu"</string>
     <string name="year" msgid="4001118221013892076">"tahun"</string>
     <string name="years" msgid="6881577717993213522">"tahun"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Masalah video"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Maaf, video ini tidak sah untuk penstriman ke peranti ini."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Tidak dapat mainkan video ini."</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 42df589..3b22779 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"uker"</string>
     <string name="year" msgid="4001118221013892076">"år"</string>
     <string name="years" msgid="6881577717993213522">"år"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Videoproblem"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Denne videoen er ikke gyldig for direkteavspilling på enheten."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Kan ikke spille av denne videoen."</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 21fe1cc..8fcaee2 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"weken"</string>
     <string name="year" msgid="4001118221013892076">"jaar"</string>
     <string name="years" msgid="6881577717993213522">"jaren"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Probleem met video"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Deze video kan niet worden gestreamd naar dit apparaat."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Deze video kan niet worden afgespeeld."</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index d0f1db3..3a9a0f6 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"tygodni"</string>
     <string name="year" msgid="4001118221013892076">"rok"</string>
     <string name="years" msgid="6881577717993213522">"lat"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Problem z filmem"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Ten film nie nadaje się do strumieniowego przesyłania do tego urządzenia."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Nie można odtworzyć tego filmu."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index fd7211e..0587d16 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"semanas"</string>
     <string name="year" msgid="4001118221013892076">"ano"</string>
     <string name="years" msgid="6881577717993213522">"anos"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Problema com o vídeo"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Este vídeo não é válido para transmissão em fluxo contínuo neste aparelho."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Não é possível reproduzir este vídeo."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index ed656fe..a450f8e 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"semanas"</string>
     <string name="year" msgid="4001118221013892076">"ano"</string>
     <string name="years" msgid="6881577717993213522">"anos"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Problema com o vídeo"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Este vídeo não é válido para transmissão neste dispositivo."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Não é possível reproduzir este vídeo."</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index 0e7aaec..d3efd7e 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -1558,6 +1558,12 @@
     <string name="weeks" msgid="6509623834583944518">"emnas"</string>
     <string name="year" msgid="4001118221013892076">"onn"</string>
     <string name="years" msgid="6881577717993213522">"onns"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <!-- no translation found for VideoView_error_title (3534509135438353077) -->
     <skip />
     <!-- no translation found for VideoView_error_text_invalid_progressive_playback (3186670335938670444) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index f274acd..d0ef774 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"săptămâni"</string>
     <string name="year" msgid="4001118221013892076">"an"</string>
     <string name="years" msgid="6881577717993213522">"ani"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Problemă video"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Acest fişier video nu este valid pentru a fi transmis în flux către acest dispozitiv."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Nu puteţi reda acest videoclip"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 1172ef7..ae7d5da 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"нед."</string>
     <string name="year" msgid="4001118221013892076">"г."</string>
     <string name="years" msgid="6881577717993213522">"г."</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Ошибка"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Это видео не предназначено для потокового воспроизведения на данном устройстве."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Не удалось воспроизвести видео."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index c364380..12a3b8f 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"týždne"</string>
     <string name="year" msgid="4001118221013892076">"rok"</string>
     <string name="years" msgid="6881577717993213522">"roky"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Problém s videom"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Je nám ľúto, ale toto video sa nedá streamovať do tohto zariadenia."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Toto video nie je možné prehrať."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 7f94c204..742a96a 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"tednov"</string>
     <string name="year" msgid="4001118221013892076">"leto"</string>
     <string name="years" msgid="6881577717993213522">"let"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Težava z videoposnetkom"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Ta videoposnetek ni veljaven za pretakanje v to napravo."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Tega videoposnetka ni mogoče predvajati."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 5a94aad..53604f1 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"недеље(а)"</string>
     <string name="year" msgid="4001118221013892076">"година"</string>
     <string name="years" msgid="6881577717993213522">"годинe(а)"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Проблем са видео снимком"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Овај видео не може да се стримује на овом уређају."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Не можете да пустите овај видео."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 2737d62..48bd352 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"veckor"</string>
     <string name="year" msgid="4001118221013892076">"år"</string>
     <string name="years" msgid="6881577717993213522">"år"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Videoproblem"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Videon kan tyvärr inte spelas upp i den här enheten."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Det går inte att spela upp videon."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 5fc2a13..7e1cb67 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"wiki"</string>
     <string name="year" msgid="4001118221013892076">"mwaka"</string>
     <string name="years" msgid="6881577717993213522">"miaka"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Shida ya video"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Video hii si halali kutiririshwa kwa kifaa hiki."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Haiwezi kucheza video hii."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 0a86a86..d26b2d5 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"สัปดาห์"</string>
     <string name="year" msgid="4001118221013892076">"ปี"</string>
     <string name="years" msgid="6881577717993213522">" ปี"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"ปัญหาเกี่ยวกับวิดีโอ"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"วิดีโอนี้ไม่สามารถสตรีมไปยังอุปกรณ์นี้"</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"ไม่สามารถเล่นวิดีโอนี้"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 072f6df..485031d 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"mga linggo"</string>
     <string name="year" msgid="4001118221013892076">"taon"</string>
     <string name="years" msgid="6881577717993213522">"mga taon"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Problema sa video"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Hindi wasto ang video na ito para sa streaming sa device na ito."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Hindi ma-play ang video na ito."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index dbb7b0d..afea527 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"hafta"</string>
     <string name="year" msgid="4001118221013892076">"yıl"</string>
     <string name="years" msgid="6881577717993213522">"yıl"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Video sorunu"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Bu video bu cihazda akış için uygun değil."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Bu video oynatılamıyor."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 6512007..171d8ca 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"тижн."</string>
     <string name="year" msgid="4001118221013892076">"рік"</string>
     <string name="years" msgid="6881577717993213522">"р."</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Проблема з відео"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Відео не придатне для потокового передавання в цей пристрій."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Неможливо відтворити це відео."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 7fb3412..e343ae7 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"tuần"</string>
     <string name="year" msgid="4001118221013892076">"năm"</string>
     <string name="years" msgid="6881577717993213522">"năm"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Sự cố video"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Video này không hợp lệ để phát trực tuyến đến thiết bị này."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Không thể phát video này."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 251389b..dee48e7 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"周"</string>
     <string name="year" msgid="4001118221013892076">"年"</string>
     <string name="years" msgid="6881577717993213522">"年"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"视频问题"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"抱歉,该视频不适合在此设备上播放。"</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"无法播放此视频。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 473b9d0..a1286f1 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"週"</string>
     <string name="year" msgid="4001118221013892076">"年"</string>
     <string name="years" msgid="6881577717993213522">"年"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"影片發生問題"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"這部影片的格式無效,因此無法在此裝置中串流播放。"</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"無法播放這部影片。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index eb1cbfb..27660de 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -993,6 +993,12 @@
     <string name="weeks" msgid="6509623834583944518">"amaviki"</string>
     <string name="year" msgid="4001118221013892076">"unyaka"</string>
     <string name="years" msgid="6881577717993213522">"iminyaka"</string>
+    <!-- no translation found for duration_seconds:one (6962015528372969481) -->
+    <!-- no translation found for duration_seconds:other (1886107766577166786) -->
+    <!-- no translation found for duration_minutes:one (4915414002546085617) -->
+    <!-- no translation found for duration_minutes:other (3165187169224908775) -->
+    <!-- no translation found for duration_hours:one (8917467491248809972) -->
+    <!-- no translation found for duration_hours:other (3863962854246773930) -->
     <string name="VideoView_error_title" msgid="3534509135438353077">"Inkinga yevidiyo"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Uxolo, le vidiyo ayilungele ukusakaza bukhomo kwale divaysi."</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"Iyehluleka ukudlala levidiyo."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 447daab..48ee429 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2107,8 +2107,8 @@
             <enum name="locale" value="3" />
         </attr>
 
-        <!-- Direction of the text. A heuristic is used to determine the resolved text direction
-             of paragraphs. -->
+        <!-- Defines the direction of the text. A heuristic is used to determine the resolved text
+              direction of paragraphs. -->
          <attr name="textDirection" format="integer">
             <!-- Default -->
             <enum name="inherit" value="0" />
@@ -2128,7 +2128,7 @@
             <enum name="locale" value="5" />
         </attr>
 
-        <!-- Alignment of the text. A heuristic is used to determine the resolved
+        <!-- Defines the alignment of the text. A heuristic is used to determine the resolved
             text alignment. -->
         <attr name="textAlignment" format="integer">
             <!-- Default -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f91df99..ea28a51 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1006,8 +1006,8 @@
      -->
     <integer-array name="config_defaultNotificationVibePattern">
         <item>0</item>
-        <item>250</item>
-        <item>250</item>
+        <item>150</item>
+        <item>200</item>
         <item>250</item>
     </integer-array>
 
@@ -1017,8 +1017,8 @@
      -->
     <integer-array name="config_notificationFallbackVibePattern">
         <item>0</item>
-        <item>250</item>
-        <item>250</item>
-        <item>250</item>
+        <item>33</item>
+        <item>150</item>
+        <item>50</item>
     </integer-array>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9932d1e..80c2a13 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2838,6 +2838,21 @@
     <!-- Appened to express the value is this unit of time. -->
     <string name="years">years</string>
 
+    <!-- Phrase describing a time duration using seconds [CHAR LIMIT=16] -->
+    <plurals name="duration_seconds">
+        <item quantity="one">1 second</item>
+        <item quantity="other"><xliff:g id="count">%d</xliff:g> seconds</item>
+    </plurals>
+    <!-- Phrase describing a time duration using minutes [CHAR LIMIT=16] -->
+    <plurals name="duration_minutes">
+        <item quantity="one">1 minute</item>
+        <item quantity="other"><xliff:g id="count">%d</xliff:g> minutes</item>
+    </plurals>
+    <!-- Phrase describing a time duration using hours [CHAR LIMIT=16] -->
+    <plurals name="duration_hours">
+        <item quantity="one">1 hour</item>
+        <item quantity="other"><xliff:g id="count">%d</xliff:g> hours</item>
+    </plurals>
 
     <!-- Title for error alert when a video cannot be played.  it can be used by any app. -->
     <string name="VideoView_error_title">Video problem</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6858732..1d29d8c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -870,6 +870,9 @@
   <java-symbol type="plurals" name="abbrev_num_hours_ago" />
   <java-symbol type="plurals" name="abbrev_num_minutes_ago" />
   <java-symbol type="plurals" name="abbrev_num_seconds_ago" />
+  <java-symbol type="plurals" name="duration_hours" />
+  <java-symbol type="plurals" name="duration_minutes" />
+  <java-symbol type="plurals" name="duration_seconds" />
   <java-symbol type="plurals" name="in_num_days" />
   <java-symbol type="plurals" name="in_num_hours" />
   <java-symbol type="plurals" name="in_num_minutes" />
diff --git a/core/tests/coretests/src/android/text/format/DateUtilsTest.java b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
new file mode 100644
index 0000000..cf42bb1
--- /dev/null
+++ b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.format;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+public class DateUtilsTest extends TestCase {
+    @SmallTest
+    public void testFormatDurationSeconds() throws Exception {
+        assertEquals("0 seconds", DateUtils.formatDuration(0));
+        assertEquals("0 seconds", DateUtils.formatDuration(1));
+        assertEquals("0 seconds", DateUtils.formatDuration(499));
+        assertEquals("1 second", DateUtils.formatDuration(500));
+        assertEquals("1 second", DateUtils.formatDuration(1000));
+        assertEquals("2 seconds", DateUtils.formatDuration(1500));
+    }
+
+    @SmallTest
+    public void testFormatDurationMinutes() throws Exception {
+        assertEquals("59 seconds", DateUtils.formatDuration(59000));
+        assertEquals("60 seconds", DateUtils.formatDuration(59500));
+        assertEquals("1 minute", DateUtils.formatDuration(60000));
+        assertEquals("2 minutes", DateUtils.formatDuration(120000));
+    }
+
+    @SmallTest
+    public void testFormatDurationHours() throws Exception {
+        assertEquals("59 minutes", DateUtils.formatDuration(3540000));
+        assertEquals("1 hour", DateUtils.formatDuration(3600000));
+        assertEquals("48 hours", DateUtils.formatDuration(172800000));
+    }
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 13d1791..83ecdd9 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -134,6 +134,7 @@
     <assign-permission name="android.permission.ACCESS_NETWORK_STATE" uid="shell" />
     <assign-permission name="android.permission.ACCESS_WIFI_STATE" uid="shell" />
     <assign-permission name="android.permission.BLUETOOTH" uid="shell" />
+    <assign-permission name="android.permission.EXPAND_STATUS_BAR" uid="shell" />
     <!-- System tool permissions granted to the shell. -->
     <assign-permission name="android.permission.GET_TASKS" uid="shell" />
     <assign-permission name="android.permission.CHANGE_CONFIGURATION" uid="shell" />
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index b4f1e84d..f6b5ffc 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -59,6 +59,10 @@
         int valNative = 0;
         if (src != null) {
             valNative = src.mNativePath;
+            isSimplePath = src.isSimplePath;
+            if (src.rects != null) {
+                rects = new Region(src.rects);
+            }
         }
         mNativePath = init2(valNative);
         mDetectSimplePaths = HardwareRenderer.isAvailable();
@@ -544,6 +548,7 @@
         int dstNative = 0;
         if (dst != null) {
             dstNative = dst.mNativePath;
+            dst.isSimplePath = false;
         }
         native_offset(mNativePath, dx, dy, dstNative);
     }
@@ -555,6 +560,7 @@
      * @param dy The amount in the Y direction to offset the entire path
      */
     public void offset(float dx, float dy) {
+        isSimplePath = false;
         native_offset(mNativePath, dx, dy);
     }
 
@@ -580,6 +586,7 @@
     public void transform(Matrix matrix, Path dst) {
         int dstNative = 0;
         if (dst != null) {
+            dst.isSimplePath = false;
             dstNative = dst.mNativePath;
         }
         native_transform(mNativePath, matrix.native_instance, dstNative);
@@ -591,6 +598,7 @@
      * @param matrix The matrix to apply to the path
      */
     public void transform(Matrix matrix) {
+        isSimplePath = false;
         native_transform(mNativePath, matrix.native_instance);
     }
 
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 315196e..56abed4 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -436,6 +436,8 @@
 
     private boolean mDockAudioMediaEnabled = true;
 
+    private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
     ///////////////////////////////////////////////////////////////////////////
     // Construction
     ///////////////////////////////////////////////////////////////////////////
@@ -3324,6 +3326,13 @@
                                 mBluetoothA2dpEnabled ?
                                         AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
                     }
+
+                    synchronized (mSettingsLock) {
+                        AudioSystem.setForceUse(AudioSystem.FOR_DOCK,
+                                mDockAudioMediaEnabled ?
+                                        AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE);
+                    }
+
                     // indicate the end of reconfiguration phase to audio HAL
                     AudioSystem.setParameters("restarting=false");
                     break;
@@ -3751,13 +3760,7 @@
                         config = AudioSystem.FORCE_BT_CAR_DOCK;
                         break;
                     case Intent.EXTRA_DOCK_STATE_LE_DESK:
-                        synchronized (mSettingsLock) {
-                            if (mDockAudioMediaEnabled) {
-                                config = AudioSystem.FORCE_ANALOG_DOCK;
-                            } else {
-                                config = AudioSystem.FORCE_NONE;
-                            }
-                        }
+                        config = AudioSystem.FORCE_ANALOG_DOCK;
                         break;
                     case Intent.EXTRA_DOCK_STATE_HE_DESK:
                         config = AudioSystem.FORCE_DIGITAL_DOCK;
@@ -3766,8 +3769,14 @@
                     default:
                         config = AudioSystem.FORCE_NONE;
                 }
-
-                AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
+                // Low end docks have a menu to enable or disable audio
+                // (see mDockAudioMediaEnabled)
+                if (!((dockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+                      ((dockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) &&
+                       (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK)))) {
+                    AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
+                }
+                mDockState = dockState;
             } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
                 state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
                                                BluetoothProfile.STATE_DISCONNECTED);
@@ -5079,18 +5088,23 @@
         // top of the stack for the media button event receivers : simply using the top of the
         // stack would make the entry disappear from the RemoteControlDisplay in conditions such as
         // notifications playing during music playback.
-        // crawl the AudioFocus stack until an entry is found with the following characteristics:
+        // Crawl the AudioFocus stack from the top until an entry is found with the following
+        // characteristics:
         // - focus gain on STREAM_MUSIC stream
         // - non-transient focus gain on a stream other than music
         FocusStackEntry af = null;
-        Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
-        while(stackIterator.hasNext()) {
-            FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
-            if ((fse.mStreamType == AudioManager.STREAM_MUSIC)
-                    || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) {
-                af = fse;
-                break;
+        try {
+            for (int index = mFocusStack.size()-1; index >= 0; index--) {
+                FocusStackEntry fse = mFocusStack.elementAt(index);
+                if ((fse.mStreamType == AudioManager.STREAM_MUSIC)
+                        || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) {
+                    af = fse;
+                    break;
+                }
             }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            Log.e(TAG, "Wrong index accessing audio focus stack when updating RCD: " + e);
+            af = null;
         }
         if (af == null) {
             clearRemoteControlDisplay_syncAfRcs();
@@ -5111,6 +5125,7 @@
             clearRemoteControlDisplay_syncAfRcs();
             return;
         }
+
         // refresh conditions were verified: update the remote controls
         // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
         updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 4414191..169502b 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -50,7 +50,7 @@
  * <tr><th>Name</th><th>Value Type</th><th>Description</th></tr>
  * <tr><td>{@link #KEY_CHANNEL_COUNT}</td><td>Integer</td><td></td></tr>
  * <tr><td>{@link #KEY_SAMPLE_RATE}</td><td>Integer</td><td></td></tr>
- * <tr><td>{@link #KEY_IS_ADTS}</td><td>Integer</td><td>optional, if content is AAC audio, setting this key to 1 indicates that each audio frame is prefixed by the ADTS header.</td></tr>
+ * <tr><td>{@link #KEY_IS_ADTS}</td><td>Integer</td><td>optional, if <em>decoding</em> AAC audio content, setting this key to 1 indicates that each audio frame is prefixed by the ADTS header.</td></tr>
  * <tr><td>{@link #KEY_AAC_PROFILE}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is AAC audio, specifies the desired profile.</td></tr>
  * <tr><td>{@link #KEY_CHANNEL_MASK}</td><td>Integer</td><td>A mask of audio channel assignments</td></tr>
  * <tr><td>{@link #KEY_FLAC_COMPRESSION_LEVEL}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is FLAC audio, specifies the desired compression level.</td></tr>
@@ -140,6 +140,8 @@
      * A key mapping to a value of 1 if the content is AAC audio and
      * audio frames are prefixed with an ADTS header.
      * The associated value is an integer (0 or 1).
+     * This key is only supported when _decoding_ content, it cannot
+     * be used to configure an encoder to emit ADTS output.
      */
     public static final String KEY_IS_ADTS = "is-adts";
 
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 2a5a16e..8701f36 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -862,7 +862,7 @@
     private static WifiDisplay findMatchingDisplay(WifiDisplay d, WifiDisplay[] displays) {
         for (int i = 0; i < displays.length; i++) {
             final WifiDisplay other = displays[i];
-            if (d.getDeviceAddress().equals(other.getDeviceAddress())) {
+            if (d.hasSameAddress(other)) {
                 return other;
             }
         }
diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
index 2669c7e..b1104cc 100644
--- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
@@ -141,7 +141,7 @@
                 />
         </LinearLayout>
 
-        <ImageView
+        <com.android.systemui.statusbar.policy.KeyButtonView
             android:layout_width="128dp"
             android:id="@+id/search_light"
             android:layout_height="match_parent"
@@ -282,7 +282,7 @@
                 />
         </LinearLayout>
 
-        <ImageView
+        <com.android.systemui.statusbar.policy.KeyButtonView
             android:layout_width="162dp"
             android:id="@+id/search_light"
             android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index 440a4e1..da52d89 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -145,7 +145,7 @@
                 />
         </LinearLayout>
 
-        <ImageView
+        <com.android.systemui.statusbar.policy.KeyButtonView
             android:layout_width="80dp"
             android:id="@+id/search_light"
             android:layout_height="match_parent"
@@ -289,7 +289,7 @@
                 />
         </LinearLayout>
 
-        <ImageView
+        <com.android.systemui.statusbar.policy.KeyButtonView
             android:id="@+id/search_light"
             android:layout_height="80dp"
             android:layout_width="match_parent"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index cc9c601..f2328566 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -415,7 +415,7 @@
         });
         parent.addView(wifiTile);
 
-        if (mModel.deviceSupportsTelephony()) {
+        if (mModel.deviceHasMobileData()) {
             // RSSI
             QuickSettingsTileView rssiTile = (QuickSettingsTileView)
                     inflater.inflate(R.layout.quick_settings_tile, parent, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index 4513dcb..ec42883 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -29,6 +29,7 @@
 import android.database.ContentObserver;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.WifiDisplayStatus;
+import android.net.ConnectivityManager;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -171,6 +172,8 @@
     private final BugreportObserver mBugreportObserver;
     private final BrightnessObserver mBrightnessObserver;
 
+    private final boolean mHasMobileData;
+
     private QuickSettingsTileView mUserTile;
     private RefreshCallback mUserCallback;
     private UserState mUserState = new UserState();
@@ -249,6 +252,10 @@
         mBrightnessObserver = new BrightnessObserver(mHandler);
         mBrightnessObserver.startObserving();
 
+        ConnectivityManager cm = (ConnectivityManager)
+                context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mHasMobileData = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+
         IntentFilter alarmIntentFilter = new IntentFilter();
         alarmIntentFilter.addAction(Intent.ACTION_ALARM_CHANGED);
         context.registerReceiver(mAlarmIntentReceiver, alarmIntentFilter);
@@ -403,22 +410,22 @@
         mWifiCallback.refreshView(mWifiTile, mWifiState);
     }
 
+    boolean deviceHasMobileData() {
+        return mHasMobileData;
+    }
+
     // RSSI
     void addRSSITile(QuickSettingsTileView view, RefreshCallback cb) {
         mRSSITile = view;
         mRSSICallback = cb;
         mRSSICallback.refreshView(mRSSITile, mRSSIState);
     }
-    boolean deviceSupportsTelephony() {
-        PackageManager pm = mContext.getPackageManager();
-        return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
-    }
     // NetworkSignalChanged callback
     @Override
     public void onMobileDataSignalChanged(
             boolean enabled, int mobileSignalIconId, String signalContentDescription,
             int dataTypeIconId, String dataContentDescription, String enabledDesc) {
-        if (deviceSupportsTelephony()) {
+        if (deviceHasMobileData()) {
             // TODO: If view is in awaiting state, disable
             Resources r = mContext.getResources();
             mRSSIState.signalIconId = enabled && (mobileSignalIconId > 0)
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
index dbd9999..762711d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
@@ -18,10 +18,9 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.util.Log;
@@ -53,17 +52,20 @@
     private final Handler mHandler = new Handler();
     private final KeyguardActivityLauncher mActivityLauncher;
     private final Callbacks mCallbacks;
+    private final CameraWidgetInfo mWidgetInfo;
     private final WindowManager mWindowManager;
     private final Point mRenderedSize = new Point();
-    private final int[] mScreenLocation = new int[2];
+    private final int[] mTmpLoc = new int[2];
+    private final Rect mTmpRect = new Rect();
 
-    private View mWidgetView;
     private long mLaunchCameraStart;
     private boolean mActive;
     private boolean mTransitioning;
-    private boolean mRecovering;
     private boolean mDown;
 
+    private FixedSizeFrameLayout mPreview;
+    private View mFullscreenPreview;
+
     private final Runnable mTransitionToCameraRunnable = new Runnable() {
         @Override
         public void run() {
@@ -81,21 +83,18 @@
             mActivityLauncher.launchCamera(worker, mSecureCameraActivityStartedRunnable);
         }};
 
+    private final Runnable mPostTransitionToCameraEndAction = new Runnable() {
+        @Override
+        public void run() {
+            mHandler.post(mTransitionToCameraEndAction);
+        }};
+
     private final Runnable mRecoverRunnable = new Runnable() {
         @Override
         public void run() {
             recover();
         }};
 
-    private final Runnable mRecoverEndAction = new Runnable() {
-        @Override
-        public void run() {
-            if (!mRecovering)
-                return;
-            mCallbacks.onCameraLaunchedUnsuccessfully();
-            reset();
-        }};
-
     private final Runnable mRenderRunnable = new Runnable() {
         @Override
         public void run() {
@@ -119,13 +118,43 @@
         };
     };
 
+    private static final class FixedSizeFrameLayout extends FrameLayout {
+        int width;
+        int height;
+
+        FixedSizeFrameLayout(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            measureChildren(
+                    MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+            setMeasuredDimension(width, height);
+        }
+    }
+
     private CameraWidgetFrame(Context context, Callbacks callbacks,
-            KeyguardActivityLauncher activityLauncher) {
+            KeyguardActivityLauncher activityLauncher,
+            CameraWidgetInfo widgetInfo, View previewWidget) {
         super(context);
         mCallbacks = callbacks;
         mActivityLauncher = activityLauncher;
+        mWidgetInfo = widgetInfo;
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         KeyguardUpdateMonitor.getInstance(context).registerCallback(mCallback);
+
+        mPreview = new FixedSizeFrameLayout(context);
+        mPreview.addView(previewWidget);
+        addView(mPreview);
+
+        View clickBlocker = new View(context);
+        clickBlocker.setBackgroundColor(Color.TRANSPARENT);
+        clickBlocker.setOnClickListener(this);
+        addView(clickBlocker);
+
+        setContentDescription(context.getString(R.string.keyguard_accessibility_camera));
         if (DEBUG) Log.d(TAG, "new CameraWidgetFrame instance " + instanceId());
     }
 
@@ -137,24 +166,17 @@
         CameraWidgetInfo widgetInfo = launcher.getCameraWidgetInfo();
         if (widgetInfo == null)
             return null;
-        View widgetView = widgetInfo.layoutId > 0 ?
-                inflateWidgetView(context, widgetInfo) :
-                inflateGenericWidgetView(context);
-        if (widgetView == null)
+        View previewWidget = getPreviewWidget(context, widgetInfo);
+        if (previewWidget == null)
             return null;
 
-        ImageView preview = new ImageView(context);
-        preview.setLayoutParams(new FrameLayout.LayoutParams(
-                FrameLayout.LayoutParams.MATCH_PARENT,
-                FrameLayout.LayoutParams.MATCH_PARENT));
-        preview.setScaleType(ScaleType.FIT_CENTER);
-        preview.setContentDescription(preview.getContext().getString(
-                R.string.keyguard_accessibility_camera));
-        CameraWidgetFrame cameraWidgetFrame = new CameraWidgetFrame(context, callbacks, launcher);
-        cameraWidgetFrame.addView(preview);
-        cameraWidgetFrame.mWidgetView = widgetView;
-        preview.setOnClickListener(cameraWidgetFrame);
-        return cameraWidgetFrame;
+        return new CameraWidgetFrame(context, callbacks, launcher, widgetInfo, previewWidget);
+    }
+
+    private static View getPreviewWidget(Context context, CameraWidgetInfo widgetInfo) {
+        return widgetInfo.layoutId > 0 ?
+                inflateWidgetView(context, widgetInfo) :
+                inflateGenericWidgetView(context);
     }
 
     private static View inflateWidgetView(Context context, CameraWidgetInfo widgetInfo) {
@@ -188,63 +210,45 @@
         return iv;
     }
 
-    public void render() {
-        final Throwable[] thrown = new Throwable[1];
-        final Bitmap[] offscreen = new Bitmap[1];
-        try {
-            final int width = getRootView().getWidth();
-            final int height = getRootView().getHeight();
-            if (mRenderedSize.x == width && mRenderedSize.y == height) {
-                if (DEBUG) Log.d(TAG, String.format("Already rendered at size=%sx%s",
-                        width, height));
-                return;
-            }
-            if (width == 0 || height == 0) {
-                return;
-            }
-            final long start = SystemClock.uptimeMillis();
-            offscreen[0] = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-            final Canvas c = new Canvas(offscreen[0]);
-            mWidgetView.measure(
-                    MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
-            mWidgetView.layout(0, 0, width, height);
-            mWidgetView.draw(c);
-
-            final long end = SystemClock.uptimeMillis();
-            if (DEBUG) Log.d(TAG, String.format(
-                    "Rendered camera widget in %sms size=%sx%s instance=%s at %s",
-                    end - start,
-                    width, height,
-                    instanceId(),
-                    end));
-            mRenderedSize.set(width, height);
-        } catch (Throwable t) {
-            thrown[0] = t;
+    private void render() {
+        final View root = getRootView();
+        final int width = root.getWidth();
+        final int height = root.getHeight();
+        if (mRenderedSize.x == width && mRenderedSize.y == height) {
+            if (DEBUG) Log.d(TAG, String.format("Already rendered at size=%sx%s", width, height));
+            return;
+        }
+        if (width == 0 || height == 0) {
+            return;
         }
 
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (thrown[0] == null) {
-                    try {
-                        ((ImageView) getChildAt(0)).setImageBitmap(offscreen[0]);
-                    } catch (Throwable t) {
-                        thrown[0] = t;
-                    }
-                }
-                if (thrown[0] == null)
-                    return;
+        mPreview.width = width;
+        mPreview.height = height;
+        mPreview.requestLayout();
 
-                Log.w(TAG, "Error rendering camera widget", thrown[0]);
-                try {
-                    removeAllViews();
-                    final View genericView = inflateGenericWidgetView(mContext);
-                    addView(genericView);
-                } catch (Throwable t) {
-                    Log.w(TAG, "Error inflating generic camera widget", t);
-                }
-            }});
+        final int thisWidth = getWidth() - getPaddingLeft() - getPaddingRight();
+        final int thisHeight = getHeight() - getPaddingTop() - getPaddingBottom();
+
+        final float pvScaleX = (float) thisWidth / width;
+        final float pvScaleY = (float) thisHeight / height;
+        final float pvScale = Math.min(pvScaleX, pvScaleY);
+
+        final int pvWidth = (int) (pvScale * width);
+        final int pvHeight = (int) (pvScale * height);
+
+        final float pvTransX = pvWidth < thisWidth ? (thisWidth - pvWidth) / 2 : 0;
+        final float pvTransY = pvHeight < thisHeight ? (thisHeight - pvHeight) / 2 : 0;
+
+        mPreview.setPivotX(0);
+        mPreview.setPivotY(0);
+        mPreview.setScaleX(pvScale);
+        mPreview.setScaleY(pvScale);
+        mPreview.setTranslationX(pvTransX);
+        mPreview.setTranslationY(pvTransY);
+
+        mRenderedSize.set(width, height);
+        if (DEBUG) Log.d(TAG, String.format("Rendered camera widget size=%sx%s instance=%s",
+                width, height, instanceId()));
     }
 
     private void transitionToCamera() {
@@ -252,55 +256,53 @@
 
         mTransitioning = true;
 
-        final View child = getChildAt(0);
-        final View root = getRootView();
-
-        final int startWidth = child.getWidth();
-        final int startHeight = child.getHeight();
-
-        final int finishWidth = root.getWidth();
-        final int finishHeight = root.getHeight();
-
-        final float scaleX = (float) finishWidth / startWidth;
-        final float scaleY = (float) finishHeight / startHeight;
-        final float scale = Math.round( Math.max(scaleX, scaleY) * 100) / 100f;
-
-        final int[] loc = new int[2];
-        root.getLocationInWindow(loc);
-        final int finishCenter = loc[1] + finishHeight / 2;
-
-        child.getLocationInWindow(loc);
-        final int startCenter = loc[1] + startHeight / 2;
-
-        if (DEBUG) Log.d(TAG, String.format("Transitioning to camera. " +
-                "(start=%sx%s, finish=%sx%s, scale=%s,%s, startCenter=%s, finishCenter=%s)",
-                startWidth, startHeight,
-                finishWidth, finishHeight,
-                scaleX, scaleY,
-                startCenter, finishCenter));
-
         enableWindowExitAnimation(false);
-        animate()
-            .scaleX(scale)
-            .scaleY(scale)
-            .translationY(finishCenter - startCenter)
-            .setDuration(WIDGET_ANIMATION_DURATION)
-            .withEndAction(mTransitionToCameraEndAction)
-            .start();
 
+        mPreview.getLocationInWindow(mTmpLoc);
+        final float pvHeight = mPreview.getHeight() * mPreview.getScaleY();
+        final float pvCenter = mTmpLoc[1] + pvHeight / 2f;
+
+        final ViewGroup root = (ViewGroup) getRootView();
+        if (mFullscreenPreview == null) {
+            mFullscreenPreview = getPreviewWidget(mContext, mWidgetInfo);
+            mFullscreenPreview.setClickable(false);
+            root.addView(mFullscreenPreview);
+        }
+
+        root.getWindowVisibleDisplayFrame(mTmpRect);
+        final float fsHeight = mTmpRect.height();
+        final float fsCenter = mTmpRect.top + fsHeight / 2;
+
+        final float fsScaleY = pvHeight / fsHeight;
+        final float fsTransY = pvCenter - fsCenter;
+        final float fsScaleX = mPreview.getScaleX();
+
+        mPreview.setVisibility(View.GONE);
+        mFullscreenPreview.setVisibility(View.VISIBLE);
+        mFullscreenPreview.setTranslationY(fsTransY);
+        mFullscreenPreview.setScaleX(fsScaleX);
+        mFullscreenPreview.setScaleY(fsScaleY);
+        mFullscreenPreview
+            .animate()
+            .scaleX(1)
+            .scaleY(1)
+            .translationX(0)
+            .translationY(0)
+            .setDuration(WIDGET_ANIMATION_DURATION)
+            .withEndAction(mPostTransitionToCameraEndAction)
+            .start();
         mCallbacks.onLaunchingCamera();
     }
 
     private void recover() {
         if (DEBUG) Log.d(TAG, "recovering at " + SystemClock.uptimeMillis());
-        mRecovering = true;
-        animate()
-            .scaleX(1)
-            .scaleY(1)
-            .translationY(0)
-            .setDuration(WIDGET_ANIMATION_DURATION)
-            .withEndAction(mRecoverEndAction)
-            .start();
+        mCallbacks.onCameraLaunchedUnsuccessfully();
+        reset();
+    }
+
+    @Override
+    public void setOnLongClickListener(OnLongClickListener l) {
+        // ignore
     }
 
     @Override
@@ -340,8 +342,8 @@
             return true;
         }
 
-        getLocationOnScreen(mScreenLocation);
-        int rawBottom = mScreenLocation[1] + getHeight();
+        getLocationOnScreen(mTmpLoc);
+        int rawBottom = mTmpLoc[1] + getHeight();
         if (event.getRawY() > rawBottom) {
             if (DEBUG) Log.d(TAG, "onUserInteraction eaten: below widget");
             return true;
@@ -388,14 +390,14 @@
         if (DEBUG) Log.d(TAG, "reset at " + SystemClock.uptimeMillis());
         mLaunchCameraStart = 0;
         mTransitioning = false;
-        mRecovering = false;
         mDown = false;
         cancelTransitionToCamera();
         mHandler.removeCallbacks(mRecoverRunnable);
-        animate().cancel();
-        setScaleX(1);
-        setScaleY(1);
-        setTranslationY(0);
+        mPreview.setVisibility(View.VISIBLE);
+        if (mFullscreenPreview != null) {
+            mFullscreenPreview.animate().cancel();
+            mFullscreenPreview.setVisibility(View.GONE);
+        }
         enableWindowExitAnimation(true);
     }
 
@@ -403,11 +405,18 @@
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         if (DEBUG) Log.d(TAG, String.format("onSizeChanged new=%sx%s old=%sx%s at %s",
                 w, h, oldw, oldh, SystemClock.uptimeMillis()));
-        final Handler worker =  getWorkerHandler();
-        (worker != null ? worker : mHandler).post(mRenderRunnable);
+        mHandler.post(mRenderRunnable);
         super.onSizeChanged(w, h, oldw, oldh);
     }
 
+    @Override
+    public void onBouncerShowing(boolean showing) {
+        if (showing) {
+            mTransitioning = false;
+            mHandler.post(mRecoverRunnable);
+        }
+    }
+
     private void enableWindowExitAnimation(boolean isEnabled) {
         View root = getRootView();
         ViewGroup.LayoutParams lp = root.getLayoutParams();
@@ -427,15 +436,14 @@
         if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged " + showing
                 + " at " + SystemClock.uptimeMillis());
         if (mTransitioning && !showing) {
-          mTransitioning = false;
-          mRecovering = false;
-          mHandler.removeCallbacks(mRecoverRunnable);
-          if (mLaunchCameraStart > 0) {
-              long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart;
-              if (DEBUG) Log.d(TAG, String.format("Camera took %sms to launch", launchTime));
-              mLaunchCameraStart = 0;
-              onCameraLaunched();
-          }
+            mTransitioning = false;
+            mHandler.removeCallbacks(mRecoverRunnable);
+            if (mLaunchCameraStart > 0) {
+                long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart;
+                if (DEBUG) Log.d(TAG, String.format("Camera took %sms to launch", launchTime));
+                mLaunchCameraStart = 0;
+                onCameraLaunched();
+            }
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index de19bd5..84f3d61 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -26,7 +26,6 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
-import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -82,6 +81,7 @@
     private int mAppWidgetToShow;
 
     private boolean mCheckAppWidgetConsistencyOnBootCompleted = false;
+    private boolean mCleanupAppWidgetsOnBootCompleted = false;
 
     protected OnDismissAction mDismissAction;
 
@@ -128,6 +128,8 @@
         mLockPatternUtils = new LockPatternUtils(context);
         mAppWidgetHost = new AppWidgetHost(
                 context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper());
+        cleanupAppWidgetIds();
+
         mAppWidgetManager = AppWidgetManager.getInstance(mContext);
         mSecurityModel = new KeyguardSecurityModel(context);
 
@@ -153,6 +155,39 @@
         }
     }
 
+    private void cleanupAppWidgetIds() {
+        // Since this method may delete a widget (which we can't do until boot completed) we
+        // may have to defer it until after boot complete.
+        if (!KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
+            mCleanupAppWidgetsOnBootCompleted = true;
+            return;
+        }
+        // Clean up appWidgetIds that are bound to lockscreen, but not actually used
+        // This is only to clean up after another bug: we used to not call
+        // deleteAppWidgetId when a user manually deleted a widget in keyguard. This code
+        // shouldn't have to run more than once per user. AppWidgetProviders rely on callbacks
+        // that are triggered by deleteAppWidgetId, which is why we're doing this
+        int[] appWidgetIdsInKeyguardSettings = mLockPatternUtils.getAppWidgets();
+        int[] appWidgetIdsBoundToHost = mAppWidgetHost.getAppWidgetIds();
+        for (int i = 0; i < appWidgetIdsBoundToHost.length; i++) {
+            int appWidgetId = appWidgetIdsBoundToHost[i];
+            if (!contains(appWidgetIdsInKeyguardSettings, appWidgetId)) {
+                Log.d(TAG, "Found a appWidgetId that's not being used by keyguard, deleting id "
+                        + appWidgetId);
+                mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+            }
+        }
+    }
+
+    private static boolean contains(int[] array, int target) {
+        for (int value : array) {
+            if (value == target) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks =
             new KeyguardUpdateMonitorCallback() {
         @Override
@@ -162,6 +197,10 @@
                 mSwitchPageRunnable.run();
                 mCheckAppWidgetConsistencyOnBootCompleted = false;
             }
+            if (mCleanupAppWidgetsOnBootCompleted) {
+                cleanupAppWidgetIds();
+                mCleanupAppWidgetsOnBootCompleted = false;
+            }
         }
     };
 
@@ -331,10 +370,17 @@
         };
 
         @Override
-        public void onRemoveView(View v) {
+        public void onRemoveView(View v, boolean deletePermanently) {
             if (numWidgets() < MAX_WIDGETS) {
                 setAddWidgetEnabled(true);
             }
+            if (deletePermanently) {
+                final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
+                if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID &&
+                        appWidgetId != LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
+                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+                }
+            }
         }
     };
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
index 76ba811..4e8aba7 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -129,12 +129,19 @@
         @Override
         protected void onConfigurationChanged(Configuration newConfig) {
             super.onConfigurationChanged(newConfig);
-            if (mKeyguardHost.getVisibility() == View.VISIBLE) {
-                // only propagate configuration messages if we're currently showing
-                maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null);
-            } else {
-                if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible");
-            }
+            post(new Runnable() {
+                @Override
+                public void run() {
+                    synchronized (KeyguardViewManager.this) {
+                        if (mKeyguardHost.getVisibility() == View.VISIBLE) {
+                            // only propagate configuration messages if we're currently showing
+                            maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null);
+                        } else {
+                            if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible");
+                        }
+                    }
+                }
+            });
         }
 
         @Override
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
index df4c661..c227619 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -22,6 +22,7 @@
 import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
+import android.app.SearchManager;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -166,6 +167,9 @@
     /** UserManager for querying number of users */
     private UserManager mUserManager;
 
+    /** SearchManager for determining whether or not search assistant is available */
+    private SearchManager mSearchManager;
+
     /**
      * Used to keep the device awake while to ensure the keyguard finishes opening before
      * we sleep.
@@ -527,6 +531,7 @@
      * Let us know that the system is ready after startup.
      */
     public void onSystemReady() {
+        mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
         synchronized (this) {
             if (DEBUG) Log.d(TAG, "onSystemReady");
             mSystemReady = true;
@@ -1313,6 +1318,9 @@
                     // showing secure lockscreen; disable ticker.
                     flags |= StatusBarManager.DISABLE_NOTIFICATION_TICKER;
                 }
+                if (!isAssistantAvailable()) {
+                    flags |= StatusBarManager.DISABLE_SEARCH;
+                }
             }
 
             if (DEBUG) {
@@ -1410,4 +1418,8 @@
         mKeyguardViewManager.showAssistant();
     }
 
+    private boolean isAssistantAvailable() {
+        return mSearchManager != null
+                && mSearchManager.getAssistIntent(mContext, UserHandle.USER_CURRENT) != null;
+    }
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java
index debf765..257fd27 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetCarousel.java
@@ -16,7 +16,6 @@
 package com.android.internal.policy.impl.keyguard;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
@@ -27,10 +26,10 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 
-import java.util.ArrayList;
-
 import com.android.internal.R;
 
+import java.util.ArrayList;
+
 public class KeyguardWidgetCarousel extends KeyguardWidgetPager {
 
     private float mAdjacentPagesAngle;
@@ -56,17 +55,30 @@
         return MAX_SCROLL_PROGRESS;
     }
 
-    public float getAlphaForPage(int screenCenter, int index) {
+    public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) {
         View child = getChildAt(index);
         if (child == null) return 0f;
 
+        boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1;
         float scrollProgress = getScrollProgress(screenCenter, child, index);
-        if (!isOverScrollChild(index, scrollProgress)) {
+
+        if (isOverScrollChild(index, scrollProgress)) {
+            return 1.0f;
+        } else if ((showSidePages && inVisibleRange) || index == getNextPage()) {
             scrollProgress = getBoundedScrollProgress(screenCenter, child, index);
             float alpha = 1.0f - 1.0f * Math.abs(scrollProgress / MAX_SCROLL_PROGRESS);
             return alpha;
         } else {
-            return 1.0f;
+            return 0f;
+        }
+    }
+
+    public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) {
+        boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1;
+        if (inVisibleRange) {
+            return super.getOutlineAlphaForPage(screenCenter, index, showSidePages);
+        } else {
+            return 0f;
         }
     }
 
@@ -75,24 +87,32 @@
             mChildrenOutlineFadeAnimation.cancel();
             mChildrenOutlineFadeAnimation = null;
         }
+        boolean showSidePages = mShowingInitialHints || isPageMoving();
         if (!isReordering(false)) {
             for (int i = 0; i < getChildCount(); i++) {
                 KeyguardWidgetFrame child = getWidgetPageAt(i);
                 if (child != null) {
-                    child.setBackgroundAlpha(getOutlineAlphaForPage(screenCenter, i));
-                    child.setContentAlpha(getAlphaForPage(screenCenter, i));
+                    float outlineAlpha = getOutlineAlphaForPage(screenCenter, i, showSidePages);
+                    float contentAlpha = getAlphaForPage(screenCenter, i,showSidePages);
+                    child.setBackgroundAlpha(outlineAlpha);
+                    child.setContentAlpha(contentAlpha);
                 }
             }
         }
     }
 
     public void showInitialPageHints() {
+        mShowingInitialHints = true;
         int count = getChildCount();
         for (int i = 0; i < count; i++) {
+            boolean inVisibleRange = i >= getNextPage() - 1 && i <= getNextPage() + 1;
             KeyguardWidgetFrame child = getWidgetPageAt(i);
-            if (i >= mCurrentPage - 1 && i <= mCurrentPage + 1) {
-                child.fadeFrame(this, true, KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER,
-                        CHILDREN_OUTLINE_FADE_IN_DURATION);
+            if (inVisibleRange) {
+                child.setBackgroundAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
+                child.setContentAlpha(1f);
+            } else {
+                child.setBackgroundAlpha(0f);
+                child.setContentAlpha(0f);
             }
         }
     }
@@ -220,8 +240,8 @@
 
         for (int i = 0; i < count; i++) {
             KeyguardWidgetFrame child = getWidgetPageAt(i);
-            float finalAlpha = getAlphaForPage(mScreenCenter, i);
-            float finalOutlineAlpha = getOutlineAlphaForPage(mScreenCenter, i);
+            float finalAlpha = getAlphaForPage(mScreenCenter, i, true);
+            float finalOutlineAlpha = getOutlineAlphaForPage(mScreenCenter, i, true);
             getTransformForPage(mScreenCenter, i, mTmpTransform);
 
             boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
index 3c79206..babb9cb 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
@@ -424,7 +424,9 @@
             mBgAlphaController = caller;
         }
 
-        if (mBgAlphaController != caller) return;
+        if (mBgAlphaController != caller && mBgAlphaController != null) {
+            return;
+        }
 
         if (mFrameFade != null) {
             mFrameFade.cancel();
@@ -512,6 +514,10 @@
         return false;
     }
 
+    public void onBouncerShowing(boolean showing) {
+        // hook for subclasses
+    }
+
     public void setWorkerHandler(Handler workerHandler) {
         mWorkerHandler = workerHandler;
     }
@@ -519,4 +525,5 @@
     public Handler getWorkerHandler() {
         return mWorkerHandler;
     }
+
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
index 25e2781..5b00dd2 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -70,6 +70,7 @@
     private Callbacks mCallbacks;
 
     private int mWidgetToResetAfterFadeOut;
+    protected boolean mShowingInitialHints = false;
 
     // Bouncer
     private int mBouncerZoomInOutDuration = 250;
@@ -237,18 +238,17 @@
         public void userActivity();
         public void onUserActivityTimeoutChanged();
         public void onAddView(View v);
-        public void onRemoveView(View v);
+        public void onRemoveView(View v, boolean deletePermanently);
     }
 
     public void addWidget(View widget) {
         addWidget(widget, -1);
     }
 
-
-    public void onRemoveView(View v) {
+    public void onRemoveView(View v, final boolean deletePermanently) {
         final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
         if (mCallbacks != null) {
-            mCallbacks.onRemoveView(v);
+            mCallbacks.onRemoveView(v, deletePermanently);
         }
         mBackgroundWorkerHandler.post(new Runnable() {
             @Override
@@ -458,12 +458,21 @@
     private void updatePageAlphaValues(int screenCenter) {
     }
 
-    public float getAlphaForPage(int screenCenter, int index) {
-        return 1f;
+    public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) {
+        if (showSidePages) {
+            return 1f;
+        } else {
+            return index == mCurrentPage ? 1.0f : 0f;
+        }
     }
 
-    public float getOutlineAlphaForPage(int screenCenter, int index) {
-        return getAlphaForPage(screenCenter, index) * KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER;
+    public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) {
+        if (showSidePages) {
+            return getAlphaForPage(screenCenter, index, showSidePages)
+                    * KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER;
+        } else {
+            return 0f;
+        }
     }
 
     protected boolean isOverScrollChild(int index, float scrollProgress) {
@@ -562,12 +571,12 @@
     }
 
     public void showInitialPageHints() {
+        mShowingInitialHints = true;
         int count = getChildCount();
         for (int i = 0; i < count; i++) {
             KeyguardWidgetFrame child = getWidgetPageAt(i);
             if (i != mCurrentPage) {
-                child.fadeFrame(this, true, KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER,
-                        CHILDREN_OUTLINE_FADE_IN_DURATION);
+                child.setBackgroundAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER);
                 child.setContentAlpha(0f);
             } else {
                 child.setBackgroundAlpha(0f);
@@ -576,10 +585,6 @@
         }
     }
 
-    public void showSidePageHints() {
-        animateOutlinesAndSidePages(true, -1);
-    }
-
     @Override
     void setCurrentPage(int currentPage) {
         super.setCurrentPage(currentPage);
@@ -658,7 +663,7 @@
         for (int i = 0; i < count; i++) {
             float finalContentAlpha;
             if (show) {
-                finalContentAlpha = getAlphaForPage(mScreenCenter, i);
+                finalContentAlpha = getAlphaForPage(mScreenCenter, i, true);
             } else if (!show && i == curPage) {
                 finalContentAlpha = 1f;
             } else {
@@ -670,7 +675,7 @@
             ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha);
             anims.add(a);
 
-            float finalOutlineAlpha = show ? getOutlineAlphaForPage(mScreenCenter, i) : 0f;
+            float finalOutlineAlpha = show ? getOutlineAlphaForPage(mScreenCenter, i, true) : 0f;
             child.fadeFrame(this, show, finalOutlineAlpha, duration);
         }
 
@@ -696,6 +701,7 @@
                         frame.resetSize();
                     }
                     mWidgetToResetAfterFadeOut = -1;
+                    mShowingInitialHints = false;
                 }
                 updateWidgetFramesImportantForAccessibility();
             }
@@ -775,6 +781,9 @@
             mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
             mZoomInOutAnim.start();
         }
+        if (currentPage instanceof KeyguardWidgetFrame) {
+            ((KeyguardWidgetFrame)currentPage).onBouncerShowing(false);
+        }
     }
 
     // Zoom out after the bouncer is initiated
@@ -800,6 +809,9 @@
             mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
             mZoomInOutAnim.start();
         }
+        if (currentPage instanceof KeyguardWidgetFrame) {
+            ((KeyguardWidgetFrame)currentPage).onBouncerShowing(true);
+        }
     }
 
     boolean isAddPage(int pageIndex) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
index 3900ab4..0b06306 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
@@ -1457,7 +1457,7 @@
                                 }
 
                                 removeView(mDragView);
-                                onRemoveView(mDragView);
+                                onRemoveView(mDragView, false);
                                 addView(mDragView, pageUnderPointIndex);
                                 onAddView(mDragView, pageUnderPointIndex);
                                 mSidePageHoverIndex = -1;
@@ -1587,7 +1587,7 @@
     }
 
     //public abstract void onFlingToDelete(View v);
-    public abstract void onRemoveView(View v);
+    public abstract void onRemoveView(View v, boolean deletePermanently);
     public abstract void onAddView(View v, int index);
 
     private void resetTouchState() {
@@ -2391,7 +2391,7 @@
                 slideAnimations.start();
 
                 removeView(dragView);
-                onRemoveView(dragView);
+                onRemoveView(dragView, true);
             }
         };
     }
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 06d37dc..9590712 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -26,6 +26,8 @@
 import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -54,13 +56,19 @@
     Locale mLocale;
     PackageManager mPackageManager;
     boolean mSafeMode;
+    private final Handler mSaveStateHandler;
 
     private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
 
     AppWidgetService(Context context) {
         mContext = context;
+
+        HandlerThread handlerThread = new HandlerThread("AppWidgetService -- Save state");
+        handlerThread.start();
+        mSaveStateHandler = new Handler(handlerThread.getLooper());
+
         mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5);
-        AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0);
+        AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0, mSaveStateHandler);
         mAppWidgetServices.append(0, primary);
     }
 
@@ -138,6 +146,11 @@
         return getImplForUser(getCallingOrCurrentUserId()).allocateAppWidgetId(
                 packageName, hostId);
     }
+
+    @Override
+    public int[] getAppWidgetIdsForHost(int hostId) throws RemoteException {
+        return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetIdsForHost(hostId);
+    }
     
     @Override
     public void deleteAppWidgetId(int appWidgetId) throws RemoteException {
@@ -229,7 +242,7 @@
             if (service == null) {
                 Slog.i(TAG, "Unable to find AppWidgetServiceImpl for user " + userId + ", adding");
                 // TODO: Verify that it's a valid user
-                service = new AppWidgetServiceImpl(mContext, userId);
+                service = new AppWidgetServiceImpl(mContext, userId, mSaveStateHandler);
                 service.systemReady(mSafeMode);
                 // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
                 mAppWidgetServices.append(userId, service);
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index daa82f2..fe92b26 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -41,7 +41,10 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -180,15 +183,18 @@
     boolean mStateLoaded;
     int mMaxWidgetBitmapMemory;
 
+    private final Handler mSaveStateHandler;
+
     // These are for debugging only -- widgets are going missing in some rare instances
     ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
     ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
 
-    AppWidgetServiceImpl(Context context, int userId) {
+    AppWidgetServiceImpl(Context context, int userId, Handler saveStateHandler) {
         mContext = context;
         mPm = AppGlobals.getPackageManager();
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mUserId = userId;
+        mSaveStateHandler = saveStateHandler;
         computeMaximumWidgetBitmapMemory();
     }
 
@@ -236,7 +242,7 @@
                         updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
                     }
                 }
-                saveStateLocked();
+                saveStateAsync();
             }
         }
     }
@@ -286,7 +292,7 @@
                         providersModified |= addProvidersForPackageLocked(pkgName);
                     }
                 }
-                saveStateLocked();
+                saveStateAsync();
             }
         } else {
             Bundle extras = intent.getExtras();
@@ -297,7 +303,7 @@
                     ensureStateLoadedLocked();
                     for (String pkgName : pkgList) {
                         providersModified |= removeProvidersForPackageLocked(pkgName);
-                        saveStateLocked();
+                        saveStateAsync();
                     }
                 }
             }
@@ -410,7 +416,7 @@
 
     private void ensureStateLoadedLocked() {
         if (!mStateLoaded) {
-            loadAppWidgetList();
+            loadAppWidgetListLocked();
             loadStateLocked();
             mStateLoaded = true;
         }
@@ -431,7 +437,7 @@
             host.instances.add(id);
             mAppWidgetIds.add(id);
 
-            saveStateLocked();
+            saveStateAsync();
             if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId
                     + " id=" + appWidgetId);
             return appWidgetId;
@@ -444,7 +450,7 @@
             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
             if (id != null) {
                 deleteAppWidgetLocked(id);
-                saveStateLocked();
+                saveStateAsync();
             }
         }
     }
@@ -456,7 +462,7 @@
             Host host = lookupHostLocked(callingUid, hostId);
             if (host != null) {
                 deleteHostLocked(host);
-                saveStateLocked();
+                saveStateAsync();
             }
         }
     }
@@ -475,7 +481,7 @@
                 }
             }
             if (changed) {
-                saveStateLocked();
+                saveStateAsync();
             }
         }
     }
@@ -591,7 +597,7 @@
 
                 // schedule the future updates
                 registerForBroadcastsLocked(p, getAppWidgetIds(p));
-                saveStateLocked();
+                saveStateAsync();
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -655,8 +661,8 @@
             } else {
                 mPackagesWithBindWidgetPermission.remove(packageName);
             }
+            saveStateAsync();
         }
-        saveStateLocked();
     }
 
     // Binds to a specific RemoteViewsService
@@ -849,13 +855,17 @@
     }
 
     public List<AppWidgetProviderInfo> getInstalledProviders() {
+        return getInstalledProviders(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
+    }
+
+    private List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) {
         synchronized (mAppWidgetIds) {
             ensureStateLoadedLocked();
             final int N = mInstalledProviders.size();
             ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
             for (int i = 0; i < N; i++) {
                 Provider p = mInstalledProviders.get(i);
-                if (!p.zombie) {
+                if (!p.zombie && (p.info.widgetCategory & categoryFilter) != 0) {
                     result.add(cloneIfLocalBinder(p.info));
                 }
             }
@@ -893,6 +903,20 @@
         }
     }
 
+    private void saveStateAsync() {
+        mSaveStateHandler.post(mSaveStateRunnable);
+    }
+
+    private final Runnable mSaveStateRunnable = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mAppWidgetIds) {
+                ensureStateLoadedLocked();
+                saveStateLocked();
+            }
+        }
+    };
+
     public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
         synchronized (mAppWidgetIds) {
             options = cloneIfLocalBinder(options);
@@ -913,7 +937,7 @@
             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
             mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
-            saveStateLocked();
+            saveStateAsync();
         }
     }
 
@@ -1214,7 +1238,7 @@
         }
     }
 
-    void loadAppWidgetList() {
+    void loadAppWidgetListLocked() {
         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
         try {
             List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
@@ -1334,6 +1358,28 @@
         }
     }
 
+    static int[] getAppWidgetIds(Host h) {
+        int instancesSize = h.instances.size();
+        int appWidgetIds[] = new int[instancesSize];
+        for (int i = 0; i < instancesSize; i++) {
+            appWidgetIds[i] = h.instances.get(i).appWidgetId;
+        }
+        return appWidgetIds;
+    }
+
+    public int[] getAppWidgetIdsForHost(int hostId) {
+        synchronized (mAppWidgetIds) {
+            ensureStateLoadedLocked();
+            int callingUid = Binder.getCallingUid();
+            Host host = lookupHostLocked(callingUid, hostId);
+            if (host != null) {
+                return getAppWidgetIds(host);
+            } else {
+                return new int[0];
+            }
+        }
+    }
+
     private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
         Provider p = null;
 
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index ad1dfb2..a7c4d73 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -2686,18 +2686,8 @@
                             state + "/" + info.getDetailedState());
                     }
 
-                    // Connectivity state changed:
-                    // [31-14] Reserved for future use
-                    // [13-10] Network subtype (for mobile network, as defined
-                    //         by TelephonyManager)
-                    // [9-4] Detailed state ordinal (as defined by
-                    //         NetworkInfo.DetailedState)
-                    // [3-0] Network type (as defined by ConnectivityManager)
-                    int eventLogParam = (info.getType() & 0xf) |
-                            ((info.getDetailedState().ordinal() & 0x3f) << 4) |
-                            (info.getSubtype() << 10);
-                    EventLog.writeEvent(EventLogTags.CONNECTIVITY_STATE_CHANGED,
-                            eventLogParam);
+                    EventLogTags.writeConnectivityStateChanged(
+                            info.getType(), info.getSubtype(), info.getDetailedState().ordinal());
 
                     if (info.getDetailedState() ==
                             NetworkInfo.DetailedState.FAILED) {
diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags
index 0fe66fc..8bc2da2 100644
--- a/services/java/com/android/server/EventLogTags.logtags
+++ b/services/java/com/android/server/EventLogTags.logtags
@@ -135,12 +135,8 @@
 # ---------------------------
 # ConnectivityService.java
 # ---------------------------
-# Connectivity state changed:
-# [31-14] Reserved for future use
-# [13-10] Network subtype (for mobile network, as defined by TelephonyManager)
-# [ 9- 4] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
-# [ 3- 0] Network type (as defined by ConnectivityManager)
-50020 connectivity_state_changed (custom|1|5)
+# Connectivity state changed
+50020 connectivity_state_changed (type|1),(subtype|1),(state|1)
 
 
 # ---------------------------
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index c9ff595..c33fc71 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -386,6 +386,7 @@
     private Locale mLastSystemLocale;
     private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
     private final IPackageManager mIPackageManager;
+    private boolean mInputBoundToKeyguard;
 
     class SettingsObserver extends ContentObserver {
         SettingsObserver(Handler handler) {
@@ -877,10 +878,12 @@
         final boolean hardKeyShown = haveHardKeyboard
                 && conf.hardKeyboardHidden
                         != Configuration.HARDKEYBOARDHIDDEN_YES;
-        final boolean isScreenLocked = mKeyguardManager != null
-                && mKeyguardManager.isKeyguardLocked()
-                && mKeyguardManager.isKeyguardSecure();
-        mImeWindowVis = (!isScreenLocked && (mInputShown || hardKeyShown)) ?
+        final boolean isScreenLocked =
+                mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
+        final boolean isScreenSecurelyLocked =
+                isScreenLocked && mKeyguardManager.isKeyguardSecure();
+        final boolean inputShown = mInputShown && (!isScreenLocked || mInputBoundToKeyguard);
+        mImeWindowVis = (!isScreenSecurelyLocked && (inputShown || hardKeyShown)) ?
                 (InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE) : 0;
         updateImeWindowStatusLocked();
     }
@@ -1124,6 +1127,13 @@
             return mNoBinding;
         }
 
+        if (mCurClient == null) {
+            mInputBoundToKeyguard = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
+            if (DEBUG) {
+                Slog.v(TAG, "New bind. keyguard = " +  mInputBoundToKeyguard);
+            }
+        }
+
         if (mCurClient != cs) {
             // If the client is changing, we need to switch over to the new
             // one.
@@ -2486,10 +2496,8 @@
                 map.put(id, p);
 
                 // Valid system default IMEs and IMEs that have English subtypes are enabled
-                // by default, unless there's a hard keyboard and the system IME was explicitly
-                // disabled
-                if ((isValidSystemDefaultIme(p, mContext) || isSystemImeThatHasEnglishSubtype(p))
-                        && (!haveHardKeyboard || disabledSysImes.indexOf(id) < 0)) {
+                // by default
+                if ((isValidSystemDefaultIme(p, mContext) || isSystemImeThatHasEnglishSubtype(p))) {
                     setInputMethodEnabledLocked(id, true);
                 }
 
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 89fa6d0..7a55497c 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -506,7 +506,7 @@
                 }
             } else {
                 Intent statusChanged = new Intent();
-                statusChanged.putExtras(extras);
+                statusChanged.putExtras(new Bundle(extras));
                 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
                 try {
                     synchronized (this) {
@@ -541,7 +541,7 @@
                 }
             } else {
                 Intent locationChanged = new Intent();
-                locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
+                locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, new Location(location));
                 try {
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index c512bc1..ad28a36 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -57,6 +57,8 @@
 import android.util.Slog;
 import android.util.Xml;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IMediaContainerService;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
@@ -181,13 +183,13 @@
     /** When defined, base template for user-specific {@link StorageVolume}. */
     private StorageVolume mEmulatedTemplate;
 
-    // @GuardedBy("mVolumesLock")
+    @GuardedBy("mVolumesLock")
     private final ArrayList<StorageVolume> mVolumes = Lists.newArrayList();
     /** Map from path to {@link StorageVolume} */
-    // @GuardedBy("mVolumesLock")
+    @GuardedBy("mVolumesLock")
     private final HashMap<String, StorageVolume> mVolumesByPath = Maps.newHashMap();
     /** Map from path to state */
-    // @GuardedBy("mVolumesLock")
+    @GuardedBy("mVolumesLock")
     private final HashMap<String, String> mVolumeStates = Maps.newHashMap();
 
     private volatile boolean mSystemReady = false;
@@ -198,8 +200,8 @@
     // Used as a lock for methods that register/unregister listeners.
     final private ArrayList<MountServiceBinderListener> mListeners =
             new ArrayList<MountServiceBinderListener>();
-    private CountDownLatch                        mConnectedSignal = new CountDownLatch(1);
-    private CountDownLatch                        mAsecsScanned = new CountDownLatch(1);
+    private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
+    private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
     private boolean                               mSendUmsConnectedOnBoot = false;
 
     /**
@@ -495,10 +497,6 @@
     }
 
     private void waitForLatch(CountDownLatch latch) {
-        if (latch == null) {
-            return;
-        }
-
         for (;;) {
             try {
                 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
@@ -738,14 +736,12 @@
                  * the hounds!
                  */
                 mConnectedSignal.countDown();
-                mConnectedSignal = null;
 
                 // Let package manager load internal ASECs.
                 mPms.scanAvailableAsecs();
 
                 // Notify people waiting for ASECs to be scanned that it's done.
                 mAsecsScanned.countDown();
-                mAsecsScanned = null;
             }
         }.start();
     }
@@ -2571,7 +2567,7 @@
         }
     }
 
-    // @VisibleForTesting
+    @VisibleForTesting
     public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
         // TODO: allow caller to provide Environment for full testing
 
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 92af9a9..5e94a9f 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -25,6 +25,7 @@
 import android.util.LocalLog;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.google.android.collect.Lists;
 
 import java.nio.charset.Charsets;
@@ -400,7 +401,7 @@
      * Append the given argument to {@link StringBuilder}, escaping as needed,
      * and surrounding with quotes when it contains spaces.
      */
-    // @VisibleForTesting
+    @VisibleForTesting
     static void appendEscaped(StringBuilder builder, String arg) {
         final boolean hasSpaces = arg.indexOf(' ') >= 0;
         if (hasSpaces) {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 70d37bf..e2be577 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -1077,16 +1077,27 @@
 
                 final AudioManager audioManager = (AudioManager) mContext
                 .getSystemService(Context.AUDIO_SERVICE);
+
                 // sound
                 final boolean useDefaultSound =
                     (notification.defaults & Notification.DEFAULT_SOUND) != 0;
-                if (useDefaultSound || notification.sound != null) {
-                    Uri uri;
-                    if (useDefaultSound) {
-                        uri = Settings.System.DEFAULT_NOTIFICATION_URI;
-                    } else {
-                        uri = notification.sound;
-                    }
+
+                Uri soundUri = null;
+                boolean hasValidSound = false;
+
+                if (useDefaultSound) {
+                    soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
+
+                    // check to see if the default notification sound is silent
+                    ContentResolver resolver = mContext.getContentResolver();
+                    hasValidSound = Settings.System.getString(resolver,
+                           Settings.System.NOTIFICATION_SOUND) != null;
+                } else if (notification.sound != null) {
+                    soundUri = notification.sound;
+                    hasValidSound = (soundUri != null);
+                }
+
+                if (hasValidSound) {
                     boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0;
                     int audioStreamType;
                     if (notification.audioStreamType >= 0) {
@@ -1103,7 +1114,7 @@
                         try {
                             final IRingtonePlayer player = mAudioService.getRingtonePlayer();
                             if (player != null) {
-                                player.playAsync(uri, user, looping, audioStreamType);
+                                player.playAsync(soundUri, user, looping, audioStreamType);
                             }
                         } catch (RemoteException e) {
                         } finally {
@@ -1117,13 +1128,13 @@
                 final boolean hasCustomVibrate = notification.vibrate != null;
 
                 // new in 4.2: if there was supposed to be a sound and we're in vibrate mode,
-                // and no other vibration is specified, we apply the default vibration anyway
+                // and no other vibration is specified, we fall back to vibration
                 final boolean convertSoundToVibration =
                            !hasCustomVibrate
-                        && (useDefaultSound || notification.sound != null)
+                        && hasValidSound
                         && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
 
-                // The DEFAULT_VIBRATE flag trumps any custom vibration.
+                // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
                 final boolean useDefaultVibrate =
                         (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
 
@@ -1136,8 +1147,8 @@
                         // does not have the VIBRATE permission.
                         long identity = Binder.clearCallingIdentity();
                         try {
-                            mVibrator.vibrate(convertSoundToVibration ? mFallbackVibrationPattern
-                                                                      : mDefaultVibrationPattern,
+                            mVibrator.vibrate(useDefaultVibrate ? mDefaultVibrationPattern
+                                                                : mFallbackVibrationPattern,
                                 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
                         } finally {
                             Binder.restoreCallingIdentity(identity);
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 35999ea..5c24e67 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -1090,11 +1090,8 @@
 
         boolean created = false;
         try {
-            mAm.mStringBuilder.setLength(0);
-            r.intent.getIntent().toShortString(mAm.mStringBuilder, true, false, true, false);
-            EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE,
-                    r.userId, System.identityHashCode(r), r.shortName,
-                    mAm.mStringBuilder.toString(), r.app.pid);
+            EventLogTags.writeAmCreateService(
+                    r.userId, System.identityHashCode(r), r.shortName, r.app.pid);
             synchronized (r.stats.getBatteryStats()) {
                 r.stats.startLaunchedLocked();
             }
@@ -1242,9 +1239,8 @@
         }
 
         if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent);
-        EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE,
-                r.userId, System.identityHashCode(r), r.shortName,
-                (r.app != null) ? r.app.pid : -1);
+        EventLogTags.writeAmDestroyService(
+                r.userId, System.identityHashCode(r), (r.app != null) ? r.app.pid : -1);
 
         mServiceMap.removeServiceByName(r.name, r.userId);
         mServiceMap.removeServiceByIntent(r.intent, r.userId);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index d2cd646..db64a9a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -14229,6 +14229,7 @@
                     startHomeActivityLocked(userId);
                 }
 
+                EventLogTags.writeAmSwitchUser(userId);
                 getUserManagerLocked().userForeground(userId);
                 sendUserSwitchBroadcastsLocked(oldUserId, userId);
                 if (needStart) {
diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags
index 6ee7507..f784861 100644
--- a/services/java/com/android/server/am/EventLogTags.logtags
+++ b/services/java/com/android/server/am/EventLogTags.logtags
@@ -63,9 +63,9 @@
 30024 am_broadcast_discard_filter (User|1|5),(Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5)
 30025 am_broadcast_discard_app (User|1|5),(Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3)
 # A service is being created
-30030 am_create_service (User|1|5),(Service Record|1|5),(Name|3),(Intent|3),(PID|1|5)
+30030 am_create_service (User|1|5),(Service Record|1|5),(Name|3),(PID|1|5)
 # A service is being destroyed
-30031 am_destroy_service (User|1|5),(Service Record|1|5),(Name|3),(PID|1|5)
+30031 am_destroy_service (User|1|5),(Service Record|1|5),(PID|1|5)
 # A process has crashed too many times, it is being cleared
 30032 am_process_crashed_too_much (User|1|5),(Name|3),(PID|1|5)
 # An unknown process is trying to attach to the activity manager
@@ -83,3 +83,6 @@
 30039 am_crash (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5)
 # Log.wtf() called
 30040 am_wtf (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Tag|3),(Message|3)
+
+# User switched
+30041 am_switch_user (id|1|5)
diff --git a/services/java/com/android/server/display/PersistentDataStore.java b/services/java/com/android/server/display/PersistentDataStore.java
index 3a6e1a6..105c253 100644
--- a/services/java/com/android/server/display/PersistentDataStore.java
+++ b/services/java/com/android/server/display/PersistentDataStore.java
@@ -81,6 +81,15 @@
         }
     }
 
+    public WifiDisplay getRememberedWifiDisplay(String deviceAddress) {
+        loadIfNeeded();
+        int index = findRememberedWifiDisplay(deviceAddress);
+        if (index >= 0) {
+            return mRememberedWifiDisplays.get(index);
+        }
+        return null;
+    }
+
     public WifiDisplay[] getRememberedWifiDisplays() {
         loadIfNeeded();
         return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]);
@@ -137,22 +146,6 @@
         return true;
     }
 
-    public boolean renameWifiDisplay(String deviceAddress, String alias) {
-        int index = findRememberedWifiDisplay(deviceAddress);
-        if (index >= 0) {
-            WifiDisplay display = mRememberedWifiDisplays.get(index);
-            if (Objects.equal(display.getDeviceAlias(), alias)) {
-                return false; // already has this alias
-            }
-            WifiDisplay renamedDisplay = new WifiDisplay(deviceAddress,
-                    display.getDeviceName(), alias);
-            mRememberedWifiDisplays.set(index, renamedDisplay);
-            setDirty();
-            return true;
-        }
-        return false;
-    }
-
     public boolean forgetWifiDisplay(String deviceAddress) {
         int index = findRememberedWifiDisplay(deviceAddress);
         if (index >= 0) {
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index 45fff30..c8a44d2 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -45,6 +45,8 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 
+import libcore.util.Objects;
+
 /**
  * Connects to Wifi displays that implement the Miracast protocol.
  * <p>
@@ -224,16 +226,18 @@
             }
         }
 
-        if (mPersistentDataStore.renameWifiDisplay(address, alias)) {
-            mPersistentDataStore.saveIfNeeded();
-            updateRememberedDisplaysLocked();
-            scheduleStatusChangedBroadcastLocked();
+        WifiDisplay display = mPersistentDataStore.getRememberedWifiDisplay(address);
+        if (display != null && !Objects.equal(display.getDeviceAlias(), alias)) {
+            display = new WifiDisplay(address, display.getDeviceName(), alias);
+            if (mPersistentDataStore.rememberWifiDisplay(display)) {
+                mPersistentDataStore.saveIfNeeded();
+                updateRememberedDisplaysLocked();
+                scheduleStatusChangedBroadcastLocked();
+            }
         }
 
-        if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)
-                && mDisplayDevice != null) {
-            mDisplayDevice.setNameLocked(mActiveDisplay.getFriendlyDisplayName());
-            sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED);
+        if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) {
+            renameDisplayDeviceLocked(mActiveDisplay.getFriendlyDisplayName());
         }
     }
 
@@ -272,9 +276,42 @@
         mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays);
     }
 
-    private void handleConnectLocked(WifiDisplay display,
+    private void fixRememberedDisplayNamesFromAvailableDisplaysLocked() {
+        // It may happen that a display name has changed since it was remembered.
+        // Consult the list of available displays and update the name if needed.
+        // We don't do anything special for the active display here.  The display
+        // controller will send a separate event when it needs to be updates.
+        boolean changed = false;
+        for (int i = 0; i < mRememberedDisplays.length; i++) {
+            WifiDisplay rememberedDisplay = mRememberedDisplays[i];
+            WifiDisplay availableDisplay = findAvailableDisplayLocked(
+                    rememberedDisplay.getDeviceAddress());
+            if (availableDisplay != null && !rememberedDisplay.equals(availableDisplay)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "fixRememberedDisplayNamesFromAvailableDisplaysLocked: "
+                            + "updating remembered display to " + availableDisplay);
+                }
+                mRememberedDisplays[i] = availableDisplay;
+                changed |= mPersistentDataStore.rememberWifiDisplay(availableDisplay);
+            }
+        }
+        if (changed) {
+            mPersistentDataStore.saveIfNeeded();
+        }
+    }
+
+    private WifiDisplay findAvailableDisplayLocked(String address) {
+        for (WifiDisplay display : mAvailableDisplays) {
+            if (display.getDeviceAddress().equals(address)) {
+                return display;
+            }
+        }
+        return null;
+    }
+
+    private void addDisplayDeviceLocked(WifiDisplay display,
             Surface surface, int width, int height, int flags) {
-        handleDisconnectLocked();
+        removeDisplayDeviceLocked();
 
         if (mPersistentDataStore.rememberWifiDisplay(display)) {
             mPersistentDataStore.saveIfNeeded();
@@ -303,7 +340,7 @@
         scheduleUpdateNotificationLocked();
     }
 
-    private void handleDisconnectLocked() {
+    private void removeDisplayDeviceLocked() {
         if (mDisplayDevice != null) {
             mDisplayDevice.clearSurfaceLocked();
             sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
@@ -313,6 +350,13 @@
         }
     }
 
+    private void renameDisplayDeviceLocked(String name) {
+        if (mDisplayDevice != null && !mDisplayDevice.getNameLocked().equals(name)) {
+            mDisplayDevice.setNameLocked(name);
+            sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED);
+        }
+    }
+
     private void scheduleStatusChangedBroadcastLocked() {
         mCurrentStatus = null;
         if (!mPendingStatusChangeBroadcast) {
@@ -446,6 +490,7 @@
                         || !Arrays.equals(mAvailableDisplays, availableDisplays)) {
                     mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING;
                     mAvailableDisplays = availableDisplays;
+                    fixRememberedDisplayNamesFromAvailableDisplaysLocked();
                     scheduleStatusChangedBroadcastLocked();
                 }
             }
@@ -483,7 +528,7 @@
                 int width, int height, int flags) {
             synchronized (getSyncRoot()) {
                 display = mPersistentDataStore.applyWifiDisplayAlias(display);
-                handleConnectLocked(display, surface, width, height, flags);
+                addDisplayDeviceLocked(display, surface, width, height, flags);
 
                 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED
                         || mActiveDisplay == null
@@ -496,10 +541,24 @@
         }
 
         @Override
+        public void onDisplayChanged(WifiDisplay display) {
+            synchronized (getSyncRoot()) {
+                display = mPersistentDataStore.applyWifiDisplayAlias(display);
+                if (mActiveDisplay != null
+                        && mActiveDisplay.hasSameAddress(display)
+                        && !mActiveDisplay.equals(display)) {
+                    mActiveDisplay = display;
+                    renameDisplayDeviceLocked(display.getFriendlyDisplayName());
+                    scheduleStatusChangedBroadcastLocked();
+                }
+            }
+        }
+
+        @Override
         public void onDisplayDisconnected() {
             // Stop listening.
             synchronized (getSyncRoot()) {
-                handleDisconnectLocked();
+                removeDisplayDeviceLocked();
 
                 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED
                         || mActiveDisplay != null) {
diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java
index 39d042f..886e049 100644
--- a/services/java/com/android/server/display/WifiDisplayController.java
+++ b/services/java/com/android/server/display/WifiDisplayController.java
@@ -120,6 +120,12 @@
     // or are not trying to connect.
     private WifiP2pDevice mConnectingDevice;
 
+    // The device from which we are currently disconnecting.
+    private WifiP2pDevice mDisconnectingDevice;
+
+    // The device to which we were previously trying to connect and are now canceling.
+    private WifiP2pDevice mCancelingDevice;
+
     // The device to which we are currently connected, which means we have an active P2P group.
     private WifiP2pDevice mConnectedDevice;
 
@@ -186,6 +192,7 @@
         updateWfdEnableState();
     }
 
+    @Override
     public void dump(PrintWriter pw) {
         pw.println("mWifiDisplayOnSetting=" + mWifiDisplayOnSetting);
         pw.println("mWifiP2pEnabled=" + mWifiP2pEnabled);
@@ -196,6 +203,8 @@
         pw.println("mDiscoverPeersRetriesLeft=" + mDiscoverPeersRetriesLeft);
         pw.println("mDesiredDevice=" + describeWifiP2pDevice(mDesiredDevice));
         pw.println("mConnectingDisplay=" + describeWifiP2pDevice(mConnectingDevice));
+        pw.println("mDisconnectingDisplay=" + describeWifiP2pDevice(mDisconnectingDevice));
+        pw.println("mCancelingDisplay=" + describeWifiP2pDevice(mCancelingDevice));
         pw.println("mConnectedDevice=" + describeWifiP2pDevice(mConnectedDevice));
         pw.println("mConnectionRetriesLeft=" + mConnectionRetriesLeft);
         pw.println("mRemoteDisplay=" + mRemoteDisplay);
@@ -384,7 +393,9 @@
         final int count = mAvailableWifiDisplayPeers.size();
         final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count);
         for (int i = 0; i < count; i++) {
-            displays[i] = createWifiDisplay(mAvailableWifiDisplayPeers.get(i));
+            WifiP2pDevice device = mAvailableWifiDisplayPeers.get(i);
+            displays[i] = createWifiDisplay(device);
+            updateDesiredDevice(device);
         }
 
         mHandler.post(new Runnable() {
@@ -395,6 +406,23 @@
         });
     }
 
+    private void updateDesiredDevice(WifiP2pDevice device) {
+        // Handle the case where the device to which we are connecting or connected
+        // may have been renamed or reported different properties in the latest scan.
+        final String address = device.deviceAddress;
+        if (mDesiredDevice != null && mDesiredDevice.deviceAddress.equals(address)) {
+            if (DEBUG) {
+                Slog.d(TAG, "updateDesiredDevice: new information "
+                        + describeWifiP2pDevice(device));
+            }
+            mDesiredDevice.update(device);
+            if (mAdvertisedDisplay != null
+                    && mAdvertisedDisplay.getDeviceAddress().equals(address)) {
+                readvertiseDisplay(createWifiDisplay(mDesiredDevice));
+            }
+        }
+    }
+
     private void connect(final WifiP2pDevice device) {
         if (mDesiredDevice != null
                 && !mDesiredDevice.deviceAddress.equals(device.deviceAddress)) {
@@ -459,12 +487,17 @@
         }
 
         // Step 2. Before we try to connect to a new device, disconnect from the old one.
+        if (mDisconnectingDevice != null) {
+            return; // wait for asynchronous callback
+        }
         if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) {
             Slog.i(TAG, "Disconnecting from Wifi display: " + mConnectedDevice.deviceName);
+            mDisconnectingDevice = mConnectedDevice;
+            mConnectedDevice = null;
 
             unadvertiseDisplay();
 
-            final WifiP2pDevice oldDevice = mConnectedDevice;
+            final WifiP2pDevice oldDevice = mDisconnectingDevice;
             mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() {
                 @Override
                 public void onSuccess() {
@@ -480,8 +513,8 @@
                 }
 
                 private void next() {
-                    if (mConnectedDevice == oldDevice) {
-                        mConnectedDevice = null;
+                    if (mDisconnectingDevice == oldDevice) {
+                        mDisconnectingDevice = null;
                         updateConnection();
                     }
                 }
@@ -491,13 +524,18 @@
 
         // Step 3. Before we try to connect to a new device, stop trying to connect
         // to the old one.
+        if (mCancelingDevice != null) {
+            return; // wait for asynchronous callback
+        }
         if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) {
             Slog.i(TAG, "Canceling connection to Wifi display: " + mConnectingDevice.deviceName);
+            mCancelingDevice = mConnectingDevice;
+            mConnectingDevice = null;
 
             unadvertiseDisplay();
             mHandler.removeCallbacks(mConnectionTimeout);
 
-            final WifiP2pDevice oldDevice = mConnectingDevice;
+            final WifiP2pDevice oldDevice = mCancelingDevice;
             mWifiP2pManager.cancelConnect(mWifiP2pChannel, new ActionListener() {
                 @Override
                 public void onSuccess() {
@@ -513,8 +551,8 @@
                 }
 
                 private void next() {
-                    if (mConnectingDevice == oldDevice) {
-                        mConnectingDevice = null;
+                    if (mCancelingDevice == oldDevice) {
+                        mCancelingDevice = null;
                         updateConnection();
                     }
                 }
@@ -763,13 +801,17 @@
                 public void run() {
                     if (oldSurface != null && surface != oldSurface) {
                         mListener.onDisplayDisconnected();
-                    } else if (oldDisplay != null && !Objects.equal(display, oldDisplay)) {
+                    } else if (oldDisplay != null && !oldDisplay.hasSameAddress(display)) {
                         mListener.onDisplayConnectionFailed();
                     }
 
                     if (display != null) {
-                        if (!Objects.equal(display, oldDisplay)) {
+                        if (!display.hasSameAddress(oldDisplay)) {
                             mListener.onDisplayConnecting(display);
+                        } else if (!display.equals(oldDisplay)) {
+                            // The address is the same but some other property such as the
+                            // name must have changed.
+                            mListener.onDisplayChanged(display);
                         }
                         if (surface != null && surface != oldSurface) {
                             mListener.onDisplayConnected(display, surface, width, height, flags);
@@ -784,6 +826,12 @@
         advertiseDisplay(null, null, 0, 0, 0);
     }
 
+    private void readvertiseDisplay(WifiDisplay display) {
+        advertiseDisplay(display, mAdvertisedDisplaySurface,
+                mAdvertisedDisplayWidth, mAdvertisedDisplayHeight,
+                mAdvertisedDisplayFlags);
+    }
+
     private static Inet4Address getInterfaceAddress(WifiP2pGroup info) {
         NetworkInterface iface;
         try {
@@ -885,6 +933,7 @@
 
         void onDisplayConnecting(WifiDisplay display);
         void onDisplayConnectionFailed();
+        void onDisplayChanged(WifiDisplay display);
         void onDisplayConnected(WifiDisplay display,
                 Surface surface, int width, int height, int flags);
         void onDisplayDisconnected();
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 43ddf8d..b839331 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -131,6 +131,7 @@
 import android.util.Xml;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Objects;
@@ -184,9 +185,11 @@
     private static final int VERSION_SWITCH_UID = 10;
     private static final int VERSION_LATEST = VERSION_SWITCH_UID;
 
-    // @VisibleForTesting
+    @VisibleForTesting
     public static final int TYPE_WARNING = 0x1;
+    @VisibleForTesting
     public static final int TYPE_LIMIT = 0x2;
+    @VisibleForTesting
     public static final int TYPE_LIMIT_SNOOZED = 0x3;
 
     private static final String TAG_POLICY_LIST = "policy-list";
@@ -214,10 +217,9 @@
 
     private static final String TAG_ALLOW_BACKGROUND = TAG + ":allowBackground";
 
-    // @VisibleForTesting
-    public static final String ACTION_ALLOW_BACKGROUND =
+    private static final String ACTION_ALLOW_BACKGROUND =
             "com.android.server.net.action.ALLOW_BACKGROUND";
-    public static final String ACTION_SNOOZE_WARNING =
+    private static final String ACTION_SNOOZE_WARNING =
             "com.android.server.net.action.SNOOZE_WARNING";
 
     private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
@@ -2063,7 +2065,7 @@
         return intent;
     }
 
-    // @VisibleForTesting
+    @VisibleForTesting
     public void addIdleHandler(IdleHandler handler) {
         mHandler.getLooper().getQueue().addIdleHandler(handler);
     }
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 0efdead..7101520 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -115,6 +115,7 @@
 import android.util.SparseIntArray;
 import android.util.TrustedTime;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FileRotator;
 import com.android.internal.util.IndentingPrintWriter;
@@ -165,7 +166,7 @@
 
     private IConnectivityManager mConnManager;
 
-    // @VisibleForTesting
+    @VisibleForTesting
     public static final String ACTION_NETWORK_STATS_POLL =
             "com.android.server.action.NETWORK_STATS_POLL";
     public static final String ACTION_NETWORK_STATS_UPDATED =
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index e05442b..dbfe34d 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -16,8 +16,7 @@
 
 package com.android.server.pm;
 
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastXmlSerializer;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -26,7 +25,6 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
@@ -34,6 +32,7 @@
 import android.os.Binder;
 import android.os.Environment;
 import android.os.FileUtils;
+import android.os.Handler;
 import android.os.IUserManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -42,9 +41,17 @@
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
@@ -54,13 +61,8 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 public class UserManagerService extends IUserManager.Stub {
 
     private static final String LOG_TAG = "UserManagerService";
@@ -86,7 +88,7 @@
 
     private static final int MIN_USER_ID = 10;
 
-    private static final int USER_VERSION = 1;
+    private static final int USER_VERSION = 2;
 
     private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
 
@@ -95,19 +97,24 @@
     private final Object mInstallLock;
     private final Object mPackagesLock;
 
+    private final Handler mHandler;
+
     private final File mUsersDir;
     private final File mUserListFile;
     private final File mBaseUserPath;
 
-    private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
-    private HashSet<Integer> mRemovingUserIds = new HashSet<Integer>();
+    private final SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
+
+    /**
+     * Set of user IDs being actively removed. Removed IDs linger in this set
+     * for several seconds to work around a VFS caching issue.
+     */
+    // @GuardedBy("mPackagesLock")
+    private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray();
 
     private int[] mUserIds;
     private boolean mGuestEnabled;
     private int mNextSerialNumber;
-    // This resets on a reboot. Otherwise it keeps incrementing so that user ids are
-    // not reused in quick succession
-    private int mNextUserId = MIN_USER_ID;
     private int mUserVersion = 0;
 
     private static UserManagerService sInstance;
@@ -147,6 +154,7 @@
         mPm = pm;
         mInstallLock = installLock;
         mPackagesLock = packagesLock;
+        mHandler = new Handler();
         synchronized (mInstallLock) {
             synchronized (mPackagesLock) {
                 mUsersDir = new File(dataDir, USER_INFO_DIR);
@@ -190,7 +198,7 @@
                 if (ui.partial) {
                     continue;
                 }
-                if (!excludeDying || !mRemovingUserIds.contains(ui.id)) {
+                if (!excludeDying || !mRemovingUserIds.get(ui.id)) {
                     users.add(ui);
                 }
             }
@@ -212,7 +220,7 @@
     private UserInfo getUserInfoLocked(int userId) {
         UserInfo ui = mUsers.get(userId);
         // If it is partial and not in the process of being removed, return as unknown user.
-        if (ui != null && ui.partial && !mRemovingUserIds.contains(userId)) {
+        if (ui != null && ui.partial && !mRemovingUserIds.get(userId)) {
             Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId);
             return null;
         }
@@ -476,8 +484,7 @@
     }
 
     /**
-     * This fixes an incorrect initialization of user name for the owner.
-     * TODO: Remove in the next release.
+     * Upgrade steps between versions, either for fixing bugs or changing the data format.
      */
     private void upgradeIfNecessary() {
         int userVersion = mUserVersion;
@@ -491,6 +498,16 @@
             userVersion = 1;
         }
 
+        if (userVersion < 2) {
+            // Owner should be marked as initialized
+            UserInfo user = mUsers.get(UserHandle.USER_OWNER);
+            if ((user.flags & UserInfo.FLAG_INITIALIZED) == 0) {
+                user.flags |= UserInfo.FLAG_INITIALIZED;
+                writeUserLocked(user);
+            }
+            userVersion = 2;
+        }
+
         if (userVersion < USER_VERSION) {
             Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
                     + USER_VERSION);
@@ -502,7 +519,7 @@
 
     private void fallbackToSingleUserLocked() {
         // Create the primary user
-        UserInfo primary = new UserInfo(0, 
+        UserInfo primary = new UserInfo(0,
                 mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
                 UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED);
         mUsers.put(0, primary);
@@ -749,7 +766,7 @@
             if (userHandle == 0 || user == null) {
                 return false;
             }
-            mRemovingUserIds.add(userHandle);
+            mRemovingUserIds.put(userHandle, true);
             // Set this to a partially created user, so that the user will be purged
             // on next startup, in case the runtime stops now before stopping and
             // removing the user completely.
@@ -813,13 +830,25 @@
         }
     }
 
-    private void removeUserStateLocked(int userHandle) {
+    private void removeUserStateLocked(final int userHandle) {
         // Cleanup package manager settings
         mPm.cleanUpUserLILPw(userHandle);
 
         // Remove this user from the list
         mUsers.remove(userHandle);
-        mRemovingUserIds.remove(userHandle);
+
+        // Have user ID linger for several seconds to let external storage VFS
+        // cache entries expire. This must be greater than the 'entry_valid'
+        // timeout used by the FUSE daemon.
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                synchronized (mPackagesLock) {
+                    mRemovingUserIds.delete(userHandle);
+                }
+            }
+        }, MINUTE_IN_MILLIS);
+
         // Remove user file
         AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
         userFile.delete();
@@ -906,14 +935,13 @@
      */
     private int getNextAvailableIdLocked() {
         synchronized (mPackagesLock) {
-            int i = mNextUserId;
+            int i = MIN_USER_ID;
             while (i < Integer.MAX_VALUE) {
-                if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.contains(i)) {
+                if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {
                     break;
                 }
                 i++;
             }
-            mNextUserId = i + 1;
             return i;
         }
     }
@@ -938,7 +966,7 @@
                 UserInfo user = mUsers.valueAt(i);
                 if (user == null) continue;
                 pw.print("  "); pw.print(user); pw.print(" serialNo="); pw.print(user.serialNumber);
-                if (mRemovingUserIds.contains(mUsers.keyAt(i))) pw.print(" <removing> ");
+                if (mRemovingUserIds.get(mUsers.keyAt(i))) pw.print(" <removing> ");
                 if (user.partial) pw.print(" <partial>");
                 pw.println();
                 pw.print("    Created: ");
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index f34a52d..c7c2c62 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -47,6 +47,8 @@
 import android.util.Pair;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
@@ -105,7 +107,7 @@
 
     private final Context mContext;
     private final ContentResolver mContentResolver;
-    // @GuardedBy("mLock")
+    @GuardedBy("mLock")
     private UsbSettingsManager mCurrentSettings;
     private NotificationManager mNotificationManager;
     private final boolean mHasUsbAccessory;
diff --git a/services/java/com/android/server/usb/UsbHostManager.java b/services/java/com/android/server/usb/UsbHostManager.java
index 175ae6f..10272f2 100644
--- a/services/java/com/android/server/usb/UsbHostManager.java
+++ b/services/java/com/android/server/usb/UsbHostManager.java
@@ -26,6 +26,8 @@
 import android.os.Parcelable;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.HashMap;
@@ -46,7 +48,7 @@
     private final Context mContext;
     private final Object mLock = new Object();
 
-    // @GuardedBy("mLock")
+    @GuardedBy("mLock")
     private UsbSettingsManager mCurrentSettings;
 
     public UsbHostManager(Context context) {
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index 629f5fa..3918d15 100644
--- a/services/java/com/android/server/usb/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -30,6 +30,7 @@
 import android.os.UserHandle;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.File;
@@ -52,7 +53,7 @@
     private final Object mLock = new Object();
 
     /** Map from {@link UserHandle} to {@link UsbSettingsManager} */
-    // @GuardedBy("mLock")
+    @GuardedBy("mLock")
     private final SparseArray<UsbSettingsManager>
             mSettingsByUser = new SparseArray<UsbSettingsManager>();
 
diff --git a/tests/AppLaunch/Android.mk b/tests/AppLaunch/Android.mk
new file mode 100644
index 0000000..c0560fd
--- /dev/null
+++ b/tests/AppLaunch/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := AppLaunch
+
+LOCAL_CERTIFICATE := platform
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/AppLaunch/AndroidManifest.xml b/tests/AppLaunch/AndroidManifest.xml
new file mode 100644
index 0000000..ac6760b
--- /dev/null
+++ b/tests/AppLaunch/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.applaunch"
+    android:sharedUserId="android.uid.system" >
+    <instrumentation android:label="Measure app start up time"
+                     android:name="android.test.InstrumentationTestRunner"
+                     android:targetPackage="com.android.tests.applaunch" />
+
+    <application android:label="App Launch Test">
+        <uses-library android:name="android.test.runner" />
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
new file mode 100644
index 0000000..e9374e0
--- /dev/null
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.applaunch;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessErrorStateInfo;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.test.InstrumentationTestCase;
+import android.test.InstrumentationTestRunner;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This test is intended to measure the time it takes for the apps to start.
+ * Names of the applications are passed in command line, and the
+ * test starts each application, and reports the start up time in milliseconds.
+ * The instrumentation expects the following key to be passed on the command line:
+ * apps - A list of applications to start and their corresponding result keys
+ * in the following format:
+ * -e apps <app name>^<result key>|<app name>^<result key>
+ */
+public class AppLaunch extends InstrumentationTestCase {
+
+    private static final int JOIN_TIMEOUT = 10000;
+    private static final String TAG = "AppLaunch";
+    private static final String KEY_APPS = "apps";
+
+    private Map<String, Intent> mNameToIntent;
+    private Map<String, String> mNameToProcess;
+    private Map<String, String> mNameToResultKey;
+
+    private IActivityManager mAm;
+
+    public void testMeasureStartUpTime() throws RemoteException {
+        InstrumentationTestRunner instrumentation =
+                (InstrumentationTestRunner)getInstrumentation();
+        Bundle args = instrumentation.getBundle();
+        mAm = ActivityManagerNative.getDefault();
+
+        createMappings();
+        parseArgs(args);
+
+        Bundle results = new Bundle();
+        for (String app : mNameToResultKey.keySet()) {
+            try {
+                startApp(app, results);
+                closeApp();
+            } catch (NameNotFoundException e) {
+                Log.i(TAG, "Application " + app + " not found");
+            }
+
+        }
+        instrumentation.sendStatus(0, results);
+    }
+
+    private void parseArgs(Bundle args) {
+        mNameToResultKey = new HashMap<String, String>();
+        String appList = args.getString(KEY_APPS);
+
+        if (appList == null)
+            return;
+
+        String appNames[] = appList.split("\\|");
+        for (String pair : appNames) {
+            String[] parts = pair.split("\\^");
+            if (parts.length != 2) {
+                Log.e(TAG, "The apps key is incorectly formatted");
+                fail();
+            }
+
+            mNameToResultKey.put(parts[0], parts[1]);
+        }
+    }
+
+    private void createMappings() {
+        mNameToIntent = new HashMap<String, Intent>();
+        mNameToProcess = new HashMap<String, String>();
+
+        PackageManager pm = getInstrumentation().getContext()
+                .getPackageManager();
+        Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
+        intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
+        List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0);
+        if (ris == null || ris.isEmpty()) {
+            Log.i(TAG, "Could not find any apps");
+        } else {
+            for (ResolveInfo ri : ris) {
+                Intent startIntent = new Intent(intentToResolve);
+                startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                startIntent.setClassName(ri.activityInfo.packageName,
+                        ri.activityInfo.name);
+                mNameToIntent.put(ri.loadLabel(pm).toString(), startIntent);
+                mNameToProcess.put(ri.loadLabel(pm).toString(),
+                        ri.activityInfo.processName);
+            }
+        }
+    }
+
+    private void startApp(String appName, Bundle results)
+            throws NameNotFoundException, RemoteException {
+        Log.i(TAG, "Starting " + appName);
+
+        Intent startIntent = mNameToIntent.get(appName);
+        AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent);
+        Thread t = new Thread(runnable);
+        long startTime = System.currentTimeMillis();
+        t.start();
+        try {
+            t.join(JOIN_TIMEOUT);
+        } catch (InterruptedException e) {
+            // ignore
+        }
+        if(t.isAlive() || (runnable.getResult() != null &&
+                runnable.getResult().result != ActivityManager.START_SUCCESS)) {
+            Log.w(TAG, "Assuming app " + appName + " crashed.");
+            reportError(appName, mNameToProcess.get(appName), results);
+            return;
+        }
+        long startUpTime = System.currentTimeMillis() - startTime;
+        results.putString(mNameToResultKey.get(appName), String.valueOf(startUpTime));
+        sleep(5000);
+    }
+
+    private void closeApp() {
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+        homeIntent.addCategory(Intent.CATEGORY_HOME);
+        homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        getInstrumentation().getContext().startActivity(homeIntent);
+        sleep(3000);
+    }
+
+    private void sleep(int time) {
+        try {
+            Thread.sleep(time);
+        } catch (InterruptedException e) {
+            // ignore
+        }
+    }
+
+    private void reportError(String appName, String processName, Bundle results) {
+        ActivityManager am = (ActivityManager) getInstrumentation()
+                .getContext().getSystemService(Context.ACTIVITY_SERVICE);
+        List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState();
+        if (crashes != null) {
+            for (ProcessErrorStateInfo crash : crashes) {
+                if (!crash.processName.equals(processName))
+                    continue;
+
+                Log.w(TAG, appName + " crashed: " + crash.shortMsg);
+                results.putString(mNameToResultKey.get(appName), crash.shortMsg);
+                return;
+            }
+        }
+
+        results.putString(mNameToResultKey.get(appName),
+                "Crashed for unknown reason");
+        Log.w(TAG, appName
+                + " not found in process list, most likely it is crashed");
+    }
+
+    private class AppLaunchRunnable implements Runnable {
+        private Intent mLaunchIntent;
+        private IActivityManager.WaitResult mResult;
+        public AppLaunchRunnable(Intent intent) {
+            mLaunchIntent = intent;
+        }
+
+        public IActivityManager.WaitResult getResult() {
+            return mResult;
+        }
+
+        public void run() {
+            try {
+                String mimeType = mLaunchIntent.getType();
+                if (mimeType == null && mLaunchIntent.getData() != null
+                        && "content".equals(mLaunchIntent.getData().getScheme())) {
+                    mimeType = mAm.getProviderMimeType(mLaunchIntent.getData(),
+                            UserHandle.USER_CURRENT);
+                }
+
+                mResult = mAm.startActivityAndWait(null, mLaunchIntent, mimeType,
+                        null, null, 0, mLaunchIntent.getFlags(), null, null, null,
+                        UserHandle.USER_CURRENT);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error launching app", e);
+            }
+        }
+    }
+}
diff --git a/tests/MemoryUsage/Android.mk b/tests/MemoryUsage/Android.mk
index e7bfb4f..0ab793b 100644
--- a/tests/MemoryUsage/Android.mk
+++ b/tests/MemoryUsage/Android.mk
@@ -8,7 +8,8 @@
 
 LOCAL_PACKAGE_NAME := MemoryUsage
 
-LOCAL_SDK_VERSION := 7
+LOCAL_CERTIFICATE := platform
+LOCAL_JAVA_LIBRARIES := android.test.runner
 
 include $(BUILD_PACKAGE)
 
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
index 5e27ba7..b550957 100644
--- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -18,14 +18,17 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.ProcessErrorStateInfo;
 import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
 import android.os.Debug.MemoryInfo;
+import android.os.RemoteException;
+import android.os.UserHandle;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
 
@@ -48,8 +51,9 @@
 
     private static final int SLEEP_TIME = 1000;
     private static final int THRESHOLD = 1024;
-    private static final int MAX_ITERATIONS = 10;
-    private static final int MIN_ITERATIONS = 4;
+    private static final int MAX_ITERATIONS = 20;
+    private static final int MIN_ITERATIONS = 6;
+    private static final int JOIN_TIMEOUT = 10000;
 
     private static final String TAG = "MemoryUsageInstrumentation";
     private static final String KEY_APPS = "apps";
@@ -58,10 +62,13 @@
     private Map<String, String> mNameToProcess;
     private Map<String, String> mNameToResultKey;
 
+    private IActivityManager mAm;
+
     public void testMemory() {
         MemoryUsageInstrumentation instrumentation =
-                    (MemoryUsageInstrumentation) getInstrumentation();
+                (MemoryUsageInstrumentation) getInstrumentation();
         Bundle args = instrumentation.getBundle();
+        mAm = ActivityManagerNative.getDefault();
 
         createMappings();
         parseArgs(args);
@@ -136,7 +143,16 @@
 
         String process = mNameToProcess.get(appName);
         Intent startIntent = mNameToIntent.get(appName);
-        getInstrumentation().getContext().startActivity(startIntent);
+
+        AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent);
+        Thread t = new Thread(runnable);
+        t.start();
+        try {
+            t.join(JOIN_TIMEOUT);
+        } catch (InterruptedException e) {
+            // ignore
+        }
+
         return process;
     }
 
@@ -234,7 +250,7 @@
             }
 
             int[] pids = {
-                proc.pid };
+                    proc.pid };
 
             MemoryInfo meminfo = am.getProcessMemoryInfo(pids)[0];
             return meminfo.getTotalPss();
@@ -242,4 +258,29 @@
         }
         return -1;
     }
+
+    private class AppLaunchRunnable implements Runnable {
+        private Intent mLaunchIntent;
+
+        public AppLaunchRunnable(Intent intent) {
+            mLaunchIntent = intent;
+        }
+
+        public void run() {
+            try {
+                String mimeType = mLaunchIntent.getType();
+                if (mimeType == null && mLaunchIntent.getData() != null
+                        && "content".equals(mLaunchIntent.getData().getScheme())) {
+                    mimeType = mAm.getProviderMimeType(mLaunchIntent.getData(),
+                            UserHandle.USER_CURRENT);
+                }
+
+                mAm.startActivityAndWait(null, mLaunchIntent, mimeType,
+                        null, null, 0, mLaunchIntent.getFlags(), null, null, null,
+                        UserHandle.USER_CURRENT_OR_SELF);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error launching app", e);
+            }
+        }
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index b871cdc..0e29882 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -508,6 +508,10 @@
     private Messenger mWifiServiceMessenger;
     private final CountDownLatch mConnected = new CountDownLatch(1);
 
+    private static Object sThreadRefLock = new Object();
+    private static int sThreadRefCount;
+    private static HandlerThread sHandlerThread;
+
     /**
      * Create a new WifiManager instance.
      * Applications will almost always want to use
@@ -1365,9 +1369,14 @@
             return;
         }
 
-        HandlerThread t = new HandlerThread("WifiManager");
-        t.start();
-        mHandler = new ServiceHandler(t.getLooper());
+        synchronized (sThreadRefLock) {
+            if (++sThreadRefCount == 1) {
+                sHandlerThread = new HandlerThread("WifiManager");
+                sHandlerThread.start();
+            }
+        }
+
+        mHandler = new ServiceHandler(sHandlerThread.getLooper());
         mAsyncChannel.connect(mContext, mHandler, mWifiServiceMessenger);
         try {
             mConnected.await();
@@ -1983,8 +1992,10 @@
 
     protected void finalize() throws Throwable {
         try {
-            if (mHandler != null && mHandler.getLooper() != null) {
-                mHandler.getLooper().quit();
+            synchronized (sThreadRefLock) {
+                if (--sThreadRefCount == 0 && sHandlerThread != null) {
+                    sHandlerThread.getLooper().quit();
+                }
             }
         } finally {
             super.finalize();
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 4c5fc5d..5e25623 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -61,7 +61,7 @@
 
     /* Sends a kill signal to supplicant. To be used when we have lost connection
        or when the supplicant is hung */
-    public native static boolean killSupplicant();
+    public native static boolean killSupplicant(boolean p2pSupported);
 
     private native boolean connectToSupplicant(String iface);
 
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 040ff24..dafa8e8 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1944,6 +1944,7 @@
                 case CMD_STOP_DRIVER:
                 case CMD_DELAYED_STOP_DRIVER:
                 case CMD_DRIVER_START_TIMED_OUT:
+                case CMD_CAPTIVE_CHECK_COMPLETE:
                 case CMD_START_AP:
                 case CMD_START_AP_SUCCESS:
                 case CMD_START_AP_FAILURE:
@@ -2189,6 +2190,13 @@
                         loge("Unable to change interface settings: " + ie);
                     }
 
+                    /* Stop a running supplicant after a runtime restart
+                     * Avoids issues with drivers that do not handle interface down
+                     * on a running supplicant properly.
+                     */
+                    if (DBG) log("Kill any running supplicant");
+                    mWifiNative.killSupplicant(mP2pSupported);
+
                     if(mWifiNative.startSupplicant(mP2pSupported)) {
                         if (DBG) log("Supplicant start successful");
                         mWifiMonitor.startMonitoring();
@@ -2384,7 +2392,7 @@
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
                     if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
                         loge("Failed to setup control channel, restart supplicant");
-                        mWifiNative.killSupplicant();
+                        mWifiNative.killSupplicant(mP2pSupported);
                         transitionTo(mDriverLoadedState);
                         sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
                     } else {
@@ -2451,7 +2459,7 @@
                     break;
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant connection lost */
                     loge("Connection lost, restart supplicant");
-                    mWifiNative.killSupplicant();
+                    mWifiNative.killSupplicant(mP2pSupported);
                     mWifiNative.closeSupplicantConnection();
                     mNetworkInfo.setIsAvailable(false);
                     handleNetworkDisconnect();
@@ -2605,14 +2613,14 @@
                     /* Socket connection can be lost when we do a graceful shutdown
                      * or when the driver is hung. Ensure supplicant is stopped here.
                      */
-                    mWifiNative.killSupplicant();
+                    mWifiNative.killSupplicant(mP2pSupported);
                     mWifiNative.closeSupplicantConnection();
                     transitionTo(mDriverLoadedState);
                     break;
                 case CMD_STOP_SUPPLICANT_FAILED:
                     if (message.arg1 == mSupplicantStopFailureToken) {
                         loge("Timed out on a supplicant stop, kill and proceed");
-                        mWifiNative.killSupplicant();
+                        mWifiNative.killSupplicant(mP2pSupported);
                         mWifiNative.closeSupplicantConnection();
                         transitionTo(mDriverLoadedState);
                     }