Merge "Check if item is selectable before calling on click" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index adfb196..014ae35 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2668,6 +2668,8 @@
     field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
     field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
+    field public static final int SHOW_MODE_AUTO = 0; // 0x0
+    field public static final int SHOW_MODE_HIDDEN = 1; // 0x1
   }
 
   public static abstract class AccessibilityService.GestureResultCallback {
@@ -5608,6 +5610,8 @@
     method public void enableCarMode(int);
     method public int getCurrentModeType();
     method public int getNightMode();
+    method public boolean isNightModeLocked();
+    method public boolean isUiModeLocked();
     method public void setNightMode(int);
     field public static java.lang.String ACTION_ENTER_CAR_MODE;
     field public static java.lang.String ACTION_ENTER_DESK_MODE;
@@ -22965,7 +22969,7 @@
   }
 
   public static final class TvInputInfo.Builder {
-    ctor public TvInputInfo.Builder(android.content.Context, java.lang.Class<?>);
+    ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName);
     method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean);
     method public android.media.tv.TvInputInfo.Builder setTunerCount(int);
@@ -23033,7 +23037,7 @@
     ctor public TvInputService.RecordingSession(android.content.Context);
     method public void notifyError(int);
     method public void notifyRecordingStopped(android.net.Uri);
-    method public void notifyTuned();
+    method public void notifyTuned(android.net.Uri);
     method public abstract void onRelease();
     method public abstract void onStartRecording(android.net.Uri);
     method public abstract void onStopRecording();
@@ -23092,7 +23096,7 @@
     method public void onDisconnected(java.lang.String);
     method public void onError(int);
     method public void onRecordingStopped(android.net.Uri);
-    method public void onTuned();
+    method public void onTuned(android.net.Uri);
   }
 
   public final class TvTrackInfo implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 4fd0baa..fc252ce 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2770,6 +2770,8 @@
     field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
     field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
+    field public static final int SHOW_MODE_AUTO = 0; // 0x0
+    field public static final int SHOW_MODE_HIDDEN = 1; // 0x1
   }
 
   public static abstract class AccessibilityService.GestureResultCallback {
@@ -5740,6 +5742,8 @@
     method public void enableCarMode(int);
     method public int getCurrentModeType();
     method public int getNightMode();
+    method public boolean isNightModeLocked();
+    method public boolean isUiModeLocked();
     method public void setNightMode(int);
     field public static java.lang.String ACTION_ENTER_CAR_MODE;
     field public static java.lang.String ACTION_ENTER_DESK_MODE;
@@ -9924,6 +9928,7 @@
     method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
     method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
     method public android.graphics.drawable.Drawable loadLogo(android.content.pm.PackageManager);
+    method public java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager);
     method public android.graphics.drawable.Drawable loadUnbadgedIcon(android.content.pm.PackageManager);
     method public android.content.res.XmlResourceParser loadXmlMetaData(android.content.pm.PackageManager, java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
@@ -24633,7 +24638,7 @@
   }
 
   public static final class TvInputInfo.Builder {
-    ctor public TvInputInfo.Builder(android.content.Context, java.lang.Class<?>);
+    ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName);
     method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean);
     method public android.media.tv.TvInputInfo.Builder setHdmiDeviceInfo(android.hardware.hdmi.HdmiDeviceInfo);
@@ -24778,7 +24783,7 @@
     method public void notifyError(int);
     method public void notifyRecordingStopped(android.net.Uri);
     method public void notifySessionEvent(java.lang.String, android.os.Bundle);
-    method public void notifyTuned();
+    method public void notifyTuned(android.net.Uri);
     method public void onAppPrivateCommand(java.lang.String, android.os.Bundle);
     method public abstract void onRelease();
     method public abstract void onStartRecording(android.net.Uri);
@@ -24846,7 +24851,7 @@
     method public void onError(int);
     method public void onEvent(java.lang.String, java.lang.String, android.os.Bundle);
     method public void onRecordingStopped(android.net.Uri);
-    method public void onTuned();
+    method public void onTuned(android.net.Uri);
   }
 
   public class TvStreamConfig implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index 3b85f5c..580af72 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2668,6 +2668,8 @@
     field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
     field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
+    field public static final int SHOW_MODE_AUTO = 0; // 0x0
+    field public static final int SHOW_MODE_HIDDEN = 1; // 0x1
   }
 
   public static abstract class AccessibilityService.GestureResultCallback {
@@ -22976,7 +22978,7 @@
   }
 
   public static final class TvInputInfo.Builder {
-    ctor public TvInputInfo.Builder(android.content.Context, java.lang.Class<?>);
+    ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName);
     method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean);
     method public android.media.tv.TvInputInfo.Builder setTunerCount(int);
@@ -23044,7 +23046,7 @@
     ctor public TvInputService.RecordingSession(android.content.Context);
     method public void notifyError(int);
     method public void notifyRecordingStopped(android.net.Uri);
-    method public void notifyTuned();
+    method public void notifyTuned(android.net.Uri);
     method public abstract void onRelease();
     method public abstract void onStartRecording(android.net.Uri);
     method public abstract void onStopRecording();
@@ -23103,7 +23105,7 @@
     method public void onDisconnected(java.lang.String);
     method public void onError(int);
     method public void onRecordingStopped(android.net.Uri);
-    method public void onTuned();
+    method public void onTuned(android.net.Uri);
   }
 
   public final class TvTrackInfo implements android.os.Parcelable {
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index fb5f5b9..ac3b8e3 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -383,13 +383,7 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SHOW_MODE_AUTO, SHOW_MODE_HIDDEN})
     public @interface SoftKeyboardShowMode {};
-    /**
-     * @hide
-     */
     public static final int SHOW_MODE_AUTO = 0;
-    /**
-     * @hide
-     */
     public static final int SHOW_MODE_HIDDEN = 1;
 
     private int mConnectionId;
@@ -1137,7 +1131,7 @@
         }
 
         /**
-         * Removes all instances of the specified change listener from teh list of magnification
+         * Removes all instances of the specified change listener from the list of magnification
          * change listeners.
          *
          * @param listener the listener to remove, must be non-null
@@ -1216,14 +1210,11 @@
 
         /**
          * Returns the show mode of the soft keyboard. The default show mode is
-         * {@code Settings.Secure.SHOW_MODE_AUTO}, where the soft keyboard is shown when a text
-         * input field is focused. An AccessibilityService can also request the show mode
-         * {@code Settings.Secure.SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
+         * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
+         * focused. An AccessibilityService can also request the show mode
+         * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
          *
          * @return the current soft keyboard show mode
-         *
-         * @see Settings#Secure#SHOW_MODE_AUTO
-         * @see Settings#Secure#SHOW_MODE_HIDDEN
          */
         @SoftKeyboardShowMode
         public int getShowMode() {
@@ -1239,9 +1230,9 @@
 
         /**
          * Sets the soft keyboard show mode. The default show mode is
-         * {@code Settings.Secure.SHOW_MODE_AUTO}, where the soft keyboard is shown when a text
-         * input field is focused. An AccessibilityService can also request the show mode
-         * {@code Settings.Secure.SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. The
+         * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
+         * focused. An AccessibilityService can also request the show mode
+         * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. The
          * The lastto this method will be honored, regardless of any previous calls (including those
          * made by other AccessibilityServices).
          * <p>
@@ -1251,9 +1242,6 @@
          *
          * @param showMode the new show mode for the soft keyboard
          * @return {@code true} on success
-         *
-         * @see Settings#Secure#SHOW_MODE_AUTO
-         * @see Settings#Secure#SHOW_MODE_HIDDEN
          */
         public boolean setShowMode(@SoftKeyboardShowMode int showMode) {
            final IAccessibilityServiceConnection connection =
@@ -1263,9 +1251,13 @@
                try {
                    return connection.setSoftKeyboardShowMode(showMode);
                } catch (RemoteException re) {
-                   Log.w(LOG_TAG, "Falied to set soft keyboard behavior", re);
+                   Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re);
+                   re.rethrowFromSystemServer();
                }
+           } else {
+               throw new RuntimeException("AccessibilityServiceConnection is null");
            }
+
            return false;
         }
 
@@ -1275,9 +1267,9 @@
         public interface OnShowModeChangedListener {
            /**
             * Called when the soft keyboard behavior changes. The default show mode is
-            * {@code Settings.Secure.SHOW_MODE_AUTO}, where the soft keyboard is shown when a text
-            * input field is focused. An AccessibilityService can also request the show mode
-            * {@code Settings.Secure.SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
+            * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
+            * focused. An AccessibilityService can also request the show mode
+            * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
             *
             * @param controller the soft keyboard controller
             * @param showMode the current soft keyboard show mode
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 4b0dfc7..3a51aff 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -16,8 +16,6 @@
 
 package android.app;
 
-import com.android.internal.util.FastPrintWriter;
-
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -33,6 +31,8 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 
+import com.android.internal.util.FastPrintWriter;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -717,10 +717,12 @@
 
         bumpBackStackNesting(1);
 
-        SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
-        SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
-        calculateFragments(firstOutFragments, lastInFragments);
-        beginTransition(firstOutFragments, lastInFragments, false);
+        if (mManager.mCurState >= Fragment.CREATED) {
+            SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
+            SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
+            calculateFragments(firstOutFragments, lastInFragments);
+            beginTransition(firstOutFragments, lastInFragments, false);
+        }
 
         Op op = mHead;
         while (op != null) {
@@ -842,6 +844,14 @@
                     firstOutFragments.remove(containerId);
                 }
             }
+            /**
+             * Ensure that fragments that are entering are at least at the CREATED state
+             * so that they may load Transitions using TransitionInflater.
+             */
+            if (fragment.mState < Fragment.CREATED && mManager.mCurState >= Fragment.CREATED) {
+                mManager.makeActive(fragment);
+                mManager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
+            }
         }
     }
 
@@ -986,7 +996,6 @@
      */
     private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments,
             SparseArray<Fragment> lastInFragments, boolean isBack) {
-        ensureFragmentsAreInitialized(lastInFragments);
         TransitionState state = new TransitionState();
 
         // Adding a non-existent target view makes sure that the transitions don't target
@@ -1012,21 +1021,6 @@
         return state;
     }
 
-    /**
-     * Ensure that fragments that are entering are at least at the CREATED state
-     * so that they may load Transitions using TransitionInflater.
-     */
-    private void ensureFragmentsAreInitialized(SparseArray<Fragment> lastInFragments) {
-        final int count = lastInFragments.size();
-        for (int i = 0; i < count; i++) {
-            final Fragment fragment = lastInFragments.valueAt(i);
-            if (fragment.mState < Fragment.CREATED) {
-                mManager.makeActive(fragment);
-                mManager.moveToState(fragment, Fragment.CREATED, 0, 0, false);
-            }
-        }
-    }
-
     private static Transition cloneTransition(Transition transition) {
         if (transition != null) {
             transition = transition.clone();
@@ -1663,12 +1657,14 @@
             pw.flush();
         }
 
-        if (state == null) {
-            if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) {
-                state = beginTransition(firstOutFragments, lastInFragments, true);
+        if (mManager.mCurState >= Fragment.CREATED) {
+            if (state == null) {
+                if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) {
+                    state = beginTransition(firstOutFragments, lastInFragments, true);
+                }
+            } else if (!doStateMove) {
+                setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
             }
-        } else if (!doStateMove) {
-            setNameOverrides(state, mSharedElementTargetNames, mSharedElementSourceNames);
         }
 
         bumpBackStackNesting(-1);
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index aafb3c6..6870bbf 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -2509,6 +2509,7 @@
             throw new SuperNotCalledException("Fragment " + this
                     + " did not call through to super.onDestroy()");
         }
