Merge "Link to new app notification settings from the inspector." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index d4f2cc5..a51216e3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -479,6 +479,7 @@
     field public static final int dashWidth = 16843174; // 0x10101a6
     field public static final int data = 16842798; // 0x101002e
     field public static final int datePickerDialogTheme = 16843951; // 0x10104af
+    field public static final int datePickerMode = 16843958; // 0x10104b6
     field public static final int datePickerStyle = 16843612; // 0x101035c
     field public static final int dateTextAppearance = 16843593; // 0x1010349
     field public static final int dayOfWeekBackgroundColor = 16843926; // 0x1010496
@@ -1313,6 +1314,7 @@
     field public static final int tileModeX = 16843897; // 0x1010479
     field public static final int tileModeY = 16843898; // 0x101047a
     field public static final int timePickerDialogTheme = 16843936; // 0x10104a0
+    field public static final int timePickerMode = 16843959; // 0x10104b7
     field public static final int timePickerStyle = 16843935; // 0x101049f
     field public static final int timeZone = 16843724; // 0x10103cc
     field public static final int tint = 16843041; // 0x1010121
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 5684a7a..90b8b86 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -32,7 +32,6 @@
 import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.BadParcelableException;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -1900,8 +1899,7 @@
             mPriority = PRIORITY_DEFAULT;
             mPeople = new ArrayList<String>();
 
-            mColorUtil = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.L ?
-                    NotificationColorUtil.getInstance() : null;
+            mColorUtil = NotificationColorUtil.getInstance();
         }
 
         /**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 04e6227..3087506 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -864,15 +864,6 @@
     }
 
     /**
-     * Kept during L development to simplify updating unbundled apps.
-     * TODO: Remove after 2014-08-04
-     * @hide
-     */
-    public String getBadgedLabelForUser(String label, UserHandle user) {
-        return (String) getBadgedLabelForUser((CharSequence) label, user);
-    }
-
-    /**
      * If the target user is a managed profile of the calling user or the caller
      * is itself a managed profile, then this returns a drawable to use as a small
      * icon to include in a view to distinguish it from the original icon.
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 74c66c8..d0a2eab 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -84,6 +84,9 @@
 public class DatePicker extends FrameLayout {
     private static final String LOG_TAG = DatePicker.class.getSimpleName();
 
+    private static final int MODE_SPINNER = 1;
+    private static final int MODE_CALENDAR = 2;
+
     private final DatePickerDelegate mDelegate;
 
     /**
@@ -120,24 +123,28 @@
 
         final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
                 defStyleAttr, defStyleRes);
-        final boolean legacyMode = a.getBoolean(R.styleable.DatePicker_legacyMode, true);
+        int mode = a.getInt(R.styleable.DatePicker_datePickerMode, MODE_SPINNER);
         a.recycle();
 
-        if (legacyMode) {
-            mDelegate = createLegacyUIDelegate(context, attrs, defStyleAttr, defStyleRes);
-        } else {
-            mDelegate = createNewUIDelegate(context, attrs, defStyleAttr, defStyleRes);
+        switch (mode) {
+            case MODE_CALENDAR:
+                mDelegate = createCalendarUIDelegate(context, attrs, defStyleAttr, defStyleRes);
+                break;
+            case MODE_SPINNER:
+            default:
+                mDelegate = createSpinnerUIDelegate(context, attrs, defStyleAttr, defStyleRes);
+                break;
         }
     }
 
-    private DatePickerDelegate createLegacyUIDelegate(Context context, AttributeSet attrs,
+    private DatePickerDelegate createSpinnerUIDelegate(Context context, AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
-        return new LegacyDatePickerDelegate(this, context, attrs, defStyleAttr, defStyleRes);
+        return new DatePickerSpinnerDelegate(this, context, attrs, defStyleAttr, defStyleRes);
     }
 
-    private DatePickerDelegate createNewUIDelegate(Context context, AttributeSet attrs,
+    private DatePickerDelegate createCalendarUIDelegate(Context context, AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
-        return new android.widget.DatePickerDelegate(this, context, attrs, defStyleAttr,
+        return new DatePickerCalendarDelegate(this, context, attrs, defStyleAttr,
                 defStyleRes);
     }
 
@@ -454,7 +461,7 @@
     /**
      * A delegate implementing the basic DatePicker
      */