+        mChildFragmentManager = null;
     }
 
     private static Transition loadTransition(Context context, TypedArray typedArray,
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 04110c6..0631943 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1625,7 +1625,9 @@
             final BackStackRecord bss = mBackStack.remove(last);
             SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
             SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
-            bss.calculateBackFragments(firstOutFragments, lastInFragments);
+            if (mCurState >= Fragment.CREATED) {
+                bss.calculateBackFragments(firstOutFragments, lastInFragments);
+            }
             bss.popFromBackStack(true, null, firstOutFragments, lastInFragments);
             reportBackStackChanged();
         } else {
@@ -1672,8 +1674,10 @@
             final int LAST = states.size()-1;
             SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
             SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
-            for (int i=0; i<=LAST; i++) {
-                states.get(i).calculateBackFragments(firstOutFragments, lastInFragments);
+            if (mCurState >= Fragment.CREATED) {
+                for (int i = 0; i <= LAST; i++) {
+                    states.get(i).calculateBackFragments(firstOutFragments, lastInFragments);
+                }
             }
             BackStackRecord.TransitionState state = null;
             for (int i=0; i<=LAST; i++) {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 307c3eb..b1c5fd8 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -226,7 +226,7 @@
         SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE);
 
         registerService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
-                new StaticOuterContextServiceFetcher<ConnectivityManager>() {
+                new StaticApplicationContextServiceFetcher<ConnectivityManager>() {
             @Override
             public ConnectivityManager createService(Context context) {
                 IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
@@ -840,22 +840,21 @@
     }
 
     /**
-     * Like StaticServiceFetcher, creates only one instance of the service per process, but when
-     * creating the service for the first time, passes it the outer context of the creating
-     * component.
+     * Like StaticServiceFetcher, creates only one instance of the service per application, but when
+     * creating the service for the first time, passes it the application context of the creating
+     * application.
      *
-     * TODO: Is this safe in the case where multiple applications share the same process?
      * TODO: Delete this once its only user (ConnectivityManager) is known to work well in the
      * case where multiple application components each have their own ConnectivityManager object.
      */
-    static abstract class StaticOuterContextServiceFetcher<T> implements ServiceFetcher<T> {
+    static abstract class StaticApplicationContextServiceFetcher<T> implements ServiceFetcher<T> {
         private T mCachedInstance;
 
         @Override
         public final T getService(ContextImpl ctx) {
-            synchronized (StaticOuterContextServiceFetcher.this) {
+            synchronized (StaticApplicationContextServiceFetcher.this) {
                 if (mCachedInstance == null) {
-                    mCachedInstance = createService(ctx.getOuterContext());
+                    mCachedInstance = createService(ctx.getApplicationContext());
                 }
                 return mCachedInstance;
             }
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 1ec6878..56b4249 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -17,7 +17,6 @@
 package android.app;
 
 import android.annotation.IntDef;
-import android.annotation.TestApi;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.os.RemoteException;
@@ -267,9 +266,7 @@
     /**
      * @return If UI mode is locked or not. When UI mode is locked, calls to change UI mode
      *         like {@link #enableCarMode(int)} will silently fail.
-     * @hide
      */
-    @TestApi
     public boolean isUiModeLocked() {
         if (mService != null) {
             try {
@@ -289,9 +286,7 @@
      * mode will fail silently.
      *
      * @return {@code true} if night mode is locked or {@code false} otherwise
-     * @hide
      */
-    @TestApi
     public boolean isNightModeLocked() {
         if (mService != null) {
             try {
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 4df83036..edd888b 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -16,12 +16,16 @@
 
 package android.content.pm;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.content.res.XmlResourceParser;
 
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.UserHandle;
+import android.text.BidiFormatter;
+import android.text.TextPaint;
 import android.text.TextUtils;
 import android.util.Printer;
 
@@ -38,6 +42,8 @@
  * in the implementation of Parcelable in subclasses.
  */
 public class PackageItemInfo {
+    private static final float MAX_LABEL_SIZE_PX = 500f;
+
     /**
      * Public name of this item. From the "android:name" attribute.
      */
@@ -140,6 +146,56 @@
     }
 
     /**
+     * Same as {@link #loadLabel(PackageManager)} with the addition that
+     * the returned label is safe for being presented in the UI since it
+     * will not contain new lines and the length will be limited to a
+     * reasonable amount. This prevents a malicious party to influence UI
+     * layout via the app label misleading the user into performing a
+     * detrimental for them action. If the label is too long it will be
+     * truncated and ellipsized at the end.
+     *
+     * @param pm A PackageManager from which the label can be loaded; usually
+     * the PackageManager from which you originally retrieved this item
+     * @return Returns a CharSequence containing the item's label. If the
+     * item does not have a label, its name is returned.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) {
+        // loadLabel() always returns non-null
+        CharSequence label = loadLabel(pm);
+
+        // If the label contains new line characters it may push the UI
+        // down to hide a part of it. Labels shouldn't have new line
+        // characters, so just truncate at the first time one is seen.
+        String labelStr = label.toString();
+        final int labelLength = labelStr.length();
+        int offset = 0;
+        while (offset < labelLength) {
+            final int codePoint = labelStr.codePointAt(offset);
+            final int type = Character.getType(codePoint);
+            if (type == Character.LINE_SEPARATOR
+                    || type == Character.CONTROL
+                    || type == Character.PARAGRAPH_SEPARATOR) {
+                labelStr = labelStr.substring(0, offset);
+                break;
+            }
+            offset += Character.charCount(codePoint);
+        }
+
+        if (labelStr.isEmpty()) {
+            return labelStr;
+        }
+
+        TextPaint paint = new TextPaint();
+        paint.setTextSize(42);
+
+        return TextUtils.ellipsize(labelStr, paint, MAX_LABEL_SIZE_PX,
+                TextUtils.TruncateAt.END);
+    }
+
+    /**
      * Retrieve the current graphical icon associated with this item.  This
      * will call back on the given PackageManager to load the icon from
      * the application.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 20b3798..6e9e456 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -36,6 +36,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.R;
@@ -680,6 +681,22 @@
     }
 
     /**
+     * Returns whether switching users is currently allowed.
+     * <p>For instance switching users is not allowed if the current user is in a phone call,
+     * or system user hasn't been unlocked yet
+     * @hide
+     */
+    public boolean canSwitchUsers() {
+        boolean allowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt(
+                mContext.getContentResolver(),
+                Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED, 0) != 0;
+        boolean isSystemUserUnlocked = isUserUnlocked(UserHandle.SYSTEM);
+        boolean inCall = TelephonyManager.getDefault().getCallState()
+                != TelephonyManager.CALL_STATE_IDLE;
+        return (allowUserSwitchingWhenSystemUserLocked || isSystemUserUnlocked) && !inCall;
+    }
+
+    /**
      * Returns the user handle for the user that this process is running under.
      *
      * @return the user handle of this process.
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 87bee44..6e3dbd8 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -23,32 +23,17 @@
 import android.content.res.TypedArray;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.text.TextUtils;
-import android.text.InputType;
-import android.text.format.DateFormat;
-import android.text.format.DateUtils;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.SparseArray;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.NumberPicker.OnValueChangeListener;
 
 import com.android.internal.R;
 
-import java.text.DateFormatSymbols;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Locale;
 import java.util.TimeZone;
 
-import libcore.icu.ICU;
-
 /**
  * Provides a widget for selecting a date.
  * <p>
@@ -527,6 +512,114 @@
         protected void onLocaleChanged(Locale locale) {
             // Stub.
         }
+
+        /**
+         * Class for managing state storing/restoring.
+         */
+        static class SavedState extends View.BaseSavedState {
+            private final int mSelectedYear;
+            private final int mSelectedMonth;
+            private final int mSelectedDay;
+            private final long mMinDate;
+            private final long mMaxDate;
+            private final int mCurrentView;
+            private final int mListPosition;
+            private final int mListPositionOffset;
+
+            public SavedState(Parcelable superState, int year, int month, int day, long minDate,
+                    long maxDate) {
+                this(superState, year, month, day, minDate, maxDate, 0, 0, 0);
+            }
+
+            /**
+             * Constructor called from {@link DatePicker#onSaveInstanceState()}
+             */
+            public SavedState(Parcelable superState, int year, int month, int day, long minDate,
+                    long maxDate, int currentView, int listPosition, int listPositionOffset) {
+                super(superState);
+                mSelectedYear = year;
+                mSelectedMonth = month;
+                mSelectedDay = day;
+                mMinDate = minDate;
+                mMaxDate = maxDate;
+                mCurrentView = currentView;
+                mListPosition = listPosition;
+                mListPositionOffset = listPositionOffset;
+            }
+
+            /**
+             * Constructor called from {@link #CREATOR}
+             */
+            private SavedState(Parcel in) {
+                super(in);
+                mSelectedYear = in.readInt();
+                mSelectedMonth = in.readInt();
+                mSelectedDay = in.readInt();
+                mMinDate = in.readLong();
+                mMaxDate = in.readLong();
+                mCurrentView = in.readInt();
+                mListPosition = in.readInt();
+                mListPositionOffset = in.readInt();
+            }
+
+            @Override
+            public void writeToParcel(Parcel dest, int flags) {
+                super.writeToParcel(dest, flags);
+                dest.writeInt(mSelectedYear);
+                dest.writeInt(mSelectedMonth);
+                dest.writeInt(mSelectedDay);
+                dest.writeLong(mMinDate);
+                dest.writeLong(mMaxDate);
+                dest.writeInt(mCurrentView);
+                dest.writeInt(mListPosition);
+                dest.writeInt(mListPositionOffset);
+            }
+
+            public int getSelectedDay() {
+                return mSelectedDay;
+            }
+
+            public int getSelectedMonth() {
+                return mSelectedMonth;
+            }
+
+            public int getSelectedYear() {
+                return mSelectedYear;
+            }
+
+            public long getMinDate() {
+                return mMinDate;
+            }
+
+            public long getMaxDate() {
+                return mMaxDate;
+            }
+
+            public int getCurrentView() {
+                return mCurrentView;
+            }
+
+            public int getListPosition() {
+                return mListPosition;
+            }
+
+            public int getListPositionOffset() {
+                return mListPositionOffset;
+            }
+
+            @SuppressWarnings("all")
+            // suppress unused and hiding
+            public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
+
+                public SavedState createFromParcel(Parcel in) {
+                    return new SavedState(in);
+                }
+
+                public SavedState[] newArray(int size) {
+                    return new SavedState[size];
+                }
+            };
+        }
     }
 
     /**
@@ -535,666 +628,7 @@
      *
      * @hide
      */
-    public static interface ValidationCallback {
+    public interface ValidationCallback {
         void onValidationChanged(boolean valid);
     }
-
-    /**
-     * A delegate implementing the basic DatePicker
-     */
-    private static class DatePickerSpinnerDelegate extends AbstractDatePickerDelegate {
-
-        private static final String DATE_FORMAT = "MM/dd/yyyy";
-
-        private static final int DEFAULT_START_YEAR = 1900;
-
-        private static final int DEFAULT_END_YEAR = 2100;
-
-        private static final boolean DEFAULT_CALENDAR_VIEW_SHOWN = true;
-
-        private static final boolean DEFAULT_SPINNERS_SHOWN = true;
-
-        private static final boolean DEFAULT_ENABLED_STATE = true;
-
-        private final LinearLayout mSpinners;
-
-        private final NumberPicker mDaySpinner;
-
-        private final NumberPicker mMonthSpinner;
-
-        private final NumberPicker mYearSpinner;
-
-        private final EditText mDaySpinnerInput;
-
-        private final EditText mMonthSpinnerInput;
-
-        private final EditText mYearSpinnerInput;
-
-        private final CalendarView mCalendarView;
-
-        private String[] mShortMonths;
-
-        private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
-
-        private int mNumberOfMonths;
-
-        private Calendar mTempDate;
-
-        private Calendar mMinDate;
-
-        private Calendar mMaxDate;
-
-        private Calendar mCurrentDate;
-
-        private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
-
-        DatePickerSpinnerDelegate(DatePicker delegator, Context context, AttributeSet attrs,
-                int defStyleAttr, int defStyleRes) {
-            super(delegator, context);
-
-            mDelegator = delegator;
-            mContext = context;
-
-            // initialization based on locale
-            setCurrentLocale(Locale.getDefault());
-
-            final TypedArray attributesArray = context.obtainStyledAttributes(attrs,
-                    R.styleable.DatePicker, defStyleAttr, defStyleRes);
-            boolean spinnersShown = attributesArray.getBoolean(R.styleable.DatePicker_spinnersShown,
-                    DEFAULT_SPINNERS_SHOWN);
-            boolean calendarViewShown = attributesArray.getBoolean(
-                    R.styleable.DatePicker_calendarViewShown, DEFAULT_CALENDAR_VIEW_SHOWN);
-            int startYear = attributesArray.getInt(R.styleable.DatePicker_startYear,
-                    DEFAULT_START_YEAR);
-            int endYear = attributesArray.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
-            String minDate = attributesArray.getString(R.styleable.DatePicker_minDate);
-            String maxDate = attributesArray.getString(R.styleable.DatePicker_maxDate);
-            int layoutResourceId = attributesArray.getResourceId(
-                    R.styleable.DatePicker_legacyLayout, R.layout.date_picker_legacy);
-            attributesArray.recycle();
-
-            LayoutInflater inflater = (LayoutInflater) context
-                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-            inflater.inflate(layoutResourceId, mDelegator, true);
-
-            OnValueChangeListener onChangeListener = new OnValueChangeListener() {
-                public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
-                    updateInputState();
-                    mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis());
-                    // take care of wrapping of days and months to update greater fields
-                    if (picker == mDaySpinner) {
-                        int maxDayOfMonth = mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH);
-                        if (oldVal == maxDayOfMonth && newVal == 1) {
-                            mTempDate.add(Calendar.DAY_OF_MONTH, 1);
-                        } else if (oldVal == 1 && newVal == maxDayOfMonth) {
-                            mTempDate.add(Calendar.DAY_OF_MONTH, -1);
-                        } else {
-                            mTempDate.add(Calendar.DAY_OF_MONTH, newVal - oldVal);
-                        }
-                    } else if (picker == mMonthSpinner) {
-                        if (oldVal == 11 && newVal == 0) {
-                            mTempDate.add(Calendar.MONTH, 1);
-                        } else if (oldVal == 0 && newVal == 11) {
-                            mTempDate.add(Calendar.MONTH, -1);
-                        } else {
-                            mTempDate.add(Calendar.MONTH, newVal - oldVal);
-                        }
-                    } else if (picker == mYearSpinner) {
-                        mTempDate.set(Calendar.YEAR, newVal);
-                    } else {
-                        throw new IllegalArgumentException();
-                    }
-                    // now set the date to the adjusted one
-                    setDate(mTempDate.get(Calendar.YEAR), mTempDate.get(Calendar.MONTH),
-                            mTempDate.get(Calendar.DAY_OF_MONTH));
-                    updateSpinners();
-                    updateCalendarView();
-                    notifyDateChanged();
-                }
-            };
-
-            mSpinners = (LinearLayout) mDelegator.findViewById(R.id.pickers);
-
-            // calendar view day-picker
-            mCalendarView = (CalendarView) mDelegator.findViewById(R.id.calendar_view);
-            mCalendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
-                public void onSelectedDayChange(CalendarView view, int year, int month, int monthDay) {
-                    setDate(year, month, monthDay);
-                    updateSpinners();
-                    notifyDateChanged();
-                }
-            });
-
-            // day
-            mDaySpinner = (NumberPicker) mDelegator.findViewById(R.id.day);
-            mDaySpinner.setFormatter(NumberPicker.getTwoDigitFormatter());
-            mDaySpinner.setOnLongPressUpdateInterval(100);
-            mDaySpinner.setOnValueChangedListener(onChangeListener);
-            mDaySpinnerInput = (EditText) mDaySpinner.findViewById(R.id.numberpicker_input);
-
-            // month
-            mMonthSpinner = (NumberPicker) mDelegator.findViewById(R.id.month);
-            mMonthSpinner.setMinValue(0);
-            mMonthSpinner.setMaxValue(mNumberOfMonths - 1);
-            mMonthSpinner.setDisplayedValues(mShortMonths);
-            mMonthSpinner.setOnLongPressUpdateInterval(200);
-            mMonthSpinner.setOnValueChangedListener(onChangeListener);
-            mMonthSpinnerInput = (EditText) mMonthSpinner.findViewById(R.id.numberpicker_input);
-
-            // year
-            mYearSpinner = (NumberPicker) mDelegator.findViewById(R.id.year);
-            mYearSpinner.setOnLongPressUpdateInterval(100);
-            mYearSpinner.setOnValueChangedListener(onChangeListener);
-            mYearSpinnerInput = (EditText) mYearSpinner.findViewById(R.id.numberpicker_input);
-
-            // show only what the user required but make sure we
-            // show something and the spinners have higher priority
-            if (!spinnersShown && !calendarViewShown) {
-                setSpinnersShown(true);
-            } else {
-                setSpinnersShown(spinnersShown);
-                setCalendarViewShown(calendarViewShown);
-            }
-
-            // set the min date giving priority of the minDate over startYear
-            mTempDate.clear();
-            if (!TextUtils.isEmpty(minDate)) {
-                if (!parseDate(minDate, mTempDate)) {
-                    mTempDate.set(startYear, 0, 1);
-                }
-            } else {
-                mTempDate.set(startYear, 0, 1);
-            }
-            setMinDate(mTempDate.getTimeInMillis());
-
-            // set the max date giving priority of the maxDate over endYear
-            mTempDate.clear();
-            if (!TextUtils.isEmpty(maxDate)) {
-                if (!parseDate(maxDate, mTempDate)) {
-                    mTempDate.set(endYear, 11, 31);
-                }
-            } else {
-                mTempDate.set(endYear, 11, 31);
-            }
-            setMaxDate(mTempDate.getTimeInMillis());
-
-            // initialize to current date
-            mCurrentDate.setTimeInMillis(System.currentTimeMillis());
-            init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), mCurrentDate
-                    .get(Calendar.DAY_OF_MONTH), null);
-
-            // re-order the number spinners to match the current date format
-            reorderSpinners();
-
-            // accessibility
-            setContentDescriptions();
-
-            // If not explicitly specified this view is important for accessibility.
-            if (mDelegator.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
-                mDelegator.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
-            }
-        }
-
-        @Override
-        public void init(int year, int monthOfYear, int dayOfMonth,
-                         OnDateChangedListener onDateChangedListener) {
-            setDate(year, monthOfYear, dayOfMonth);
-            updateSpinners();
-            updateCalendarView();
-            mOnDateChangedListener = onDateChangedListener;
-        }
-
-        @Override
-        public void updateDate(int year, int month, int dayOfMonth) {
-            if (!isNewDate(year, month, dayOfMonth)) {
-                return;
-            }
-            setDate(year, month, dayOfMonth);
-            updateSpinners();
-            updateCalendarView();
-            notifyDateChanged();
-        }
-
-        @Override
-        public int getYear() {
-            return mCurrentDate.get(Calendar.YEAR);
-        }
-
-        @Override
-        public int getMonth() {
-            return mCurrentDate.get(Calendar.MONTH);
-        }
-
-        @Override
-        public int getDayOfMonth() {
-            return mCurrentDate.get(Calendar.DAY_OF_MONTH);
-        }
-
-        @Override
-        public void setFirstDayOfWeek(int firstDayOfWeek) {
-            mCalendarView.setFirstDayOfWeek(firstDayOfWeek);
-        }
-
-        @Override
-        public int getFirstDayOfWeek() {
-            return mCalendarView.getFirstDayOfWeek();
-        }
-
-        @Override
-        public void setMinDate(long minDate) {
-            mTempDate.setTimeInMillis(minDate);
-            if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
-                    && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) {
-                return;
-            }
-            mMinDate.setTimeInMillis(minDate);
-            mCalendarView.setMinDate(minDate);
-            if (mCurrentDate.before(mMinDate)) {
-                mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
-                updateCalendarView();
-            }
-            updateSpinners();
-        }
-
-        @Override
-        public Calendar getMinDate() {
-            final Calendar minDate = Calendar.getInstance();
-            minDate.setTimeInMillis(mCalendarView.getMinDate());
-            return minDate;
-        }
-
-        @Override
-        public void setMaxDate(long maxDate) {
-            mTempDate.setTimeInMillis(maxDate);
-            if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR)
-                    && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) {
-                return;
-            }
-            mMaxDate.setTimeInMillis(maxDate);
-            mCalendarView.setMaxDate(maxDate);
-            if (mCurrentDate.after(mMaxDate)) {
-                mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
-                updateCalendarView();
-            }
-            updateSpinners();
-        }
-
-        @Override
-        public Calendar getMaxDate() {
-            final Calendar maxDate = Calendar.getInstance();
-            maxDate.setTimeInMillis(mCalendarView.getMaxDate());
-            return maxDate;
-        }
-
-        @Override
-        public void setEnabled(boolean enabled) {
-            mDaySpinner.setEnabled(enabled);
-            mMonthSpinner.setEnabled(enabled);
-            mYearSpinner.setEnabled(enabled);
-            mCalendarView.setEnabled(enabled);
-            mIsEnabled = enabled;
-        }
-
-        @Override
-        public boolean isEnabled() {
-            return mIsEnabled;
-        }
-
-        @Override
-        public CalendarView getCalendarView() {
-            return mCalendarView;
-        }
-
-        @Override
-        public void setCalendarViewShown(boolean shown) {
-            mCalendarView.setVisibility(shown ? VISIBLE : GONE);
-        }
-
-        @Override
-        public boolean getCalendarViewShown() {
-            return (mCalendarView.getVisibility() == View.VISIBLE);
-        }
-
-        @Override
-        public void setSpinnersShown(boolean shown) {
-            mSpinners.setVisibility(shown ? VISIBLE : GONE);
-        }
-
-        @Override
-        public boolean getSpinnersShown() {
-            return mSpinners.isShown();
-        }
-
-        @Override
-        public void onConfigurationChanged(Configuration newConfig) {
-            setCurrentLocale(newConfig.locale);
-        }
-
-        @Override
-        public Parcelable onSaveInstanceState(Parcelable superState) {
-            return new SavedState(superState, getYear(), getMonth(), getDayOfMonth());
-        }
-
-        @Override
-        public void onRestoreInstanceState(Parcelable state) {
-            SavedState ss = (SavedState) state;
-            setDate(ss.mYear, ss.mMonth, ss.mDay);
-            updateSpinners();
-            updateCalendarView();
-        }
-
-        @Override
-        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-            onPopulateAccessibilityEvent(event);
-            return true;
-        }
-
-        @Override
-        public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
-            final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
-            String selectedDateUtterance = DateUtils.formatDateTime(mContext,
-                    mCurrentDate.getTimeInMillis(), flags);
-            event.getText().add(selectedDateUtterance);
-        }
-
-        /**
-         * Sets the current locale.
-         *
-         * @param locale The current locale.
-         */
-        @Override
-        protected void setCurrentLocale(Locale locale) {
-            super.setCurrentLocale(locale);
-
-            mTempDate = getCalendarForLocale(mTempDate, locale);
-            mMinDate = getCalendarForLocale(mMinDate, locale);
-            mMaxDate = getCalendarForLocale(mMaxDate, locale);
-            mCurrentDate = getCalendarForLocale(mCurrentDate, locale);
-
-            mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1;
-            mShortMonths = new DateFormatSymbols().getShortMonths();
-
-            if (usingNumericMonths()) {
-                // We're in a locale where a date should either be all-numeric, or all-text.
-                // All-text would require custom NumberPicker formatters for day and year.
-                mShortMonths = new String[mNumberOfMonths];
-                for (int i = 0; i < mNumberOfMonths; ++i) {
-                    mShortMonths[i] = String.format("%d", i + 1);
-                }
-            }
-        }
-
-        /**
-         * Tests whether the current locale is one where there are no real month names,
-         * such as Chinese, Japanese, or Korean locales.
-         */
-        private boolean usingNumericMonths() {
-            return Character.isDigit(mShortMonths[Calendar.JANUARY].charAt(0));
-        }
-
-        /**
-         * Gets a calendar for locale bootstrapped with the value of a given calendar.
-         *
-         * @param oldCalendar The old calendar.
-         * @param locale The locale.
-         */
-        private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
-            if (oldCalendar == null) {
-                return Calendar.getInstance(locale);
-            } else {
-                final long currentTimeMillis = oldCalendar.getTimeInMillis();
-                Calendar newCalendar = Calendar.getInstance(locale);
-                newCalendar.setTimeInMillis(currentTimeMillis);
-                return newCalendar;
-            }
-        }
-
-        /**
-         * Reorders the spinners according to the date format that is
-         * explicitly set by the user and if no such is set fall back
-         * to the current locale's default format.
-         */
-        private void reorderSpinners() {
-            mSpinners.removeAllViews();
-            // We use numeric spinners for year and day, but textual months. Ask icu4c what
-            // order the user's locale uses for that combination. http://b/7207103.
-            String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd");
-            char[] order = ICU.getDateFormatOrder(pattern);
-            final int spinnerCount = order.length;
-            for (int i = 0; i < spinnerCount; i++) {
-                switch (order[i]) {
-                    case 'd':
-                        mSpinners.addView(mDaySpinner);
-                        setImeOptions(mDaySpinner, spinnerCount, i);
-                        break;
-                    case 'M':
-                        mSpinners.addView(mMonthSpinner);
-                        setImeOptions(mMonthSpinner, spinnerCount, i);
-                        break;
-                    case 'y':
-                        mSpinners.addView(mYearSpinner);
-                        setImeOptions(mYearSpinner, spinnerCount, i);
-                        break;
-                    default:
-                        throw new IllegalArgumentException(Arrays.toString(order));
-                }
-            }
-        }
-
-        /**
-         * Parses the given <code>date</code> and in case of success sets the result
-         * to the <code>outDate</code>.
-         *
-         * @return True if the date was parsed.
-         */
-        private boolean parseDate(String date, Calendar outDate) {
-            try {
-                outDate.setTime(mDateFormat.parse(date));
-                return true;
-            } catch (ParseException e) {
-                Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
-                return false;
-            }
-        }
-
-        private boolean isNewDate(int year, int month, int dayOfMonth) {
-            return (mCurrentDate.get(Calendar.YEAR) != year
-                    || mCurrentDate.get(Calendar.MONTH) != month
-                    || mCurrentDate.get(Calendar.DAY_OF_MONTH) != dayOfMonth);
-        }
-
-        private void setDate(int year, int month, int dayOfMonth) {
-            mCurrentDate.set(year, month, dayOfMonth);
-            if (mCurrentDate.before(mMinDate)) {
-                mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
-            } else if (mCurrentDate.after(mMaxDate)) {
-                mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
-            }
-        }
-
-        private void updateSpinners() {
-            // set the spinner ranges respecting the min and max dates
-            if (mCurrentDate.equals(mMinDate)) {
-                mDaySpinner.setMinValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
-                mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
-                mDaySpinner.setWrapSelectorWheel(false);
-                mMonthSpinner.setDisplayedValues(null);
-                mMonthSpinner.setMinValue(mCurrentDate.get(Calendar.MONTH));
-                mMonthSpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.MONTH));
-                mMonthSpinner.setWrapSelectorWheel(false);
-            } else if (mCurrentDate.equals(mMaxDate)) {
-                mDaySpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.DAY_OF_MONTH));
-                mDaySpinner.setMaxValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
-                mDaySpinner.setWrapSelectorWheel(false);
-                mMonthSpinner.setDisplayedValues(null);
-                mMonthSpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.MONTH));
-                mMonthSpinner.setMaxValue(mCurrentDate.get(Calendar.MONTH));
-                mMonthSpinner.setWrapSelectorWheel(false);
-            } else {
-                mDaySpinner.setMinValue(1);
-                mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
-                mDaySpinner.setWrapSelectorWheel(true);
-                mMonthSpinner.setDisplayedValues(null);
-                mMonthSpinner.setMinValue(0);
-                mMonthSpinner.setMaxValue(11);
-                mMonthSpinner.setWrapSelectorWheel(true);
-            }
-
-            // make sure the month names are a zero based array
-            // with the months in the month spinner
-            String[] displayedValues = Arrays.copyOfRange(mShortMonths,
-                    mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1);
-            mMonthSpinner.setDisplayedValues(displayedValues);
-
-            // year spinner range does not change based on the current date
-            mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR));
-            mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR));
-            mYearSpinner.setWrapSelectorWheel(false);
-
-            // set the spinner values
-            mYearSpinner.setValue(mCurrentDate.get(Calendar.YEAR));
-            mMonthSpinner.setValue(mCurrentDate.get(Calendar.MONTH));
-            mDaySpinner.setValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
-
-            if (usingNumericMonths()) {
-                mMonthSpinnerInput.setRawInputType(InputType.TYPE_CLASS_NUMBER);
-            }
-        }
-
-        /**
-         * Updates the calendar view with the current date.
-         */
-        private void updateCalendarView() {
-            mCalendarView.setDate(mCurrentDate.getTimeInMillis(), false, false);
-        }
-
-
-        /**
-         * Notifies the listener, if such, for a change in the selected date.
-         */
-        private void notifyDateChanged() {
-            mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
-            if (mOnDateChangedListener != null) {
-                mOnDateChangedListener.onDateChanged(mDelegator, getYear(), getMonth(),
-                        getDayOfMonth());
-            }
-        }
-
-        /**
-         * Sets the IME options for a spinner based on its ordering.
-         *
-         * @param spinner The spinner.
-         * @param spinnerCount The total spinner count.
-         * @param spinnerIndex The index of the given spinner.
-         */
-        private void setImeOptions(NumberPicker spinner, int spinnerCount, int spinnerIndex) {
-            final int imeOptions;
-            if (spinnerIndex < spinnerCount - 1) {
-                imeOptions = EditorInfo.IME_ACTION_NEXT;
-            } else {
-                imeOptions = EditorInfo.IME_ACTION_DONE;
-            }
-            TextView input = (TextView) spinner.findViewById(R.id.numberpicker_input);
-            input.setImeOptions(imeOptions);
-        }
-
-        private void setContentDescriptions() {
-            // Day
-            trySetContentDescription(mDaySpinner, R.id.increment,
-                    R.string.date_picker_increment_day_button);
-            trySetContentDescription(mDaySpinner, R.id.decrement,
-                    R.string.date_picker_decrement_day_button);
-            // Month
-            trySetContentDescription(mMonthSpinner, R.id.increment,
-                    R.string.date_picker_increment_month_button);
-            trySetContentDescription(mMonthSpinner, R.id.decrement,
-                    R.string.date_picker_decrement_month_button);
-            // Year
-            trySetContentDescription(mYearSpinner, R.id.increment,
-                    R.string.date_picker_increment_year_button);
-            trySetContentDescription(mYearSpinner, R.id.decrement,
-                    R.string.date_picker_decrement_year_button);
-        }
-
-        private void trySetContentDescription(View root, int viewId, int contDescResId) {
-            View target = root.findViewById(viewId);
-            if (target != null) {
-                target.setContentDescription(mContext.getString(contDescResId));
-            }
-        }
-
-        private void updateInputState() {
-            // Make sure that if the user changes the value and the IME is active
-            // for one of the inputs if this widget, the IME is closed. If the user
-            // changed the value via the IME and there is a next input the IME will
-            // be shown, otherwise the user chose another means of changing the
-            // value and having the IME up makes no sense.
-            InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
-            if (inputMethodManager != null) {
-                if (inputMethodManager.isActive(mYearSpinnerInput)) {
-                    mYearSpinnerInput.clearFocus();
-                    inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
-                } else if (inputMethodManager.isActive(mMonthSpinnerInput)) {
-                    mMonthSpinnerInput.clearFocus();
-                    inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
-                } else if (inputMethodManager.isActive(mDaySpinnerInput)) {
-                    mDaySpinnerInput.clearFocus();
-                    inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
-                }
-            }
-        }
-    }
-
-    /**
-     * Class for managing state storing/restoring.
-     */
-    private static class SavedState extends BaseSavedState {
-
-        private final int mYear;
-
-        private final int mMonth;
-
-        private final int mDay;
-
-        /**
-         * Constructor called from {@link DatePicker#onSaveInstanceState()}
-         */
-        private SavedState(Parcelable superState, int year, int month, int day) {
-            super(superState);
-            mYear = year;
-            mMonth = month;
-            mDay = day;
-        }
-
-        /**
-         * Constructor called from {@link #CREATOR}
-         */
-        private SavedState(Parcel in) {
-            super(in);
-            mYear = in.readInt();
-            mMonth = in.readInt();
-            mDay = in.readInt();
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeInt(mYear);
-            dest.writeInt(mMonth);
-            dest.writeInt(mDay);
-        }
-
-        @SuppressWarnings("all")
-        // suppress unused and hiding
-        public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
-
-            public SavedState createFromParcel(Parcel in) {
-                return new SavedState(in);
-            }
-
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
-    }
 }
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index cbb6109..5adac01 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -16,13 +16,14 @@
 
 package android.widget;
 
+import com.android.internal.R;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.format.DateFormat;
 import android.text.format.DateUtils;
@@ -37,8 +38,6 @@
 import android.widget.DayPickerView.OnDaySelectedListener;
 import android.widget.YearPickerView.OnYearSelectedListener;
 
-import com.android.internal.R;
-
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Locale;
@@ -550,25 +549,27 @@
 
     @Override
     public void onRestoreInstanceState(Parcelable state) {
-        final SavedState ss = (SavedState) state;
+        if (state instanceof SavedState) {
+            final SavedState ss = (SavedState) state;
 
-        // TODO: Move instance state into DayPickerView, YearPickerView.
-        mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
-        mMinDate.setTimeInMillis(ss.getMinDate());
-        mMaxDate.setTimeInMillis(ss.getMaxDate());
+            // TODO: Move instance state into DayPickerView, YearPickerView.
+            mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
+            mMinDate.setTimeInMillis(ss.getMinDate());
+            mMaxDate.setTimeInMillis(ss.getMaxDate());
 
-        onCurrentDateChanged(false);
+            onCurrentDateChanged(false);
 
-        final int currentView = ss.getCurrentView();
-        setCurrentView(currentView);
+            final int currentView = ss.getCurrentView();
+            setCurrentView(currentView);
 
-        final int listPosition = ss.getListPosition();
-        if (listPosition != -1) {
-            if (currentView == VIEW_MONTH_DAY) {
-                mDayPickerView.setPosition(listPosition);
-            } else if (currentView == VIEW_YEAR) {
-                final int listPositionOffset = ss.getListPositionOffset();
-                mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset);
+            final int listPosition = ss.getListPosition();
+            if (listPosition != -1) {
+                if (currentView == VIEW_MONTH_DAY) {
+                    mDayPickerView.setPosition(listPosition);
+                } else if (currentView == VIEW_YEAR) {
+                    final int listPositionOffset = ss.getListPositionOffset();
+                    mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset);
+                }
             }
         }
     }