-    private static class LegacyDatePickerDelegate extends AbstractDatePickerDelegate {
+    private static class DatePickerSpinnerDelegate extends AbstractDatePickerDelegate {
 
         private static final String DATE_FORMAT = "MM/dd/yyyy";
 
@@ -500,7 +507,7 @@
 
         private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
 
-        LegacyDatePickerDelegate(DatePicker delegator, Context context, AttributeSet attrs,
+        DatePickerSpinnerDelegate(DatePicker delegator, Context context, AttributeSet attrs,
                 int defStyleAttr, int defStyleRes) {
             super(delegator, context);
 
diff --git a/core/java/android/widget/DatePickerDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
similarity index 99%
rename from core/java/android/widget/DatePickerDelegate.java
rename to core/java/android/widget/DatePickerCalendarDelegate.java
index ddc565d..c0c76ac 100644
--- a/core/java/android/widget/DatePickerDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -52,7 +52,7 @@
 /**
  * A delegate for picking up a date (day / month / year).
  */
-class DatePickerDelegate extends DatePicker.AbstractDatePickerDelegate implements
+class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate implements
         View.OnClickListener, DatePickerController {
 
     private static final int UNINITIALIZED = -1;
@@ -113,7 +113,7 @@
 
     private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>();
 
-    public DatePickerDelegate(DatePicker delegator, Context context, AttributeSet attrs,
+    public DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
         super(delegator, context);
 
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 026a8ee..c488666 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -45,6 +45,9 @@
  */
 @Widget
 public class TimePicker extends FrameLayout {
+    private static final int MODE_SPINNER = 1;
+    private static final int MODE_CLOCK = 2;
+
     private final TimePickerDelegate mDelegate;
 
     /**
@@ -77,15 +80,19 @@
 
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes);
-        final boolean legacyMode = a.getBoolean(R.styleable.TimePicker_legacyMode, true);
+        int mode = a.getInt(R.styleable.TimePicker_timePickerMode, MODE_SPINNER);
         a.recycle();
 
-        if (legacyMode) {
-            mDelegate = new LegacyTimePickerDelegate(
-                    this, context, attrs, defStyleAttr, defStyleRes);
-        } else {
-            mDelegate = new android.widget.TimePickerDelegate(
-                    this, context, attrs, defStyleAttr, defStyleRes);
+        switch (mode) {
+            case MODE_CLOCK:
+                mDelegate = new TimePickerSpinnerDelegate(
+                        this, context, attrs, defStyleAttr, defStyleRes);
+                break;
+            case MODE_SPINNER:
+            default:
+                mDelegate = new TimePickerClockDelegate(
+                        this, context, attrs, defStyleAttr, defStyleRes);
+                break;
         }
     }
 
diff --git a/core/java/android/widget/LegacyTimePickerDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
similarity index 98%
rename from core/java/android/widget/LegacyTimePickerDelegate.java
rename to core/java/android/widget/TimePickerClockDelegate.java
index 70db211..59ec6d3 100644
--- a/core/java/android/widget/LegacyTimePickerDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -44,7 +44,7 @@
 /**
  * A delegate implementing the basic TimePicker
  */
-class LegacyTimePickerDelegate extends TimePicker.AbstractTimePickerDelegate {
+class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
 
     private static final boolean DEFAULT_ENABLED_STATE = true;
 
@@ -100,8 +100,8 @@
                 }
             };
 
-    public LegacyTimePickerDelegate(TimePicker delegator, Context context, AttributeSet attrs,
-                                    int defStyleAttr, int defStyleRes) {
+    public TimePickerClockDelegate(TimePicker delegator, Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
         super(delegator, context);
 
         // process style attributes
diff --git a/core/java/android/widget/TimePickerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
similarity index 99%
rename from core/java/android/widget/TimePickerDelegate.java
rename to core/java/android/widget/TimePickerSpinnerDelegate.java
index c68619c..4e9a39f 100644
--- a/core/java/android/widget/TimePickerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -51,7 +51,7 @@
 /**
  * A view for selecting the time of day, in either 24 hour or AM/PM mode.
  */
-class TimePickerDelegate extends TimePicker.AbstractTimePickerDelegate implements
+class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate implements
         RadialTimePickerView.OnValueSelectedListener {
 
     private static final String TAG = "TimePickerDelegate";
@@ -119,8 +119,8 @@
 
     private Calendar mTempCalendar;
 
-    public TimePickerDelegate(TimePicker delegator, Context context, AttributeSet attrs,
-                              int defStyleAttr, int defStyleRes) {
+    public TimePickerSpinnerDelegate(TimePicker delegator, Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
         super(delegator, context);
 
         // process style attributes
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 2baa599..771690e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4260,8 +4260,6 @@
         <attr name="internalLayout" format="reference"  />
         <!-- @hide The layout of the legacy DatePicker. -->
         <attr name="legacyLayout" />
-        <!-- @hide Enables or disable the use of the legacy layout for the DatePicker. -->
-        <attr name="legacyMode" />
         <!-- The background color for the date selector 's day of week. -->
         <attr name="dayOfWeekBackgroundColor" format="color" />
         <!-- The text color for the date selector's day of week. -->
@@ -4288,6 +4286,15 @@
              if the text color does not explicitly have a color set for the
              selected state. -->
         <attr name="calendarSelectedTextColor" format="color" />
+        <!-- Defines the look of the widget. Prior to the L release, the only choice was
+             spinner. As of L, with the Material theme selected, the default layout is calendar,
+             but this attribute can be used to force spinner to be used instead. -->
+        <attr name="datePickerMode">
+            <!-- Date picker with spinner controls to select the date. -->
+            <enum name="spinner" value="1" />
+            <!-- Date picker with calendar to select the date. -->
+            <enum name="calendar" value="2" />
+        </attr>
     </declare-styleable>
 
     <declare-styleable name="TwoLineListItem">
@@ -4557,8 +4564,6 @@
     </declare-styleable>
 
     <declare-styleable name="TimePicker">
-        <!-- @hide Enables or disable the use of the legacy layout for the TimePicker. -->
-        <attr name="legacyMode" format="boolean" />
         <!-- @hide The layout of the legacy time picker. -->
         <attr name="legacyLayout" format="reference" />
         <!-- @hide The layout of the time picker. -->
@@ -4587,6 +4592,15 @@
         <attr name="amPmSelectedBackgroundColor" format="color" />
         <!-- The color for the hours/minutes selector of the TimePicker. -->
         <attr name="numbersSelectorColor" format="color" />
+        <!-- Defines the look of the widget. Prior to the L release, the only choice was
+             spinner. As of L, with the Material theme selected, the default layout is clock,
+             but this attribute can be used to force spinner to be used instead. -->
+        <attr name="timePickerMode">
+            <!-- Time picker with spinner controls to select the time. -->
+            <enum name="spinner" value="1" />
+            <!-- Time picker with clock face to select the time. -->
+            <enum name="clock" value="2" />
+        </attr>
     </declare-styleable>
 
     <!-- ========================= -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 08398f0..73aaafd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2562,4 +2562,7 @@
   <public type="raw" name="nodomain"/>
 
   <public type="attr" name="contentRatingSystemXml"/>
+
+  <public type="attr" name="datePickerMode"/>
+  <public type="attr" name="timePickerMode"/>
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 4a70952..a5cac6b 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -568,12 +568,12 @@
     </style>
 
     <style name="Widget.TimePicker">
-        <item name="legacyMode">true</item>
+        <item name="timePickerMode">spinner</item>
         <item name="legacyLayout">@layout/time_picker_legacy</item>
     </style>
 
     <style name="Widget.DatePicker">
-        <item name="legacyMode">true</item>
+        <item name="datePickerMode">spinner</item>
         <item name="legacyLayout">@layout/date_picker_legacy</item>
         <item name="calendarViewShown">false</item>
     </style>
diff --git a/core/res/res/values/styles_holo.xml b/core/res/res/values/styles_holo.xml
index d79e46d..2a54ccf 100644
--- a/core/res/res/values/styles_holo.xml
+++ b/core/res/res/values/styles_holo.xml
@@ -462,7 +462,7 @@
     </style>
 
     <style name="Widget.Holo.TimePicker" parent="Widget.TimePicker">
-        <item name="legacyMode">true</item>
+        <item name="timePickerMode">spinner</item>
         <item name="legacyLayout">@layout/time_picker_legacy_holo</item>
         <!-- Attributes for new-style TimePicker. -->
         <item name="internalLayout">@layout/time_picker_holo</item>
@@ -479,7 +479,7 @@
     </style>
 
     <style name="Widget.Holo.DatePicker" parent="Widget.DatePicker">
-        <item name="legacyMode">true</item>
+        <item name="datePickerMode">spinner</item>
         <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
         <item name="internalLayout">@layout/date_picker_holo</item>
         <item name="calendarViewShown">true</item>
@@ -886,7 +886,7 @@
     <style name="Widget.Holo.Light.NumberPicker" parent="Widget.Holo.NumberPicker" />
 
     <style name="Widget.Holo.Light.TimePicker" parent="Widget.TimePicker">
-        <item name="legacyMode">true</item>
+        <item name="timePickerMode">spinner</item>
         <item name="legacyLayout">@layout/time_picker_legacy_holo</item>
         <!-- Non-legacy styling -->
         <item name="internalLayout">@layout/time_picker_holo</item>
@@ -903,7 +903,7 @@
     </style>
 
     <style name="Widget.Holo.Light.DatePicker" parent="Widget.DatePicker">
-        <item name="legacyMode">true</item>
+        <item name="datePickerMode">spinner</item>
         <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
         <item name="internalLayout">@layout/date_picker_holo</item>
         <item name="calendarViewShown">true</item>
diff --git a/core/res/res/values/styles_leanback.xml b/core/res/res/values/styles_leanback.xml
index a899c4d..256ef00 100644
--- a/core/res/res/values/styles_leanback.xml
+++ b/core/res/res/values/styles_leanback.xml
@@ -23,12 +23,12 @@
     </style>
 
     <style name="Widget.Leanback.TimePicker" parent="Widget.Material.TimePicker">
-        <item name="legacyMode">true</item>
+        <item name="timePickerMode">spinner</item>
         <item name="legacyLayout">@layout/time_picker_legacy_leanback</item>
     </style>
 
     <style name="Widget.Leanback.DatePicker" parent="Widget.Material.DatePicker">
-        <item name="legacyMode">true</item>
+        <item name="datePickerMode">spinner</item>
     </style>
 
     <style name="Widget.Leanback.NumberPicker" parent="Widget.Material.NumberPicker">
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 2dc0438..97d4bf6 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -587,7 +587,7 @@
     </style>
 
     <style name="Widget.Material.TimePicker" parent="Widget.TimePicker">
-        <item name="legacyMode">false</item>
+        <item name="timePickerMode">clock</item>
         <item name="legacyLayout">@layout/time_picker_legacy_holo</item>
         <!-- Attributes for new-style TimePicker. -->
         <item name="internalLayout">@layout/time_picker_holo</item>
@@ -604,7 +604,7 @@
     </style>
 
     <style name="Widget.Material.DatePicker" parent="Widget.DatePicker">
-        <item name="legacyMode">false</item>
+        <item name="datePickerMode">calendar</item>
         <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
         <!-- Attributes for new-style DatePicker. -->
         <item name="internalLayout">@layout/date_picker_holo</item>
diff --git a/docs/html/training/articles/security-gms-provider.jd b/docs/html/training/articles/security-gms-provider.jd
new file mode 100644
index 0000000..0d3cf1e
--- /dev/null
+++ b/docs/html/training/articles/security-gms-provider.jd
@@ -0,0 +1,298 @@
+page.title=Updating Your Security Provider to Protect Against SSL Exploits
+page.tags="network","certificates"
+
+page.article=true
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>In this document</h2>
+<ol class="nolist">
+  <li><a href="#patching">Patching the Security Provider with
+      ProviderInstaller</a></li>
+  <li><a href="#example_sync">Patching Synchronously</a></li>
+  <li><a href="#example_async">Patching Asynchronously</a></li>
+
+</ol>
+
+
+<h2>See also</h2>
+<ul>
+  <li><a href="{@docRoot}google/play-services/">Google Play Services</a></li>
+  <li><a href="https://www.openssl.org/news/secadv_20140605.txt">OpenSSL
+   Security Advisory [05 Jun 2014]: SSL/TLS MITM vulnerability
+   (CVE-2014-0224)</a></li>
+  <li><a href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-0224">
+    Vulnerability Summary for CVE-2014-0224</a></li>
+</ul>
+</div>
+</div>
+
+
+<p> Android relies on a security {@link java.security.Provider Provider} to
+provide secure network communications. However, from time to time,
+vulnerabilities are found in the default security provider. To protect against
+these vulnerabilities, <a href="{@docRoot}google/play-services/">Google Play
+services</a> provides a way to automatically update a device's security provider
+to protect against known exploits. By calling Google Play services methods, your
+app can ensure that it's running on a device that has the latest updates to
+protect against known exploits.</p>
+
+<p>For example, a vulnerability was discovered in OpenSSL
+(<a href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-0224">CVE-2014-0224</a>)
+that can leave apps open to a "man-in-the-middle" attack that decrypts
+secure traffic without either side knowing. With Google Play services version
+5.0, a fix is available, but apps must ensure that this fix is installed. By
+using the Google Play services methods, your app can ensure that it's running
+on a device that's secured against that attack.</p>
+
+<p class="caution"><strong>Caution: </strong>Updating a device's security {@link
+java.security.Provider Provider} does <em>not</em> update {@link
+android.net.SSLCertificateSocketFactory
+android.net.SSLCertificateSocketFactory}. Rather than using this class, we
+encourage app developers to use high-level methods for interacting with
+cryptography. Most apps can use APIs like {@link
+javax.net.ssl.HttpsURLConnection}, {@link org.apache.http.client.HttpClient},
+and {@link android.net.http.AndroidHttpClient} without needing to set a custom
+{@link javax.net.ssl.TrustManager} or create an {@link
+android.net.SSLCertificateSocketFactory}.</p>
+
+<h2 id="patching">Patching the Security Provider with ProviderInstaller</h2>
+
+<p>To update a device's security provider, use the
+<a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html">{@code ProviderInstaller}</a>
+class. You can verify that the security provider is up-to-date (and update it,
+if necessary) by calling
+that class's <a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html##installIfNeeded(android.content.Context)">{@code installIfNeeded()}</a>
+(or <a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html#installIfNeededAsync(android.content.Context, com.google.android.gms.security.ProviderInstaller.ProviderInstallListener)">{@code installIfNeededAsync()}</a>)
+method.</p>
+
+<p>When you call <a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html##installIfNeeded(android.content.Context)">{@code installIfNeeded()}</a>, the
+<a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html">{@code ProviderInstaller}</a>
+does the following:</p>
+
+<ul>
+  <li>If the device's {@link java.security.Provider Provider} is successfully
+    updated (or is already up-to-date), the method returns normally.</li>
+  <li>If the device's Google Play services library is out of date, the method
+    throws
+    <a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesRepairableException.html">{@code GooglePlayServicesRepairableException}</a>.
+    The app can then catch this exception and show
+    the user an appropriate dialog box to update Google Play services.</li>
+    <li>If a non-recoverable error occurs, the method throws
+    <a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesNotAvailableException.html">{@code GooglePlayServicesNotAvailableException}</a>
+    to indicate that it is unable to update the {@link java.security.Provider
+    Provider}. The app can then catch the exception and choose an appropriate
+    course of action, such as displaying the standard
+    <a href="{@docRoot}reference/com/google/android/gms/common/SupportErrorDialogFragment.html">fix-it flow diagram</a>.</li>
+</ul>
+
+<p>The
+<a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html#installIfNeededAsync(android.content.Context, com.google.android.gms.security.ProviderInstaller.ProviderInstallListener)">{@code installIfNeededAsync()}</a>
+method behaves similarly, except that instead of
+throwing exceptions, it calls the appropriate callback method to indicate
+success or failure.</p>
+
+<p>If <a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html##installIfNeeded(android.content.Context)">{@code installIfNeeded()}</a>
+needs to install a new {@link java.security.Provider Provider}, this can take
+anywhere from 30-50 milliseconds (on more recent devices) to 350 ms (on older
+devices). If the security provider is already up-to-date, the method takes a
+negligible amount of time. To avoid affecting user experience:</p>
+
+<ul>
+  <li>Call
+  <a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html##installIfNeeded(android.content.Context)">{@code installIfNeeded()}</a>
+  from background networking threads immediately when the threads are loaded,
+  instead of waiting for the thread to try to use the network. (There's no harm
+  in calling the method multiple times, since it returns immediately if the
+  security provider doesn't need updating.)</li>
+
+  <li>If user experience will be affected by the thread blocking--for example,
+  if the call is from an activity in the UI thread--call the asynchronous
+  version of the method,
+  <a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html#installIfNeededAsync(android.content.Context, com.google.android.gms.security.ProviderInstaller.ProviderInstallListener)">{@code installIfNeededAsync()}</a>.
+  (Of course, if you do this, you need to wait for the operation to finish
+  before you attempt any secure communications. The
+  <a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html">{@code ProviderInstaller}</a>
+  calls your listener's <a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.ProviderInstallListener.html#onProviderInstalled()">{@code onProviderInstalled()}</a>
+  method to signal success.)</li>
+</ul>
+
+<p class="warning"><strong>Warning:</strong> If the
+<a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html">{@code ProviderInstaller}</a>
+is unable to install an updated {@link java.security.Provider Provider},
+your device's security provider might be vulnerable  to known exploits. Your app
+should behave as if all HTTP communication is unencrypted.</p>
+
+<p>Once the {@link java.security.Provider Provider} is updated, all calls to
+security APIs (including SSL APIs) are routed through it.
+(However, this does not apply to  {@link android.net.SSLCertificateSocketFactory
+android.net.SSLCertificateSocketFactory}, which remains vulnerable to such
+exploits as
+<a href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-0224">CVE-2014-0224</a>.)</p>
+
+<h2 id="example_sync">Patching Synchronously</h2>
+
+<p>The simplest way to patch the security provider is to call the synchronous
+method <a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html##installIfNeeded(android.content.Context)">{@code installIfNeeded()}</a>.
+This is appropriate if user experience won't be affected by the thread blocking
+while it waits for the operation to finish.</p>
+
+<p>For example, here's an implementation of a <a href="{@docRoot}training/sync-adapters">sync adapter</a> that updates the security provider. Since a sync
+adapter runs in the background, it's okay if the thread blocks while waiting
+for the security provider to be updated. The sync adapter calls
+<a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html##installIfNeeded(android.content.Context)">{@code installIfNeeded()}</a> to
+update the security provider. If the method returns normally, the sync adapter
+knows the security provider is up-to-date. If the method throws an exception,
+the sync adapter can take appropriate action (such as prompting the user to
+update Google Play services).</p>
+
+<pre>/**
+ * Sample sync adapter using {&#64;link ProviderInstaller}.
+ */
+public class SyncAdapter extends AbstractThreadedSyncAdapter {
+
+  ...
+
+  // This is called each time a sync is attempted; this is okay, since the
+  // overhead is negligible if the security provider is up-to-date.
+  &#64;Override
+  public void onPerformSync(Account account, Bundle extras, String authority,
+      ContentProviderClient provider, SyncResult syncResult) {
+    try {
+      ProviderInstaller.installIfNeeded(getContext());
+    } catch (GooglePlayServicesRepairableException e) {
+
+      // Indicates that Google Play services is out of date, disabled, etc.
+
+      // Prompt the user to install/update/enable Google Play services.
+      GooglePlayServicesUtil.showErrorNotification(
+          e.getConnectionStatusCode(), getContext());
+
+      // Notify the SyncManager that a soft error occurred.
+      syncResult.stats.numIOExceptions++;
+      return;
+
+    } catch (GooglePlayServicesNotAvailableException e) {
+      // Indicates a non-recoverable error; the ProviderInstaller is not able
+      // to install an up-to-date Provider.
+
+      // Notify the SyncManager that a hard error occurred.
+      syncResult.stats.numAuthExceptions++;
+      return;
+    }
+
+    // If this is reached, you know that the provider was already up-to-date,
+    // or was successfully updated.
+  }
+}</pre>
+
+<h2 id="example_async">Patching Asynchronously</h2>
+
+<p>Updating the security provider can take as much as 350 milliseconds (on
+older devices). If you're doing the update on a thread that directly affects
+user experience, such as the UI thread, you don't want to make a synchronous
+call to update the provider, since that can result in the app or device
+freezing until the operation finishes. Instead, you should use the asynchronous
+method
+<a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html#installIfNeededAsync(android.content.Context, com.google.android.gms.security.ProviderInstaller.ProviderInstallListener)">{@code installIfNeededAsync()}</a>.
+That method indicates its success or failure by calling callbacks.</p>
+
+<p>For example, here's some code that updates the security provider in an
+activity in the UI thread. The activity calls <a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.html#installIfNeededAsync(android.content.Context, com.google.android.gms.security.ProviderInstaller.ProviderInstallListener)">{@code installIfNeededAsync()}</a>
+to update the provider, and designates itself as the listener to receive success
+or failure notifications. If the security provider is up-to-date or is
+successfully updated, the activity's
+<a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.ProviderInstallListener.html#onProviderInstalled()">{@code onProviderInstalled()}</a>
+method is called, and the activity knows communication is secure. If the
+provider cannot be updated, the activity's
+<a href="{@docRoot}reference/com/google/android/gms/security/ProviderInstaller.ProviderInstallListener.html#onProviderInstallFailed(int, android.content.Intent)">{@code onProviderInstallFailed()}</a>
+method is called, and the activity can take appropriate action (such as
+prompting the user to update Google Play services).</p>
+
+<pre>/**
+ * Sample activity using {&#64;link ProviderInstaller}.
+ */
+public class MainActivity extends Activity
+    implements ProviderInstaller.ProviderInstallListener {
+
+  private static final int ERROR_DIALOG_REQUEST_CODE = 1;
+
+  private boolean mRetryProviderInstall;
+
+  //Update the security provider when the activity is created.
+  &#64;Override
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    ProviderInstaller.installIfNeededAsync(this, this);
+  }
+
+  /**
+   * This method is only called if the provider is successfully updated
+   * (or is already up-to-date).
+   */
+  &#64;Override
+  protected void onProviderInstalled() {
+    // Provider is up-to-date, app can make secure network calls.
+  }
+
+  /**
+   * This method is called if updating fails; the error code indicates
+   * whether the error is recoverable.
+   */
+  &#64;Override
+  protected void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {
+    if (GooglePlayServicesUtil.isUserRecoverableError(errorCode)) {
+      // Recoverable error. Show a dialog prompting the user to
+      // install/update/enable Google Play services.
+      GooglePlayServicesUtil.showErrorDialogFragment(
+          errorCode,
+          this,
+          ERROR_DIALOG_REQUEST_CODE,
+          new DialogInterface.OnCancelListener() {
+            &#64;Override
+            public void onCancel(DialogInterface dialog) {
+              // The user chose not to take the recovery action
+              onProviderInstallerNotAvailable();
+            }
+          });
+    } else {
+      // Google Play services is not available.
+      onProviderInstallerNotAvailable();
+    }
+  }
+
+  &#64;Override
+  protected void onActivityResult(int requestCode, int resultCode,
+      Intent data) {
+    super.onActivityResult(requestCode, resultCode, data);
+    if (requestCode == ERROR_DIALOG_REQUEST_CODE) {
+      // Adding a fragment via GooglePlayServicesUtil.showErrorDialogFragment
+      // before the instance state is restored throws an error. So instead,
+      // set a flag here, which will cause the fragment to delay until
+      // onPostResume.
+      mRetryProviderInstall = true;
+    }
+  }
+
+  /**
+   * On resume, check to see if we flagged that we need to reinstall the
+   * provider.
+   */
+  &#64;Override
+  protected void onPostResume() {
+    super.onPostResult();
+    if (mRetryProviderInstall) {
+      // We can now safely retry installation.
+      ProviderInstall.installIfNeededAsync(this, this);
+    }
+    mRetryProviderInstall = false;
+  }
+
+  private void onProviderInstallerNotAvailable() {
+    // This is reached if the provider cannot be updated for some reason.
+    // App should consider all HTTP communication to be vulnerable, and take
+    // appropriate action.
+  }
+}
+</pre>
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 4407d30..4dd57ab 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -1487,6 +1487,14 @@
           >Security with HTTPS and SSL</a>
       </li>
 
+      <li>
+        <a href="<?cs var:toroot ?>training/articles/security-gms-provider.html"
+           description=
+           "How to use and update Google Play services security provider, to
+           protect against SSL exploits."
+          >Updating Your Security Provider to Protect Against SSL Exploits</a>
+      </li>
+
       <li class="nav-section">
         <div class="nav-section-header">
           <a href="<?cs var:toroot ?>training/enterprise/index.html"
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 6e1b80a..b786f94 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -77,9 +77,9 @@
      * data.</p>
      *
      * @param width
-     *            The width in pixels of the Images that this reader will produce.
+     *            The default width in pixels of the Images that this reader will produce.
      * @param height
-     *            The height in pixels of the Images that this reader will produce.
+     *            The default height in pixels of the Images that this reader will produce.
      * @param format
      *            The format of the Image that this reader will produce. This
      *            must be one of the {@link android.graphics.ImageFormat} or
@@ -130,39 +130,43 @@
     }
 
     /**
-     * The width of each {@link Image}, in pixels.
+     * The default width of {@link Image Images}, in pixels.
      *
-     * <p>ImageReader guarantees that all Images acquired from ImageReader (for example, with
-     * {@link #acquireNextImage}) will have the same dimensions as specified in
-     * {@link #newInstance}.</p>
+     * <p>The width may be overridden by the producer sending buffers to this
+     * ImageReader's Surface. If so, the actual width of the images can be
+     * found using {@link Image#getWidth}.</p>
      *
-     * @return the width of an Image
+     * @return the expected width of an Image
      */
     public int getWidth() {
         return mWidth;
     }
 
     /**
-     * The height of each {@link Image}, in pixels.
+     * The default height of {@link Image Images}, in pixels.
      *
-     * <p>ImageReader guarantees that all Images acquired from ImageReader (for example, with
-     * {@link #acquireNextImage}) will have the same dimensions as specified in
-     * {@link #newInstance}.</p>
+     * <p>The height may be overridden by the producer sending buffers to this
+     * ImageReader's Surface. If so, the actual height of the images can be
+     * found using {@link Image#getHeight}.</p>
      *
-     * @return the height of an Image
+     * @return the expected height of an Image
      */
     public int getHeight() {
         return mHeight;
     }
 
     /**
-     * The {@link ImageFormat image format} of each Image.
+     * The default {@link ImageFormat image format} of {@link Image Images}.
      *
-     * <p>ImageReader guarantees that all {@link Image Images} acquired from ImageReader
-     *  (for example, with {@link #acquireNextImage}) will have the same format as specified in
-     * {@link #newInstance}.</p>
+     * <p>Some color formats may be overridden by the producer sending buffers to
+     * this ImageReader's Surface if the default color format allows. ImageReader
+     * guarantees that all {@link Image Images} acquired from ImageReader
+     * (for example, with {@link #acquireNextImage}) will have a "compatible"
+     * format to what was specified in {@link #newInstance}.
+     * As of now, each format is only compatible to itself.
+     * The actual format of the images can be found using {@link Image#getFormat}.</p>
      *
-     * @return the format of an Image
+     * @return the expected format of an Image
      *
      * @see ImageFormat
      */
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index fc2dc02..4eab9c7 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -50,12 +50,8 @@
         android:visibility="gone"
         />
 
-    <include
-        layout="@layout/keyguard_status_bar"
-        android:visibility="invisible" />
-
     <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
-        android:layout_width="@dimen/notification_panel_width"
+        android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="@integer/notification_panel_layout_gravity"
         android:id="@+id/notification_container_parent"
@@ -64,8 +60,9 @@
 
         <com.android.systemui.statusbar.phone.ObservableScrollView
             android:id="@+id/scroll_view"
-            android:layout_width="match_parent"
+            android:layout_width="@dimen/notification_panel_width"
             android:layout_height="match_parent"
+            android:layout_gravity="@integer/notification_panel_layout_gravity"
             android:scrollbars="none"
             android:overScrollMode="never"
             android:fillViewport="true">
@@ -97,8 +94,9 @@
 
         <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
             android:id="@+id/notification_stack_scroller"
-            android:layout_width="match_parent"
+            android:layout_width="@dimen/notification_panel_width"
             android:layout_height="match_parent"
+            android:layout_gravity="@integer/notification_panel_layout_gravity"
             android:layout_marginBottom="@dimen/close_handle_underlap"/>
 
         <ViewStub
@@ -109,6 +107,10 @@
             android:layout_gravity="end"
             android:layout="@layout/keyguard_user_switcher" />
 
+        <include
+            layout="@layout/keyguard_status_bar"
+            android:visibility="invisible" />
+
     </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
 
     <include layout="@layout/status_bar_expanded_header" />
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 2d82d6e..7cdc078 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -72,9 +72,6 @@
     <!-- Margin on the left side of the carrier text on Keyguard -->
     <dimen name="keyguard_carrier_text_margin">24dp</dimen>
 
-    <!-- end margin for system icons if multi user switch is hidden -->
-    <dimen name="system_icons_switcher_hidden_expanded_margin">20dp</dimen>
-
     <!-- The width/height of the phone/camera/unlock icon on keyguard. -->
     <dimen name="keyguard_affordance_height">80dp</dimen>
     <dimen name="keyguard_affordance_width">120dp</dimen>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 1cdcc2b..c3ea8f8 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -103,4 +103,6 @@
     <color name="notification_guts_btn_color">#FFFFFFFF</color>
 
     <color name="search_panel_card_color">#ffffff</color>
+
+    <color name="keyguard_user_switcher_background_gradient_color">#77000000</color>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 35b58d7..e70422b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -44,6 +44,7 @@
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.MirrorView;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
@@ -62,6 +63,7 @@
 
     private KeyguardAffordanceHelper mAfforanceHelper;
     private StatusBarHeaderView mHeader;
+    private KeyguardUserSwitcher mKeyguardUserSwitcher;
     private KeyguardStatusBarView mKeyguardStatusBar;
     private View mQsContainer;
     private QSPanel mQsPanel;
@@ -225,11 +227,18 @@
             mHeader.post(mUpdateHeader);
         }
 
-        lp = (FrameLayout.LayoutParams) mNotificationContainerParent.getLayoutParams();
+        lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams();
         if (lp.width != panelWidth) {
             lp.width = panelWidth;
             lp.gravity = panelGravity;
-            mNotificationContainerParent.setLayoutParams(lp);
+            mNotificationStackScroller.setLayoutParams(lp);
+        }
+
+        lp = (FrameLayout.LayoutParams) mScrollView.getLayoutParams();
+        if (lp.width != panelWidth) {
+            lp.width = panelWidth;
+            lp.gravity = panelGravity;
+            mScrollView.setLayoutParams(lp);
         }
     }
 
@@ -940,6 +949,9 @@
                 && !mStackScrollerOverscrolling && mQsScrimEnabled
                         ? View.VISIBLE
                         : View.INVISIBLE);
+        if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
+            mKeyguardUserSwitcher.hide();
+        }
     }
 
     private void setQsExpansion(float height) {
@@ -1705,6 +1717,10 @@
         }
     }
 
+    public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
+        mKeyguardUserSwitcher = keyguardUserSwitcher;
+    }
+
     private final Runnable mUpdateHeader = new Runnable() {
         @Override
         public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 7c6e47c..57b7401 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -35,6 +35,7 @@
     private View mScrollView;
     private View mUserSwitcher;
     private View mStackScroller;
+    private View mKeyguardStatusBar;
     private boolean mInflated;
 
     public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
@@ -46,6 +47,7 @@
         super.onFinishInflate();
         mScrollView = findViewById(R.id.scroll_view);
         mStackScroller = findViewById(R.id.notification_stack_scroller);
+        mKeyguardStatusBar = findViewById(R.id.keyguard_header);
         ViewStub userSwitcher = (ViewStub) findViewById(R.id.keyguard_user_switcher);
         userSwitcher.setOnInflateListener(this);
         mUserSwitcher = userSwitcher;
@@ -61,18 +63,30 @@
     @Override
     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
         boolean userSwitcherVisible = mInflated && mUserSwitcher.getVisibility() == View.VISIBLE;
+        boolean statusBarVisible = mKeyguardStatusBar.getVisibility() == View.VISIBLE;
 
         // Invert the order of the scroll view and user switcher such that the notifications receive
         // touches first but the panel gets drawn above.
         if (child == mScrollView) {
             return super.drawChild(canvas, mStackScroller, drawingTime);
         } else if (child == mStackScroller) {
-            return super.drawChild(canvas, userSwitcherVisible ? mUserSwitcher : mScrollView,
+            return super.drawChild(canvas,
+                    userSwitcherVisible && statusBarVisible ? mUserSwitcher
+                    : statusBarVisible ? mKeyguardStatusBar
+                    : userSwitcherVisible ? mUserSwitcher
+                    : mScrollView,
                     drawingTime);
         } else if (child == mUserSwitcher) {
-            return super.drawChild(canvas, userSwitcherVisible ? mScrollView : mUserSwitcher,
+            return super.drawChild(canvas,
+                    userSwitcherVisible && statusBarVisible ? mKeyguardStatusBar
+                    : mScrollView,
                     drawingTime);
-        } else {
+        } else if (child == mKeyguardStatusBar) {
+            return super.drawChild(canvas,
+                    userSwitcherVisible && statusBarVisible ? mScrollView
+                    : mScrollView,
+                    drawingTime);
+        }else {
             return super.drawChild(canvas, child, drawingTime);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index e8b167c..1d678af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -805,7 +805,7 @@
 
         mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
                 (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
-                mKeyguardStatusBar, mUserSwitcherController);
+                mKeyguardStatusBar, mNotificationPanel, mUserSwitcherController);
 
 
         // Set up the quick settings tile panel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index ffc6898..203196e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.database.DataSetObserver;
-import android.provider.Settings;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -28,6 +27,8 @@
 import com.android.systemui.R;
 import com.android.systemui.qs.tiles.UserDetailItemView;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.StatusBarHeaderView;
 import com.android.systemui.statusbar.phone.UserAvatarView;
 
 /**
@@ -44,11 +45,14 @@
     private final boolean mSimpleUserSwitcher;
 
     public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
-            KeyguardStatusBarView statusBarView, UserSwitcherController userSwitcherController) {
+            KeyguardStatusBarView statusBarView, NotificationPanelView panelView,
+            UserSwitcherController userSwitcherController) {
         if (context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON) {
             mUserSwitcher = (ViewGroup) userSwitcher.inflate();
+            mUserSwitcher.setBackground(new KeyguardUserSwitcherScrim(mUserSwitcher));
             mStatusBarView = statusBarView;
             mStatusBarView.setKeyguardUserSwitcher(this);
+            panelView.setKeyguardUserSwitcher(this);
             mAdapter = new Adapter(context, userSwitcherController);
             mAdapter.registerDataSetObserver(mDataSetObserver);
             mSimpleUserSwitcher = userSwitcherController.isSimpleUserSwitcher();
@@ -75,7 +79,7 @@
      * @see android.os.UserManager#isUserSwitcherEnabled()
      */
     private boolean shouldExpandByDefault() {
-        return mSimpleUserSwitcher || mAdapter.getSwitchableUsers() > 1;
+        return mSimpleUserSwitcher;
     }
 
     public void show() {
@@ -87,7 +91,7 @@
     }
 
     public void hide() {
-        if (mUserSwitcher != null) {
+        if (mUserSwitcher != null && mUserSwitcher.getVisibility() == View.VISIBLE) {
             // TODO: animate
             mUserSwitcher.setVisibility(View.GONE);
             mStatusBarView.setKeyguardUserSwitcherShowing(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
new file mode 100644
index 0000000..3356afd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar.policy;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.util.LayoutDirection;
+import android.view.View;
+
+import com.android.systemui.R;
+
+/**
+ * Gradient background for the user switcher on Keyguard.
+ */
+public class KeyguardUserSwitcherScrim extends Drawable
+        implements View.OnLayoutChangeListener {
+
+    private static final float OUTER_EXTENT = 2.5f;
+    private static final float INNER_EXTENT = 0.75f;
+
+    private int mDarkColor;
+    private int mTop;
+    private Paint mRadialGradientPaint = new Paint();
+
+    public KeyguardUserSwitcherScrim(View host) {
+        host.addOnLayoutChangeListener(this);
+        mDarkColor = host.getResources().getColor(
+                R.color.keyguard_user_switcher_background_gradient_color);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        boolean isLtr = getLayoutDirection() == LayoutDirection.LTR;
+        Rect bounds = getBounds();
+        float width = bounds.width() * OUTER_EXTENT;
+        float height = (mTop + bounds.height()) * OUTER_EXTENT;
+        canvas.translate(0, -mTop);
+        canvas.scale(1, height/width);
+        canvas.drawRect(isLtr ? bounds.right - width : 0, 0,
+                isLtr ? bounds.right : bounds.left + width, width, mRadialGradientPaint);
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
+            int oldTop, int oldRight, int oldBottom) {
+        if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
+            int width = right - left;
+            float radius = width * OUTER_EXTENT;
+            boolean isLtr = getLayoutDirection() == LayoutDirection.LTR;
+            mRadialGradientPaint.setShader(
+                    new RadialGradient(isLtr ? width : 0, 0, radius,
+                            new int[] { mDarkColor, Color.TRANSPARENT},
+                            new float[] { Math.max(0f, width * INNER_EXTENT / radius), 1f},
+                            Shader.TileMode.CLAMP));
+            mTop = top;
+        }
+    }
+}