@@ -613,108 +614,4 @@
     private void tryVibrate() {
         mDelegator.performHapticFeedback(HapticFeedbackConstants.CALENDAR_DATE);
     }
-
-    /**
-     * Class for managing state storing/restoring.
-     */
-    private static class SavedState extends View.BaseSavedState {
-        private final int mSelectedYear;
-        private final int mSelectedMonth;
-        private final int mSelectedDay;
-        private final long mMinDate;
-        private final long mMaxDate;
-        private final int mCurrentView;
-        private final int mListPosition;
-        private final int mListPositionOffset;
-
-        /**
-         * Constructor called from {@link DatePicker#onSaveInstanceState()}
-         */
-        private SavedState(Parcelable superState, int year, int month, int day,
-                long minDate, long maxDate, int currentView, int listPosition,
-                int listPositionOffset) {
-            super(superState);
-            mSelectedYear = year;
-            mSelectedMonth = month;
-            mSelectedDay = day;
-            mMinDate = minDate;
-            mMaxDate = maxDate;
-            mCurrentView = currentView;
-            mListPosition = listPosition;
-            mListPositionOffset = listPositionOffset;
-        }
-
-        /**
-         * Constructor called from {@link #CREATOR}
-         */
-        private SavedState(Parcel in) {
-            super(in);
-            mSelectedYear = in.readInt();
-            mSelectedMonth = in.readInt();
-            mSelectedDay = in.readInt();
-            mMinDate = in.readLong();
-            mMaxDate = in.readLong();
-            mCurrentView = in.readInt();
-            mListPosition = in.readInt();
-            mListPositionOffset = in.readInt();
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeInt(mSelectedYear);
-            dest.writeInt(mSelectedMonth);
-            dest.writeInt(mSelectedDay);
-            dest.writeLong(mMinDate);
-            dest.writeLong(mMaxDate);
-            dest.writeInt(mCurrentView);
-            dest.writeInt(mListPosition);
-            dest.writeInt(mListPositionOffset);
-        }
-
-        public int getSelectedDay() {
-            return mSelectedDay;
-        }
-
-        public int getSelectedMonth() {
-            return mSelectedMonth;
-        }
-
-        public int getSelectedYear() {
-            return mSelectedYear;
-        }
-
-        public long getMinDate() {
-            return mMinDate;
-        }
-
-        public long getMaxDate() {
-            return mMaxDate;
-        }
-
-        public int getCurrentView() {
-            return mCurrentView;
-        }
-
-        public int getListPosition() {
-            return mListPosition;
-        }
-
-        public int getListPositionOffset() {
-            return mListPositionOffset;
-        }
-
-        @SuppressWarnings("all")
-        // suppress unused and hiding
-        public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
-
-            public SavedState createFromParcel(Parcel in) {
-                return new SavedState(in);
-            }
-
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
-    }
 }
diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java
new file mode 100644
index 0000000..255de79
--- /dev/null
+++ b/core/java/android/widget/DatePickerSpinnerDelegate.java
@@ -0,0 +1,652 @@
+/*
+ * Copyright (C) 2016 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.widget;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.os.Parcelable;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.DatePicker.AbstractDatePickerDelegate;
+import android.widget.NumberPicker.OnValueChangeListener;
+
+import java.text.DateFormatSymbols;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Locale;
+
+import libcore.icu.ICU;
+
+/**
+ * A delegate implementing the basic DatePicker
+ */
+class DatePickerSpinnerDelegate extends AbstractDatePickerDelegate {
+
+    private static final String DATE_FORMAT = "MM/dd/yyyy";
+
+    private static final int DEFAULT_START_YEAR = 1900;
+
+    private static final int DEFAULT_END_YEAR = 2100;
+
+    private static final boolean DEFAULT_CALENDAR_VIEW_SHOWN = true;
+
+    private static final boolean DEFAULT_SPINNERS_SHOWN = true;
+
+    private static final boolean DEFAULT_ENABLED_STATE = true;
+
+    private final LinearLayout mSpinners;
+
+    private final NumberPicker mDaySpinner;
+
+    private final NumberPicker mMonthSpinner;
+
+    private final NumberPicker mYearSpinner;
+
+    private final EditText mDaySpinnerInput;
+
+    private final EditText mMonthSpinnerInput;
+
+    private final EditText mYearSpinnerInput;
+
+    private final CalendarView mCalendarView;
+
+    private String[] mShortMonths;
+
+    private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
+
+    private int mNumberOfMonths;
+
+    private Calendar mTempDate;
+
+    private Calendar mMinDate;
+
+    private Calendar mMaxDate;
+
+    private Calendar mCurrentDate;
+
+    private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
+
+    DatePickerSpinnerDelegate(DatePicker delegator, Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(delegator, context);
+
+        mDelegator = delegator;
+        mContext = context;
+
+        // initialization based on locale
+        setCurrentLocale(Locale.getDefault());
+
+        final TypedArray attributesArray = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.DatePicker, defStyleAttr, defStyleRes);
+        boolean spinnersShown = attributesArray.getBoolean(com.android.internal.R.styleable.DatePicker_spinnersShown,
+                DEFAULT_SPINNERS_SHOWN);
+        boolean calendarViewShown = attributesArray.getBoolean(
+                com.android.internal.R.styleable.DatePicker_calendarViewShown, DEFAULT_CALENDAR_VIEW_SHOWN);
+        int startYear = attributesArray.getInt(com.android.internal.R.styleable.DatePicker_startYear,
+                DEFAULT_START_YEAR);
+        int endYear = attributesArray.getInt(com.android.internal.R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
+        String minDate = attributesArray.getString(com.android.internal.R.styleable.DatePicker_minDate);
+        String maxDate = attributesArray.getString(com.android.internal.R.styleable.DatePicker_maxDate);
+        int layoutResourceId = attributesArray.getResourceId(
+                com.android.internal.R.styleable.DatePicker_legacyLayout, com.android.internal.R.layout.date_picker_legacy);
+        attributesArray.recycle();
+
+        LayoutInflater inflater = (LayoutInflater) context
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        inflater.inflate(layoutResourceId, mDelegator, true);
+
+        OnValueChangeListener onChangeListener = new OnValueChangeListener() {
+            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+                updateInputState();
+                mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis());
+                // take care of wrapping of days and months to update greater fields
+                if (picker == mDaySpinner) {
+                    int maxDayOfMonth = mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH);
+                    if (oldVal == maxDayOfMonth && newVal == 1) {
+                        mTempDate.add(Calendar.DAY_OF_MONTH, 1);
+                    } else if (oldVal == 1 && newVal == maxDayOfMonth) {
+                        mTempDate.add(Calendar.DAY_OF_MONTH, -1);
+                    } else {
+                        mTempDate.add(Calendar.DAY_OF_MONTH, newVal - oldVal);
+                    }
+                } else if (picker == mMonthSpinner) {
+                    if (oldVal == 11 && newVal == 0) {
+                        mTempDate.add(Calendar.MONTH, 1);
+                    } else if (oldVal == 0 && newVal == 11) {
+                        mTempDate.add(Calendar.MONTH, -1);
+                    } else {
+                        mTempDate.add(Calendar.MONTH, newVal - oldVal);
+                    }
+                } else if (picker == mYearSpinner) {
+                    mTempDate.set(Calendar.YEAR, newVal);
+                } else {
+                    throw new IllegalArgumentException();
+                }
+                // now set the date to the adjusted one
+                setDate(mTempDate.get(Calendar.YEAR), mTempDate.get(Calendar.MONTH),
+                        mTempDate.get(Calendar.DAY_OF_MONTH));
+                updateSpinners();
+                updateCalendarView();
+                notifyDateChanged();
+            }
+        };
+
+        mSpinners = (LinearLayout) mDelegator.findViewById(com.android.internal.R.id.pickers);
+
+        // calendar view day-picker
+        mCalendarView = (CalendarView) mDelegator.findViewById(com.android.internal.R.id.calendar_view);
+        mCalendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
+            public void onSelectedDayChange(CalendarView view, int year, int month, int monthDay) {
+                setDate(year, month, monthDay);
+                updateSpinners();
+                notifyDateChanged();
+            }
+        });
+
+        // day
+        mDaySpinner = (NumberPicker) mDelegator.findViewById(com.android.internal.R.id.day);
+        mDaySpinner.setFormatter(NumberPicker.getTwoDigitFormatter());
+        mDaySpinner.setOnLongPressUpdateInterval(100);
+        mDaySpinner.setOnValueChangedListener(onChangeListener);
+        mDaySpinnerInput = (EditText) mDaySpinner.findViewById(com.android.internal.R.id.numberpicker_input);
+
+        // month
+        mMonthSpinner = (NumberPicker) mDelegator.findViewById(com.android.internal.R.id.month);
+        mMonthSpinner.setMinValue(0);
+        mMonthSpinner.setMaxValue(mNumberOfMonths - 1);
+        mMonthSpinner.setDisplayedValues(mShortMonths);
+        mMonthSpinner.setOnLongPressUpdateInterval(200);
+        mMonthSpinner.setOnValueChangedListener(onChangeListener);
+        mMonthSpinnerInput = (EditText) mMonthSpinner.findViewById(com.android.internal.R.id.numberpicker_input);
+
+        // year
+        mYearSpinner = (NumberPicker) mDelegator.findViewById(com.android.internal.R.id.year);
+        mYearSpinner.setOnLongPressUpdateInterval(100);
+        mYearSpinner.setOnValueChangedListener(onChangeListener);
+        mYearSpinnerInput = (EditText) mYearSpinner.findViewById(com.android.internal.R.id.numberpicker_input);
+
+        // show only what the user required but make sure we
+        // show something and the spinners have higher priority
+        if (!spinnersShown && !calendarViewShown) {
+            setSpinnersShown(true);
+        } else {
+            setSpinnersShown(spinnersShown);
+            setCalendarViewShown(calendarViewShown);
+        }
+
+        // set the min date giving priority of the minDate over startYear
+        mTempDate.clear();
+        if (!TextUtils.isEmpty(minDate)) {
+            if (!parseDate(minDate, mTempDate)) {
+                mTempDate.set(startYear, 0, 1);
+            }
+        } else {
+            mTempDate.set(startYear, 0, 1);
+        }
+        setMinDate(mTempDate.getTimeInMillis());
+
+        // set the max date giving priority of the maxDate over endYear
+        mTempDate.clear();
+        if (!TextUtils.isEmpty(maxDate)) {
+            if (!parseDate(maxDate, mTempDate)) {
+                mTempDate.set(endYear, 11, 31);
+            }
+        } else {
+            mTempDate.set(endYear, 11, 31);
+        }
+        setMaxDate(mTempDate.getTimeInMillis());
+
+        // initialize to current date
+        mCurrentDate.setTimeInMillis(System.currentTimeMillis());
+        init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), mCurrentDate
+                .get(Calendar.DAY_OF_MONTH), null);
+
+        // re-order the number spinners to match the current date format
+        reorderSpinners();
+
+        // accessibility
+        setContentDescriptions();
+
+        // If not explicitly specified this view is important for accessibility.
+        if (mDelegator.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+            mDelegator.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
+    }
+
+    @Override
+    public void init(int year, int monthOfYear, int dayOfMonth,
+                     DatePicker.OnDateChangedListener onDateChangedListener) {
+        setDate(year, monthOfYear, dayOfMonth);
+        updateSpinners();
+        updateCalendarView();
+        mOnDateChangedListener = onDateChangedListener;
+    }
+
+    @Override
+    public void updateDate(int year, int month, int dayOfMonth) {
+        if (!isNewDate(year, month, dayOfMonth)) {
+            return;
+        }
+        setDate(year, month, dayOfMonth);
+        updateSpinners();
+        updateCalendarView();
+        notifyDateChanged();
+    }
+
+    @Override
+    public int getYear() {
+        return mCurrentDate.get(Calendar.YEAR);
+    }
+
+    @Override
+    public int getMonth() {
+        return mCurrentDate.get(Calendar.MONTH);
+    }
+
+    @Override
+    public int getDayOfMonth() {
+        return mCurrentDate.get(Calendar.DAY_OF_MONTH);
+    }
+
+    @Override
+    public void setFirstDayOfWeek(int firstDayOfWeek) {
+        mCalendarView.setFirstDayOfWeek(firstDayOfWeek);
+    }
+
+    @Override
+    public int getFirstDayOfWeek() {
+        return mCalendarView.getFirstDayOfWeek();
+    }
+
+    @Override
+    public void setMinDate(long minDate) {
+        mTempDate.setTimeInMillis(minDate);
+        if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
+                && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) {
+            return;
+        }
+        mMinDate.setTimeInMillis(minDate);
+        mCalendarView.setMinDate(minDate);
+        if (mCurrentDate.before(mMinDate)) {
+            mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
+            updateCalendarView();
+        }
+        updateSpinners();
+    }
+
+    @Override
+    public Calendar getMinDate() {
+        final Calendar minDate = Calendar.getInstance();
+        minDate.setTimeInMillis(mCalendarView.getMinDate());
+        return minDate;
+    }
+
+    @Override
+    public void setMaxDate(long maxDate) {
+        mTempDate.setTimeInMillis(maxDate);
+        if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR)
+                && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) {
+            return;
+        }
+        mMaxDate.setTimeInMillis(maxDate);
+        mCalendarView.setMaxDate(maxDate);
+        if (mCurrentDate.after(mMaxDate)) {
+            mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
+            updateCalendarView();
+        }
+        updateSpinners();
+    }
+
+    @Override
+    public Calendar getMaxDate() {
+        final Calendar maxDate = Calendar.getInstance();
+        maxDate.setTimeInMillis(mCalendarView.getMaxDate());
+        return maxDate;
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        mDaySpinner.setEnabled(enabled);
+        mMonthSpinner.setEnabled(enabled);
+        mYearSpinner.setEnabled(enabled);
+        mCalendarView.setEnabled(enabled);
+        mIsEnabled = enabled;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    @Override
+    public CalendarView getCalendarView() {
+        return mCalendarView;
+    }
+
+    @Override
+    public void setCalendarViewShown(boolean shown) {
+        mCalendarView.setVisibility(shown ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public boolean getCalendarViewShown() {
+        return (mCalendarView.getVisibility() == View.VISIBLE);
+    }
+
+    @Override
+    public void setSpinnersShown(boolean shown) {
+        mSpinners.setVisibility(shown ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public boolean getSpinnersShown() {
+        return mSpinners.isShown();
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        setCurrentLocale(newConfig.locale);
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState(Parcelable superState) {
+        return new SavedState(superState, getYear(), getMonth(), getDayOfMonth(),
+                getMinDate().getTimeInMillis(), getMaxDate().getTimeInMillis());
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        if (state instanceof SavedState) {
+            final SavedState ss = (SavedState) state;
+            setDate(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
+            updateSpinners();
+            updateCalendarView();
+        }
+    }
+
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        onPopulateAccessibilityEvent(event);
+        return true;
+    }
+
+    @Override
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
+        String selectedDateUtterance = DateUtils.formatDateTime(mContext,
+                mCurrentDate.getTimeInMillis(), flags);
+        event.getText().add(selectedDateUtterance);
+    }
+
+    /**
+     * Sets the current locale.
+     *
+     * @param locale The current locale.
+     */
+    @Override
+    protected void setCurrentLocale(Locale locale) {
+        super.setCurrentLocale(locale);
+
+        mTempDate = getCalendarForLocale(mTempDate, locale);
+        mMinDate = getCalendarForLocale(mMinDate, locale);
+        mMaxDate = getCalendarForLocale(mMaxDate, locale);
+        mCurrentDate = getCalendarForLocale(mCurrentDate, locale);
+
+        mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1;
+        mShortMonths = new DateFormatSymbols().getShortMonths();
+
+        if (usingNumericMonths()) {
+            // We're in a locale where a date should either be all-numeric, or all-text.
+            // All-text would require custom NumberPicker formatters for day and year.
+            mShortMonths = new String[mNumberOfMonths];
+            for (int i = 0; i < mNumberOfMonths; ++i) {
+                mShortMonths[i] = String.format("%d", i + 1);
+            }
+        }
+    }
+
+    /**
+     * Tests whether the current locale is one where there are no real month names,
+     * such as Chinese, Japanese, or Korean locales.
+     */
+    private boolean usingNumericMonths() {
+        return Character.isDigit(mShortMonths[Calendar.JANUARY].charAt(0));
+    }
+
+    /**
+     * Gets a calendar for locale bootstrapped with the value of a given calendar.
+     *
+     * @param oldCalendar The old calendar.
+     * @param locale The locale.
+     */
+    private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
+        if (oldCalendar == null) {
+            return Calendar.getInstance(locale);
+        } else {
+            final long currentTimeMillis = oldCalendar.getTimeInMillis();
+            Calendar newCalendar = Calendar.getInstance(locale);
+            newCalendar.setTimeInMillis(currentTimeMillis);
+            return newCalendar;
+        }
+    }
+
+    /**
+     * Reorders the spinners according to the date format that is
+     * explicitly set by the user and if no such is set fall back
+     * to the current locale's default format.
+     */
+    private void reorderSpinners() {
+        mSpinners.removeAllViews();
+        // We use numeric spinners for year and day, but textual months. Ask icu4c what
+        // order the user's locale uses for that combination. http://b/7207103.
+        String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd");
+        char[] order = ICU.getDateFormatOrder(pattern);
+        final int spinnerCount = order.length;
+        for (int i = 0; i < spinnerCount; i++) {
+            switch (order[i]) {
+                case 'd':
+                    mSpinners.addView(mDaySpinner);
+                    setImeOptions(mDaySpinner, spinnerCount, i);
+                    break;
+                case 'M':
+                    mSpinners.addView(mMonthSpinner);
+                    setImeOptions(mMonthSpinner, spinnerCount, i);
+                    break;
+                case 'y':
+                    mSpinners.addView(mYearSpinner);
+                    setImeOptions(mYearSpinner, spinnerCount, i);
+                    break;
+                default:
+                    throw new IllegalArgumentException(Arrays.toString(order));
+            }
+        }
+    }
+
+    /**
+     * Parses the given <code>date</code> and in case of success sets the result
+     * to the <code>outDate</code>.
+     *
+     * @return True if the date was parsed.
+     */
+    private boolean parseDate(String date, Calendar outDate) {
+        try {
+            outDate.setTime(mDateFormat.parse(date));
+            return true;
+        } catch (ParseException e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    private boolean isNewDate(int year, int month, int dayOfMonth) {
+        return (mCurrentDate.get(Calendar.YEAR) != year
+                || mCurrentDate.get(Calendar.MONTH) != month
+                || mCurrentDate.get(Calendar.DAY_OF_MONTH) != dayOfMonth);
+    }
+
+    private void setDate(int year, int month, int dayOfMonth) {
+        mCurrentDate.set(year, month, dayOfMonth);
+        if (mCurrentDate.before(mMinDate)) {
+            mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
+        } else if (mCurrentDate.after(mMaxDate)) {
+            mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
+        }
+    }
+
+    private void updateSpinners() {
+        // set the spinner ranges respecting the min and max dates
+        if (mCurrentDate.equals(mMinDate)) {
+            mDaySpinner.setMinValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
+            mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
+            mDaySpinner.setWrapSelectorWheel(false);
+            mMonthSpinner.setDisplayedValues(null);
+            mMonthSpinner.setMinValue(mCurrentDate.get(Calendar.MONTH));
+            mMonthSpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.MONTH));
+            mMonthSpinner.setWrapSelectorWheel(false);
+        } else if (mCurrentDate.equals(mMaxDate)) {
+            mDaySpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.DAY_OF_MONTH));
+            mDaySpinner.setMaxValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
+            mDaySpinner.setWrapSelectorWheel(false);
+            mMonthSpinner.setDisplayedValues(null);
+            mMonthSpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.MONTH));
+            mMonthSpinner.setMaxValue(mCurrentDate.get(Calendar.MONTH));
+            mMonthSpinner.setWrapSelectorWheel(false);
+        } else {
+            mDaySpinner.setMinValue(1);
+            mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
+            mDaySpinner.setWrapSelectorWheel(true);
+            mMonthSpinner.setDisplayedValues(null);
+            mMonthSpinner.setMinValue(0);
+            mMonthSpinner.setMaxValue(11);
+            mMonthSpinner.setWrapSelectorWheel(true);
+        }
+
+        // make sure the month names are a zero based array
+        // with the months in the month spinner
+        String[] displayedValues = Arrays.copyOfRange(mShortMonths,
+                mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1);
+        mMonthSpinner.setDisplayedValues(displayedValues);
+
+        // year spinner range does not change based on the current date
+        mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR));
+        mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR));
+        mYearSpinner.setWrapSelectorWheel(false);
+
+        // set the spinner values
+        mYearSpinner.setValue(mCurrentDate.get(Calendar.YEAR));
+        mMonthSpinner.setValue(mCurrentDate.get(Calendar.MONTH));
+        mDaySpinner.setValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
+
+        if (usingNumericMonths()) {
+            mMonthSpinnerInput.setRawInputType(InputType.TYPE_CLASS_NUMBER);
+        }
+    }
+
+    /**
+     * Updates the calendar view with the current date.
+     */
+    private void updateCalendarView() {
+        mCalendarView.setDate(mCurrentDate.getTimeInMillis(), false, false);
+    }
+
+
+    /**
+     * Notifies the listener, if such, for a change in the selected date.
+     */
+    private void notifyDateChanged() {
+        mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+        if (mOnDateChangedListener != null) {
+            mOnDateChangedListener.onDateChanged(mDelegator, getYear(), getMonth(),
+                    getDayOfMonth());
+        }
+    }
+
+    /**
+     * Sets the IME options for a spinner based on its ordering.
+     *
+     * @param spinner The spinner.
+     * @param spinnerCount The total spinner count.
+     * @param spinnerIndex The index of the given spinner.
+     */
+    private void setImeOptions(NumberPicker spinner, int spinnerCount, int spinnerIndex) {
+        final int imeOptions;
+        if (spinnerIndex < spinnerCount - 1) {
+            imeOptions = EditorInfo.IME_ACTION_NEXT;
+        } else {
+            imeOptions = EditorInfo.IME_ACTION_DONE;
+        }
+        TextView input = (TextView) spinner.findViewById(com.android.internal.R.id.numberpicker_input);
+        input.setImeOptions(imeOptions);
+    }
+
+    private void setContentDescriptions() {
+        // Day
+        trySetContentDescription(mDaySpinner, com.android.internal.R.id.increment,
+                com.android.internal.R.string.date_picker_increment_day_button);
+        trySetContentDescription(mDaySpinner, com.android.internal.R.id.decrement,
+                com.android.internal.R.string.date_picker_decrement_day_button);
+        // Month
+        trySetContentDescription(mMonthSpinner, com.android.internal.R.id.increment,
+                com.android.internal.R.string.date_picker_increment_month_button);
+        trySetContentDescription(mMonthSpinner, com.android.internal.R.id.decrement,
+                com.android.internal.R.string.date_picker_decrement_month_button);
+        // Year
+        trySetContentDescription(mYearSpinner, com.android.internal.R.id.increment,
+                com.android.internal.R.string.date_picker_increment_year_button);
+        trySetContentDescription(mYearSpinner, com.android.internal.R.id.decrement,
+                com.android.internal.R.string.date_picker_decrement_year_button);
+    }
+
+    private void trySetContentDescription(View root, int viewId, int contDescResId) {
+        View target = root.findViewById(viewId);
+        if (target != null) {
+            target.setContentDescription(mContext.getString(contDescResId));
+        }
+    }
+
+    private void updateInputState() {
+        // Make sure that if the user changes the value and the IME is active
+        // for one of the inputs if this widget, the IME is closed. If the user
+        // changed the value via the IME and there is a next input the IME will
+        // be shown, otherwise the user chose another means of changing the
+        // value and having the IME up makes no sense.
+        InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
+        if (inputMethodManager != null) {
+            if (inputMethodManager.isActive(mYearSpinnerInput)) {
+                mYearSpinnerInput.clearFocus();
+                inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
+            } else if (inputMethodManager.isActive(mMonthSpinnerInput)) {
+                mMonthSpinnerInput.clearFocus();
+                inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
+            } else if (inputMethodManager.isActive(mDaySpinnerInput)) {
+                mDaySpinnerInput.clearFocus();
+                inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
+            }
+        }
+    }
+}
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index a24d37f..f2fc617 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -22,8 +22,11 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
+import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.Parcelable.Creator;
 import android.util.AttributeSet;
+import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import com.android.internal.R;
 
@@ -301,5 +304,69 @@
             mContext = context;
             mLocale = context.getResources().getConfiguration().locale;
         }
+
+        protected static class SavedState extends View.BaseSavedState {
+            private final int mHour;
+            private final int mMinute;
+            private final boolean mIs24HourMode;
+            private final int mCurrentItemShowing;
+
+            public SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode) {
+                this(superState, hour, minute, is24HourMode, 0);
+            }
+
+            public SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode,
+                    int currentItemShowing) {
+                super(superState);
+                mHour = hour;
+                mMinute = minute;
+                mIs24HourMode = is24HourMode;
+                mCurrentItemShowing = currentItemShowing;
+            }
+
+            private SavedState(Parcel in) {
+                super(in);
+                mHour = in.readInt();
+                mMinute = in.readInt();
+                mIs24HourMode = (in.readInt() == 1);
+                mCurrentItemShowing = in.readInt();
+            }
+
+            public int getHour() {
+                return mHour;
+            }
+
+            public int getMinute() {
+                return mMinute;
+            }
+
+            public boolean is24HourMode() {
+                return mIs24HourMode;
+            }
+
+            public int getCurrentItemShowing() {
+                return mCurrentItemShowing;
+            }
+
+            @Override
+            public void writeToParcel(Parcel dest, int flags) {
+                super.writeToParcel(dest, flags);
+                dest.writeInt(mHour);
+                dest.writeInt(mMinute);
+                dest.writeInt(mIs24HourMode ? 1 : 0);
+                dest.writeInt(mCurrentItemShowing);
+            }
+
+            @SuppressWarnings({"unused", "hiding"})
+            public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
+                public SavedState createFromParcel(Parcel in) {
+                    return new SavedState(in);
+                }
+
+                public SavedState[] newArray(int size) {
+                    return new SavedState[size];
+                }
+            };
+        }
     }
 }
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 05fd4c8..4a24e26 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -21,7 +21,6 @@
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.SpannableStringBuilder;
 import android.text.format.DateFormat;
@@ -45,7 +44,6 @@
 import com.android.internal.widget.NumericTextView.OnValueChangedListener;
 
 import java.util.Calendar;
-import java.util.Locale;
 
 /**
  * A delegate implementing the radial clock-based TimePicker.
@@ -501,9 +499,11 @@
 
     @Override
     public void onRestoreInstanceState(Parcelable state) {
-        final SavedState ss = (SavedState) state;
-        initialize(ss.getHour(), ss.getMinute(), ss.is24HourMode(), ss.getCurrentItemShowing());
-        mRadialTimePickerView.invalidate();
+        if (state instanceof SavedState) {
+            final SavedState ss = (SavedState) state;
+            initialize(ss.getHour(), ss.getMinute(), ss.is24HourMode(), ss.getCurrentItemShowing());
+            mRadialTimePickerView.invalidate();
+        }
     }
 
     @Override
@@ -544,70 +544,6 @@
         }
     }
 
-    /**
-     * Used to save / restore state of time picker
-     */
-    private static class SavedState extends View.BaseSavedState {
-
-        private final int mHour;
-        private final int mMinute;
-        private final boolean mIs24HourMode;
-        private final int mCurrentItemShowing;
-
-        private SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode,
-                int currentItemShowing) {
-            super(superState);
-            mHour = hour;
-            mMinute = minute;
-            mIs24HourMode = is24HourMode;
-            mCurrentItemShowing = currentItemShowing;
-        }
-
-        private SavedState(Parcel in) {
-            super(in);
-            mHour = in.readInt();
-            mMinute = in.readInt();
-            mIs24HourMode = (in.readInt() == 1);
-            mCurrentItemShowing = in.readInt();
-        }
-
-        public int getHour() {
-            return mHour;
-        }
-
-        public int getMinute() {
-            return mMinute;
-        }
-
-        public boolean is24HourMode() {
-            return mIs24HourMode;
-        }
-
-        public int getCurrentItemShowing() {
-            return mCurrentItemShowing;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeInt(mHour);
-            dest.writeInt(mMinute);
-            dest.writeInt(mIs24HourMode ? 1 : 0);
-            dest.writeInt(mCurrentItemShowing);
-        }
-
-        @SuppressWarnings({"unused", "hiding"})
-        public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
-            public SavedState createFromParcel(Parcel in) {
-                return new SavedState(in);
-            }
-
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
-    }
-
     private void tryVibrate() {
         mDelegator.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
     }
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 863d409..b113fd9 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.format.DateFormat;
 import android.text.format.DateUtils;
@@ -32,7 +31,6 @@
 import com.android.internal.R;
 
 import java.util.Calendar;
-import java.util.Locale;
 
 import libcore.icu.LocaleData;
 
@@ -387,14 +385,16 @@
 
     @Override
     public Parcelable onSaveInstanceState(Parcelable superState) {
-        return new SavedState(superState, getHour(), getMinute());
+        return new SavedState(superState, getHour(), getMinute(), is24Hour());
     }
 
     @Override
     public void onRestoreInstanceState(Parcelable state) {
-        SavedState ss = (SavedState) state;
-        setHour(ss.getHour());
-        setMinute(ss.getMinute());
+        if (state instanceof SavedState) {
+            final SavedState ss = (SavedState) state;
+            setHour(ss.getHour());
+            setMinute(ss.getMinute());
+        }
     }
 
     @Override
@@ -525,52 +525,6 @@
         }
     }
 
-    /**
-     * Used to save / restore state of time picker
-     */
-    private static class SavedState extends View.BaseSavedState {
-        private final int mHour;
-        private final int mMinute;
-
-        private SavedState(Parcelable superState, int hour, int minute) {
-            super(superState);
-            mHour = hour;
-            mMinute = minute;
-        }
-
-        private SavedState(Parcel in) {
-            super(in);
-            mHour = in.readInt();
-            mMinute = in.readInt();
-        }
-
-        public int getHour() {
-            return mHour;
-        }
-
-        public int getMinute() {
-            return mMinute;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeInt(mHour);
-            dest.writeInt(mMinute);
-        }
-
-        @SuppressWarnings({"unused", "hiding"})
-        public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
-            public SavedState createFromParcel(Parcel in) {
-                return new SavedState(in);
-            }
-
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
-    }
-
     public static String[] getAmPmStrings(Context context) {
         String[] result = new String[2];
         LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
diff --git a/core/res/res/values-h426dp-port/integers.xml b/core/res/res/values-h426dp-port/integers.xml
new file mode 100644
index 0000000..94abbec
--- /dev/null
+++ b/core/res/res/values-h426dp-port/integers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <integer name="date_picker_mode">2</integer>
+    <integer name="time_picker_mode">2</integer>
+</resources>
diff --git a/core/res/res/values-w426dp-land/integers.xml b/core/res/res/values-w426dp-land/integers.xml
new file mode 100644
index 0000000..94abbec
--- /dev/null
+++ b/core/res/res/values-w426dp-land/integers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <integer name="date_picker_mode">2</integer>
+    <integer name="time_picker_mode">2</integer>
+</resources>
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
index 8e27226..8f8d59e 100644
--- a/core/res/res/values/integers.xml
+++ b/core/res/res/values/integers.xml
@@ -23,4 +23,7 @@
     <integer name="button_pressed_animation_delay">100</integer>
     <integer name="disabled_alpha_animation_duration">100</integer>
     <integer name="dock_enter_exit_duration">250</integer>
+
+    <integer name="date_picker_mode">1</integer>
+    <integer name="time_picker_mode">1</integer>
 </resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index db418f7..a9c8a06 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -652,7 +652,7 @@
     </style>
 
     <style name="Widget.Material.TimePicker">
-        <item name="timePickerMode">clock</item>
+        <item name="timePickerMode">@integer/time_picker_mode</item>
         <item name="legacyLayout">@layout/time_picker_legacy_material</item>
         <!-- Attributes for new-style TimePicker. -->
         <item name="internalLayout">@layout/time_picker_material</item>
@@ -666,7 +666,7 @@
     </style>
 
     <style name="Widget.Material.DatePicker">
-        <item name="datePickerMode">calendar</item>
+        <item name="datePickerMode">@integer/date_picker_mode</item>
         <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
         <item name="calendarViewShown">true</item>
         <!-- Attributes for new-style DatePicker. -->
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index 5dd4e85..72f8b57 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -45,7 +45,7 @@
     void onTimeShiftCurrentPositionChanged(long timeMs, int seq);
 
     // For the recording session
-    void onTuned(int seq);
+    void onTuned(int seq, in Uri channelUri);
     void onRecordingStopped(in Uri recordedProgramUri, int seq);
     void onError(int error, int seq);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 60d6f0d..af76f90 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -42,7 +42,7 @@
     void onTimeShiftCurrentPositionChanged(long timeMs);
 
     // For the recording session
-    void onTuned();
+    void onTuned(in Uri channelUri);
     void onRecordingStopped(in Uri recordedProgramUri);
     void onError(int error);
 }
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 6e0f5f2..63e3edc 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -677,11 +677,12 @@
          * Constructs a new builder for {@link TvInputInfo}.
          *
          * @param context A Context of the application package implementing this class.
-         * @param cls The component class that is to be used for the {@link TvInputService}.
+         * @param component The name of the application component to be used for the
+         *            {@link TvInputService}.
          */
-        public Builder(Context context, Class<?> cls) {
+        public Builder(Context context, ComponentName component) {
             mContext = context;
-            Intent intent = new Intent(TvInputService.SERVICE_INTERFACE).setClass(context, cls);
+            Intent intent = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(component);
             mResolveInfo = context.getPackageManager().resolveService(intent,
                     PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
         }
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 2703b1a..59dfbee 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -478,8 +478,10 @@
         /**
          * This is called when the recording session has been tuned to the given channel and is
          * ready to start recording.
+         *
+         * @param channelUri The URI of a channel.
          */
-        void onTuned(Session session) {
+        void onTuned(Session session, Uri channelUri) {
         }
 
         // For the recording session only
@@ -653,11 +655,11 @@
         }
 
         // For the recording session only
-        void postTuned() {
+        void postTuned(final Uri channelUri) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mSessionCallback.onTuned(mSession);
+                    mSessionCallback.onTuned(mSession, channelUri);
                 }
             });
         }
@@ -1013,14 +1015,14 @@
             }
 
             @Override
-            public void onTuned(int seq) {
+            public void onTuned(int seq, Uri channelUri) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
                     if (record == null) {
                         Log.e(TAG, "Callback not found for seq " + seq);
                         return;
                     }
-                    record.postTuned();
+                    record.postTuned(channelUri);
                 }
             }
 
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index db851a3..8fb58b5 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1567,8 +1567,10 @@
          * passed channel and call this method to indicate that it is now available for immediate
          * recording. When {@link #onStartRecording(Uri)} is called, recording must start with
          * minimal delay.
+         *
+         * @param channelUri The URI of a channel.
          */
-        public void notifyTuned() {
+        public void notifyTuned(Uri channelUri) {
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
                 @Override
@@ -1576,7 +1578,7 @@
                     try {
                         if (DEBUG) Log.d(TAG, "notifyTuned");
                         if (mSessionCallback != null) {
-                            mSessionCallback.onTuned();
+                            mSessionCallback.onTuned(channelUri);
                         }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in notifyTuned", e);
@@ -1679,7 +1681,7 @@
          * <p>The application may call this method before starting or after stopping recording, but
          * not during recording.
          *
-         * <p>The session must call {@link #notifyTuned()} if the tune request was fulfilled, or
+         * <p>The session must call {@link #notifyTuned(Uri)} if the tune request was fulfilled, or
          * {@link #notifyError(int)} otherwise.
          *
          * @param channelUri The URI of a channel.
@@ -1708,8 +1710,8 @@
          * Called when the application requests to start TV program recording. Recording must start
          * immediately when this method is called.
          *
-         * <p>The application may supply the URI for a TV program as a hint for filling in program
-         * specific data fields in the {@link android.media.tv.TvContract.RecordedPrograms} table.
+         * <p>The application may supply the URI for a TV program for filling in program specific
+         * data fields in the {@link android.media.tv.TvContract.RecordedPrograms} table.
          * A non-null {@code programHint} implies the started recording should be of that specific
          * program, whereas null {@code programHint} does not impose such a requirement and the
          * recording can span across multiple TV programs. In either case, the application must call
@@ -1718,10 +1720,10 @@
          * <p>The session must call {@link #notifyError(int)} if the start request cannot be
          * fulfilled.
          *
-         * @param programHint The URI for the TV program to record as a hint, built by
+         * @param programUri The URI for the TV program to record, built by
          *            {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
          */
-        public abstract void onStartRecording(@Nullable Uri programHint);
+        public abstract void onStartRecording(@Nullable Uri programUri);
 
         /**
          * Called when the application requests to stop TV program recording. Recording must stop
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index 1c920f5..d48ea21e 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -76,11 +76,12 @@
      * during recording.
      *
      * <p>The recording session will respond by calling
-     * {@link RecordingCallback#onTuned()} if the tune request was fulfilled, or
+     * {@link RecordingCallback#onTuned(Uri)} if the tune request was fulfilled, or
      * {@link RecordingCallback#onError(int)} otherwise.
      *
      * @param inputId The ID of the TV input for the given channel.
      * @param channelUri The URI of a channel.
+     * @throws IllegalStateException If recording is already started.
      */
     public void tune(String inputId, Uri channelUri) {
         tune(inputId, channelUri, null);
@@ -102,6 +103,7 @@
      * @param inputId The ID of the TV input for the given channel.
      * @param channelUri The URI of a channel.
      * @param params Extra parameters.
+     * @throws IllegalStateException If recording is already started.
      * @hide
      */
     @SystemApi
@@ -152,8 +154,8 @@
      * immediately when this method is called. If the current recording session has not yet tuned to
      * any channel, this method throws an exception.
      *
-     * <p>The application may supply the URI for a TV program as a hint for filling in program
-     * specific data fields in the {@link android.media.tv.TvContract.RecordedPrograms} table.
+     * <p>The application may supply the URI for a TV program for filling in program specific data
+     * fields in the {@link android.media.tv.TvContract.RecordedPrograms} table.
      * A non-null {@code programHint} implies the started recording should be of that specific
      * program, whereas null {@code programHint} does not impose such a requirement and the
      * recording can span across multiple TV programs. In either case, the application must call
@@ -162,15 +164,16 @@
      * <p>The recording session will respond by calling {@link RecordingCallback#onError(int)} if
      * the start request cannot be fulfilled.
      *
-     * @param programHint The URI for the TV program to record as a hint, built by
+     * @param programUri The URI for the TV program to record, built by
      *            {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
+     * @throws IllegalStateException If {@link #tune} request hasn't been handled yet.
      */
-    public void startRecording(@Nullable Uri programHint) {
+    public void startRecording(@Nullable Uri programUri) {
         if (!mIsTuned) {
             throw new IllegalStateException("startRecording failed - not yet tuned");
         }
         if (mSession != null) {
-            mSession.startRecording(programHint);
+            mSession.startRecording(programUri);
             mIsRecordingStarted = true;
         }
     }
@@ -245,8 +248,10 @@
         /**
          * This is called when the recording session has been tuned to the given channel and is
          * ready to start recording.
+         *
+         * @param channelUri The URI of a channel.
          */
-        public void onTuned() {
+        public void onTuned(Uri channelUri) {
         }
 
         /**
@@ -327,7 +332,7 @@
         }
 
         @Override
-        void onTuned(TvInputManager.Session session) {
+        void onTuned(TvInputManager.Session session, Uri channelUri) {
             if (DEBUG) {
                 Log.d(TAG, "onTuned()");
             }
@@ -336,7 +341,7 @@
                 return;
             }
             mIsTuned = true;
-            mCallback.onTuned();
+            mCallback.onTuned(channelUri);
         }
 
         @Override
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index b169ec8..29f65e09 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -73,7 +73,8 @@
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
-            android:orientation="vertical" >
+            android:orientation="vertical"
+            android:layout_gravity="center_vertical" >
 
             <TextView
                 android:id="@android:id/title"
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index ab67a51..6856129c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -115,7 +115,13 @@
      * Gather roots from all known storage providers.
      */
     public void updateAsync() {
-        // Verifying an assumption about the recents root being immutable.
+
+        // NOTE: This method is called when the UI language changes.
+        // For that reason we upadte our RecentsRoot to reflect
+        // the current language.
+        mRecentsRoot.title = mContext.getString(R.string.root_recent);
+
+        // Nothing else about the root should ever change.
         assert(mRecentsRoot.authority == null);
         assert(mRecentsRoot.rootId == null);
         assert(mRecentsRoot.derivedIcon == R.drawable.ic_root_recent);
@@ -123,7 +129,6 @@
         assert(mRecentsRoot.flags == (Root.FLAG_LOCAL_ONLY
                 | Root.FLAG_SUPPORTS_IS_CHILD
                 | Root.FLAG_SUPPORTS_CREATE));
-        assert(mRecentsRoot.title == mContext.getString(R.string.root_recent));
         assert(mRecentsRoot.availableBytes == -1);
 
         new UpdateTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java
index b940ffb..11f9aa7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java
@@ -29,6 +29,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.documentsui.R;
@@ -38,9 +39,10 @@
 
 final class ListDocumentHolder extends DocumentHolder {
     final TextView mTitle;
-    final TextView mSummary;
+    final LinearLayout mDetails;  // Container of date/size/summary
     final TextView mDate;
     final TextView mSize;
+    final TextView mSummary;
     final ImageView mIconMime;
     final ImageView mIconThumb;
     final ImageView mIconCheck;
@@ -50,9 +52,10 @@
         super(context, parent, R.layout.item_doc_list);
 
         mTitle = (TextView) itemView.findViewById(android.R.id.title);
-        mSummary = (TextView) itemView.findViewById(android.R.id.summary);
+        mDetails = (LinearLayout) itemView.findViewById(R.id.line2);
         mDate = (TextView) itemView.findViewById(R.id.date);
         mSize = (TextView) itemView.findViewById(R.id.size);
+        mSummary = (TextView) itemView.findViewById(android.R.id.summary);
         mIconMime = (ImageView) itemView.findViewById(R.id.icon_mime);
         mIconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb);
         mIconCheck = (ImageView) itemView.findViewById(R.id.icon_check);
@@ -91,6 +94,7 @@
         final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
         final String docSummary = getCursorString(cursor, Document.COLUMN_SUMMARY);
         final long docSize = getCursorLong(cursor, Document.COLUMN_SIZE);
+        final boolean isDirectory = Document.MIME_TYPE_DIR.equals(docMimeType);
 
         mIconHelper.stopLoading(mIconThumb);
 
@@ -105,24 +109,36 @@
         mTitle.setText(docDisplayName, TextView.BufferType.SPANNABLE);
         mTitle.setVisibility(View.VISIBLE);
 
-        if (docSummary != null) {
-            mSummary.setText(docSummary);
-            mSummary.setVisibility(View.VISIBLE);
-        } else {
-            mSummary.setVisibility(View.INVISIBLE);
-        }
 
-        if (docLastModified == -1) {
-            mDate.setText(null);
+        // Note, we don't show any details for any directory...ever.
+        if (isDirectory) {
+            mDetails.setVisibility(View.GONE);
         } else {
-            mDate.setText(Shared.formatTime(mContext, docLastModified));
-        }
+            boolean hasDetails = false;
+            if (docSummary != null) {
+                hasDetails = true;
+                mSummary.setText(docSummary);
+                mSummary.setVisibility(View.VISIBLE);
+            } else {
+                mSummary.setVisibility(View.INVISIBLE);
+            }
 
-        if (!state.showSize || Document.MIME_TYPE_DIR.equals(docMimeType) || docSize == -1) {
-            mSize.setVisibility(View.GONE);
-        } else {
-            mSize.setVisibility(View.VISIBLE);
-            mSize.setText(Formatter.formatFileSize(mContext, docSize));
+            if (docLastModified == -1) {
+                hasDetails = true;
+                mDate.setText(null);
+            } else {
+                mDate.setText(Shared.formatTime(mContext, docLastModified));
+            }
+
+            if (!state.showSize || docSize == -1) {
+                hasDetails = true;
+                mSize.setVisibility(View.GONE);
+                mDetails.setVisibility(View.GONE);
+            } else {
+                mSize.setVisibility(View.VISIBLE);
+                mSize.setText(Formatter.formatFileSize(mContext, docSize));
+            }
+            mDetails.setVisibility(hasDetails ? View.VISIBLE : View.GONE);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index ea7270d..4b775a5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -38,7 +38,9 @@
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
 import android.text.TextUtils;
+import android.util.Pair;
 import android.util.Slog;
+import android.widget.Toast;
 
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -46,6 +48,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.bluetooth.Utils;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
 
@@ -76,8 +79,9 @@
     private static final int STATE_WAITING_FOR_BLUETOOTH = 4;
     private static final int STATE_PAIRING = 5;
     private static final int STATE_PAIRED = 6;
-    private static final int STATE_USER_CANCELLED = 7;
-    private static final int STATE_DEVICE_NOT_FOUND = 8;
+    private static final int STATE_PAIRING_FAILED = 7;
+    private static final int STATE_USER_CANCELLED = 8;
+    private static final int STATE_DEVICE_NOT_FOUND = 9;
 
     private static final int MSG_INIT = 0;
     private static final int MSG_ON_BOOT_COMPLETED = 1;
@@ -90,6 +94,7 @@
     private static final int MSG_SHOW_BLUETOOTH_DIALOG = 8;
     private static final int MSG_DISMISS_BLUETOOTH_DIALOG = 9;
     private static final int MSG_BLE_ABORT_SCAN = 10;
+    private static final int MSG_SHOW_ERROR = 11;
 
     private volatile KeyboardHandler mHandler;
     private volatile KeyboardUIHandler mUIHandler;
@@ -178,6 +183,7 @@
         mLocalBluetoothAdapter = bluetoothManager.getBluetoothAdapter();
         mProfileManager = bluetoothManager.getProfileManager();
         bluetoothManager.getEventManager().registerCallback(new BluetoothCallbackHandler());
+        Utils.setErrorListener(new BluetoothErrorListener());
 
         InputManager im = context.getSystemService(InputManager.class);
         im.registerOnTabletModeChangedListener(this, mHandler);
@@ -204,13 +210,15 @@
         if (mInTabletMode != InputManager.SWITCH_STATE_OFF) {
             if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY) {
                 stopScanning();
+            } else if (mState == STATE_WAITING_FOR_BLUETOOTH) {
+                mUIHandler.sendEmptyMessage(MSG_DISMISS_BLUETOOTH_DIALOG);
             }
             mState = STATE_WAITING_FOR_TABLET_MODE_EXIT;
             return;
         }
 
         final int btState = mLocalBluetoothAdapter.getState();
-        if (btState == BluetoothAdapter.STATE_TURNING_ON || btState == BluetoothAdapter.STATE_ON
+        if ((btState == BluetoothAdapter.STATE_TURNING_ON || btState == BluetoothAdapter.STATE_ON)
                 && mState == STATE_WAITING_FOR_BLUETOOTH) {
             // If we're waiting for bluetooth but it has come on in the meantime, or is coming
             // on, just dismiss the dialog. This frequently happens during device startup.
@@ -334,7 +342,10 @@
 
     private void stopScanning() {
         if (mScanCallback != null) {
-            mLocalBluetoothAdapter.getBluetoothLeScanner().stopScan(mScanCallback);
+            BluetoothLeScanner scanner = mLocalBluetoothAdapter.getBluetoothLeScanner();
+            if (scanner != null) {
+                scanner.stopScan(mScanCallback);
+            }
             mScanCallback = null;
         }
     }
@@ -370,10 +381,14 @@
 
     // Should only be called on the handler thread
     private void onDeviceBondStateChangedInternal(CachedBluetoothDevice d, int bondState) {
-        if (d.getName().equals(mKeyboardName) && bondState == BluetoothDevice.BOND_BONDED) {
-            // We don't need to manually connect to the device here because it will automatically
-            // try to connect after it has been paired.
-            mState = STATE_PAIRED;
+        if (mState == STATE_PAIRING && d.getName().equals(mKeyboardName)) {
+            if (bondState == BluetoothDevice.BOND_BONDED) {
+                // We don't need to manually connect to the device here because it will
+                // automatically try to connect after it has been paired.
+                mState = STATE_PAIRED;
+            } else if (bondState == BluetoothDevice.BOND_NONE) {
+                mState = STATE_PAIRING_FAILED;
+            }
         }
     }
 
@@ -385,6 +400,17 @@
         }
     }
 
+    // Should only be called on the handler thread. We want to be careful not to show errors for
+    // pairings not initiated by this UI, so we only pop up the toast when we're at an appropriate
+    // point in our pairing flow and it's the expected device.
+    private void onShowErrorInternal(Context context, String name, int messageResId) {
+        if ((mState == STATE_PAIRING || mState == STATE_PAIRING_FAILED)
+                && mKeyboardName.equals(name)) {
+            String message = context.getString(messageResId, name);
+            Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
+        }
+    }
+
     private final class KeyboardUIHandler extends Handler {
         public KeyboardUIHandler() {
             super(Looper.getMainLooper(), null, true /*async*/);
@@ -393,19 +419,27 @@
         public void handleMessage(Message msg) {
             switch(msg.what) {
                 case MSG_SHOW_BLUETOOTH_DIALOG: {
-                    DialogInterface.OnClickListener listener = new BluetoothDialogClickListener();
+                    if (mDialog != null) {
+                        // Don't show another dialog if one is already present
+                        break;
+                    }
+                    DialogInterface.OnClickListener clickListener =
+                            new BluetoothDialogClickListener();
+                    DialogInterface.OnDismissListener dismissListener =
+                            new BluetoothDialogDismissListener();
                     mDialog = new BluetoothDialog(mContext);
                     mDialog.setTitle(R.string.enable_bluetooth_title);
                     mDialog.setMessage(R.string.enable_bluetooth_message);
-                    mDialog.setPositiveButton(R.string.enable_bluetooth_confirmation_ok, listener);
-                    mDialog.setNegativeButton(android.R.string.cancel, listener);
+                    mDialog.setPositiveButton(
+                            R.string.enable_bluetooth_confirmation_ok, clickListener);
+                    mDialog.setNegativeButton(android.R.string.cancel, clickListener);
+                    mDialog.setOnDismissListener(dismissListener);
                     mDialog.show();
                     break;
                 }
                 case MSG_DISMISS_BLUETOOTH_DIALOG: {
                     if (mDialog != null) {
                         mDialog.dismiss();
-                        mDialog = null;
                     }
                     break;
                 }
@@ -469,6 +503,10 @@
                     onBleScanFailedInternal();
                     break;
                 }
+                case MSG_SHOW_ERROR: {
+                    Pair<Context, String> p = (Pair<Context, String>) msg.obj;
+                    onShowErrorInternal(p.first, p.second, msg.arg1);
+                }
             }
         }
     }
@@ -482,6 +520,14 @@
         }
     }
 
+    private final class BluetoothDialogDismissListener
+            implements DialogInterface.OnDismissListener {
+        @Override
+        public void onDismiss(DialogInterface dialog) {
+            mDialog = null;
+        }
+    }
+
     private final class KeyboardScanCallback extends ScanCallback {
 
         private boolean isDeviceDiscoverable(ScanResult result) {
@@ -564,6 +610,13 @@
         public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
     }
 
+    private final class BluetoothErrorListener implements Utils.ErrorListener {
+        public void onShowError(Context context, String name, int messageResId) {
+            mHandler.obtainMessage(MSG_SHOW_ERROR, messageResId, 0 /*unused*/,
+                    new Pair<>(context, name)).sendToTarget();
+        }
+    }
+
     private static String stateToString(int state) {
         switch (state) {
             case STATE_NOT_ENABLED:
@@ -580,13 +633,15 @@
                 return "STATE_PAIRING";
             case STATE_PAIRED:
                 return "STATE_PAIRED";
+            case STATE_PAIRING_FAILED:
+                return "STATE_PAIRING_FAILED";
             case STATE_USER_CANCELLED:
                 return "STATE_USER_CANCELLED";
             case STATE_DEVICE_NOT_FOUND:
                 return "STATE_DEVICE_NOT_FOUND";
             case STATE_UNKNOWN:
             default:
-                return "STATE_UNKNOWN";
+                return "STATE_UNKNOWN (" + state + ")";
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 53fd446..b6a3234 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -37,6 +37,8 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -99,7 +101,6 @@
     private boolean mSimpleUserSwitcher;
     private boolean mAddUsersWhenLocked;
     private boolean mPauseRefreshUsers;
-    private boolean mAllowUserSwitchingWhenSystemUserLocked;
     private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
 
     public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
@@ -140,6 +141,7 @@
         mSettingsObserver.onChange(false);
 
         keyguardMonitor.addCallback(mCallback);
+        listenForCallState();
 
         refreshUsers(UserHandle.USER_NULL);
     }
@@ -186,8 +188,7 @@
                 }
                 ArrayList<UserRecord> records = new ArrayList<>(infos.size());
                 int currentId = ActivityManager.getCurrentUser();
-                boolean allowUserSwitching = mAllowUserSwitchingWhenSystemUserLocked
-                        || mUserManager.isUserUnlocked(UserHandle.SYSTEM);
+                boolean canSwitchUsers = mUserManager.canSwitchUsers();
                 UserInfo currentUserInfo = null;
                 UserRecord guestRecord = null;
                 int avatarSize = mContext.getResources()
@@ -198,12 +199,14 @@
                     if (isCurrent) {
                         currentUserInfo = info;
                     }
-                    boolean switchToEnabled = allowUserSwitching || isCurrent;
+                    boolean switchToEnabled = canSwitchUsers || isCurrent;
                     if (info.isEnabled()) {
                         if (info.isGuest()) {
+                            // Tapping guest icon triggers remove and a user switch therefore
+                            // the icon shouldn't be enabled even if the user is current
                             guestRecord = new UserRecord(info, null /* picture */,
                                     true /* isGuest */, isCurrent, false /* isAddUser */,
-                                    false /* isRestricted */, switchToEnabled);
+                                    false /* isRestricted */, canSwitchUsers);
                         } else if (info.supportsSwitchToByUser()) {
                             Bitmap picture = bitmaps.get(info.id);
                             if (picture == null) {
@@ -240,7 +243,7 @@
                         if (canCreateGuest) {
                             guestRecord = new UserRecord(null /* info */, null /* picture */,
                                     true /* isGuest */, false /* isCurrent */,
-                                    false /* isAddUser */, createIsRestricted, allowUserSwitching);
+                                    false /* isAddUser */, createIsRestricted, canSwitchUsers);
                             checkIfAddUserDisallowedByAdminOnly(guestRecord);
                             records.add(guestRecord);
                         }
@@ -253,7 +256,7 @@
                 if (!mSimpleUserSwitcher && canCreateUser) {
                     UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
                             false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
-                            createIsRestricted, allowUserSwitching);
+                            createIsRestricted, canSwitchUsers);
                     checkIfAddUserDisallowedByAdminOnly(addUserRecord);
                     records.add(addUserRecord);
                 }
@@ -359,19 +362,6 @@
         switchToUserId(id);
     }
 
-    public void switchTo(int userId) {
-        final int count = mUsers.size();
-        for (int i = 0; i < count; ++i) {
-            UserRecord record = mUsers.get(i);
-            if (record.info != null && record.info.id == userId) {
-                switchTo(record);
-                return;
-            }
-        }
-
-        Log.e(TAG, "Couldn't switch to user, id=" + userId);
-    }
-
     private void switchToUserId(int id) {
         try {
             pauseRefreshUsers();
@@ -417,6 +407,24 @@
         mUserManager.removeUser(id);
     }
 
+    private void listenForCallState() {
+        TelephonyManager.from(mContext).listen(new PhoneStateListener() {
+            private int mCallState;
+            @Override
+            public void onCallStateChanged(int state, String incomingNumber) {
+                if (mCallState == state) return;
+                if (DEBUG) Log.v(TAG, "Call state changed: " + state);
+                mCallState = state;
+                int currentUserId = ActivityManager.getCurrentUser();
+                UserInfo userInfo = mUserManager.getUserInfo(currentUserId);
+                if (userInfo != null && userInfo.isGuest()) {
+                    showGuestNotification(currentUserId);
+                }
+                refreshUsers(UserHandle.USER_NULL);
+            }
+        }, PhoneStateListener.LISTEN_CALL_STATE);
+    }
+
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -488,25 +496,6 @@
             }
         }
 
-        private void showGuestNotification(int guestUserId) {
-            PendingIntent removeGuestPI = PendingIntent.getBroadcastAsUser(mContext,
-                    0, new Intent(ACTION_REMOVE_GUEST), 0, UserHandle.SYSTEM);
-            Notification notification = new Notification.Builder(mContext)
-                    .setVisibility(Notification.VISIBILITY_SECRET)
-                    .setPriority(Notification.PRIORITY_MIN)
-                    .setSmallIcon(R.drawable.ic_person)
-                    .setContentTitle(mContext.getString(R.string.guest_notification_title))
-                    .setContentText(mContext.getString(R.string.guest_notification_text))
-                    .setContentIntent(removeGuestPI)
-                    .setShowWhen(false)
-                    .addAction(R.drawable.ic_delete,
-                            mContext.getString(R.string.guest_notification_remove_action),
-                            removeGuestPI)
-                    .build();
-            NotificationManager.from(mContext).notifyAsUser(TAG_REMOVE_GUEST, ID_REMOVE_GUEST,
-                    notification, new UserHandle(guestUserId));
-        }
-
         private void showLogoutNotification(int userId) {
             PendingIntent logoutPI = PendingIntent.getBroadcastAsUser(mContext,
                     0, new Intent(ACTION_LOGOUT_USER), 0, UserHandle.SYSTEM);
@@ -528,6 +517,28 @@
         }
     };
 
+    private void showGuestNotification(int guestUserId) {
+        boolean canSwitchUsers = mUserManager.canSwitchUsers();
+        // Disable 'Remove guest' action if cannot switch users right now
+        PendingIntent removeGuestPI = canSwitchUsers ? PendingIntent.getBroadcastAsUser(mContext,
+                0, new Intent(ACTION_REMOVE_GUEST), 0, UserHandle.SYSTEM) : null;
+
+        Notification notification = new Notification.Builder(mContext)
+                .setVisibility(Notification.VISIBILITY_SECRET)
+                .setPriority(Notification.PRIORITY_MIN)
+                .setSmallIcon(R.drawable.ic_person)
+                .setContentTitle(mContext.getString(R.string.guest_notification_title))
+                .setContentText(mContext.getString(R.string.guest_notification_text))
+                .setContentIntent(removeGuestPI)
+                .setShowWhen(false)
+                .addAction(R.drawable.ic_delete,
+                        mContext.getString(R.string.guest_notification_remove_action),
+                        removeGuestPI)
+                .build();
+        NotificationManager.from(mContext).notifyAsUser(TAG_REMOVE_GUEST, ID_REMOVE_GUEST,
+                notification, new UserHandle(guestUserId));
+    }
+
     private final Runnable mUnpauseRefreshUsers = new Runnable() {
         @Override
         public void run() {
@@ -543,9 +554,6 @@
                     SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0;
             mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(),
                     Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
-            mAllowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt(
-                    mContext.getContentResolver(),
-                    Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED, 0) != 0;
             refreshUsers(UserHandle.USER_NULL);
         };
     };
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index a5dc008..a626b5b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -16,6 +16,8 @@
 
 package com.android.server.net;
 
+import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
 import static android.net.wifi.WifiInfo.removeDoubleQuotes;
 import static com.android.server.net.NetworkPolicyManagerService.newWifiPolicy;
 import static com.android.server.net.NetworkPolicyManagerService.TAG;
@@ -83,6 +85,8 @@
         pw.println("");
         pw.println("  add restrict-background-whitelist UID");
         pw.println("    Adds a UID to the whitelist for restrict background usage.");
+        pw.println("  add restrict-background-blacklist UID");
+        pw.println("    Adds a UID to the blacklist for restrict background usage.");
         pw.println("  get metered-network ID");
         pw.println("    Checks whether the given non-mobile network is metered or not.");
         pw.println("  get restrict-background");
@@ -93,8 +97,12 @@
         pw.println("    networks.");
         pw.println("  list restrict-background-whitelist");
         pw.println("    Lists UIDs that are whitelisted for restrict background usage.");
+        pw.println("  list restrict-background-blacklist");
+        pw.println("    Lists UIDs that are blacklisted for restrict background usage.");
         pw.println("  remove restrict-background-whitelist UID");
         pw.println("    Removes a UID from the whitelist for restrict background usage.");
+        pw.println("  remove restrict-background-blacklist UID");
+        pw.println("    Removes a UID from the blacklist for restrict background usage.");
         pw.println("  set metered-network ID BOOLEAN");
         pw.println("    Toggles whether the given non-mobile network is metered.");
         pw.println("  set restrict-background BOOLEAN");
@@ -147,6 +155,8 @@
                 return listMeteredWifiNetworks();
             case "restrict-background-whitelist":
                 return listRestrictBackgroundWhitelist();
+            case "restrict-background-blacklist":
+                return listRestrictBackgroundBlacklist();
         }
         pw.println("Error: unknown list type '" + type + "'");
         return -1;
@@ -162,6 +172,8 @@
         switch(type) {
             case "restrict-background-whitelist":
                 return addRestrictBackgroundWhitelist();
+            case "restrict-background-blacklist":
+                return addRestrictBackgroundBlacklist();
         }
         pw.println("Error: unknown add type '" + type + "'");
         return -1;
@@ -177,6 +189,8 @@
         switch(type) {
             case "restrict-background-whitelist":
                 return removeRestrictBackgroundWhitelist();
+            case "restrict-background-blacklist":
+                return removeRestrictBackgroundBlacklist();
         }
         pw.println("Error: unknown remove type '" + type + "'");
         return -1;
@@ -199,6 +213,24 @@
         return 0;
     }
 
+    private int listRestrictBackgroundBlacklist() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+
+        final int[] uids = mInterface.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND);
+        pw.print("Restrict background blacklisted UIDs: ");
+        if (uids.length == 0) {
+            pw.println("none");
+        } else {
+            for (int i = 0; i < uids.length; i++) {
+                int uid = uids[i];
+                pw.print(uid);
+                pw.print(' ');
+            }
+        }
+        pw.println();
+        return 0;
+    }
+
     private int getRestrictBackground() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         pw.print("Restrict background status: ");
@@ -233,6 +265,24 @@
         return 0;
     }
 
+    private int addRestrictBackgroundBlacklist() throws RemoteException {
+        final int uid = getUidFromNextArg();
+        if (uid < 0) {
+            return uid;
+        }
+        mInterface.setUidPolicy(uid, POLICY_REJECT_METERED_BACKGROUND);
+        return 0;
+    }
+
+    private int removeRestrictBackgroundBlacklist() throws RemoteException {
+        final int uid = getUidFromNextArg();
+        if (uid < 0) {
+            return uid;
+        }
+        mInterface.setUidPolicy(uid, POLICY_NONE);
+        return 0;
+    }
+
     private int listMeteredWifiNetworks() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         final String arg = getNextArg();
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 8a2729e..49aaa4a 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -2512,7 +2512,7 @@
 
         // For the recording session only
         @Override
-        public void onTuned() {
+        public void onTuned(Uri channelUri) {
             synchronized (mLock) {
                 if (DEBUG) {
                     Slog.d(TAG, "onTuned()");
@@ -2521,7 +2521,7 @@
                     return;
                 }
                 try {
-                    mSessionState.client.onTuned(mSessionState.seq);
+                    mSessionState.client.onTuned(mSessionState.seq, channelUri);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error in onTuned", e);
                 }
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 2f076d1..7611527 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -15,9 +15,12 @@
  */
 package com.android.server.vr;
 
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.IBinder;
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -40,6 +43,9 @@
     private static native void setVrModeNative(boolean enabled);
 
     private final Object mLock = new Object();
+
+    private final IBinder mOverlayToken = new Binder();
+
     private boolean mVrModeEnabled = false;
     private ArraySet<VrStateListener> mListeners = new ArraySet<>();
 
@@ -97,11 +103,25 @@
                 // Log mode change event.
                 Slog.i(TAG, "VR mode " + ((mVrModeEnabled) ? "enabled" : "disabled"));
                 setVrModeNative(mVrModeEnabled);
+                updateOverlayStateLocked();
                 onVrModeChangedLocked();
             }
         }
     }
 
+    private void updateOverlayStateLocked() {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+            if (appOpsManager != null) {
+                appOpsManager.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+                        mVrModeEnabled, mOverlayToken);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     private boolean getVrMode() {
         synchronized (mLock) {
             return mVrModeEnabled;