Merge "[ActivityManager] Fix IndexOutOfBoundsException" am: a61515720a
am: 6c8b91d8c2

* commit '6c8b91d8c2c657eb8a5b0afa00a3b93044447f56':
  [ActivityManager] Fix IndexOutOfBoundsException
diff --git a/Android.mk b/Android.mk
index 791a70a..ce01c20 100644
--- a/Android.mk
+++ b/Android.mk
@@ -348,6 +348,7 @@
 	media/java/android/media/IRingtonePlayer.aidl \
 	media/java/android/media/IVolumeController.aidl \
 	media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl \
+	media/java/android/media/midi/IBluetoothMidiService.aidl \
 	media/java/android/media/midi/IMidiDeviceListener.aidl \
 	media/java/android/media/midi/IMidiDeviceOpenCallback.aidl \
 	media/java/android/media/midi/IMidiDeviceServer.aidl \
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2cb3f39..ecd0050 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -689,6 +689,8 @@
     private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
     private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_";
     private static final String SAVED_DIALOG_ARGS_KEY_PREFIX = "android:dialog_args_";
+    private static final String HAS_CURENT_PERMISSIONS_REQUEST_KEY =
+            "android:hasCurrentPermissionsRequest";
 
     private static final String REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:";
 
@@ -797,6 +799,8 @@
     SharedElementCallback mEnterTransitionListener = SharedElementCallback.NULL_CALLBACK;
     SharedElementCallback mExitTransitionListener = SharedElementCallback.NULL_CALLBACK;
 
+    private boolean mHasCurrentPermissionsRequest;
+
     /** Return the intent that started this activity. */
     public Intent getIntent() {
         return mIntent;
@@ -1298,6 +1302,7 @@
         onSaveInstanceState(outState);
         saveManagedDialogs(outState);
         mActivityTransitionState.saveState(outState);
+        storeHasCurrentPermissionRequest(outState);
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
     }
 
@@ -1313,6 +1318,7 @@
     final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
         onSaveInstanceState(outState, outPersistentState);
         saveManagedDialogs(outState);
+        storeHasCurrentPermissionRequest(outState);
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState +
                 ", " + outPersistentState);
     }
@@ -3811,8 +3817,15 @@
      * @see #shouldShowRequestPermissionRationale(String)
      */
     public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
+        if (mHasCurrentPermissionsRequest) {
+            Log.w(TAG, "Can reqeust only one set of permissions at a time");
+            // Dispatch the callback with empty arrays which means a cancellation.
+            onRequestPermissionsResult(requestCode, new String[0], new int[0]);
+            return;
+        }
         Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
         startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
+        mHasCurrentPermissionsRequest = true;
     }
 
     /**
@@ -6234,12 +6247,14 @@
     }
 
     final void performCreate(Bundle icicle) {
+        restoreHasCurrentPermissionRequest(icicle);
         onCreate(icicle);
         mActivityTransitionState.readState(icicle);
         performCreateCommon();
     }
 
     final void performCreate(Bundle icicle, PersistableBundle persistentState) {
+        restoreHasCurrentPermissionRequest(icicle);
         onCreate(icicle, persistentState);
         mActivityTransitionState.readState(icicle);
         performCreateCommon();
@@ -6418,6 +6433,19 @@
         return mResumed;
     }
 
+    private void storeHasCurrentPermissionRequest(Bundle bundle) {
+        if (bundle != null && mHasCurrentPermissionsRequest) {
+            bundle.putBoolean(HAS_CURENT_PERMISSIONS_REQUEST_KEY, true);
+        }
+    }
+
+    private void restoreHasCurrentPermissionRequest(Bundle bundle) {
+        if (bundle != null) {
+            mHasCurrentPermissionsRequest = bundle.getBoolean(
+                    HAS_CURENT_PERMISSIONS_REQUEST_KEY, false);
+        }
+    }
+
     void dispatchActivityResult(String who, int requestCode,
         int resultCode, Intent data) {
         if (false) Log.v(
@@ -6545,6 +6573,7 @@
     }
 
     private void dispatchRequestPermissionsResult(int requestCode, Intent data) {
+        mHasCurrentPermissionsRequest = false;
         // If the package installer crashed we may have not data - best effort.
         String[] permissions = (data != null) ? data.getStringArrayExtra(
                 PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES) : new String[0];
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0adce5d..7cae745 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1619,7 +1619,8 @@
         // System apps and apps demanding internal storage can't be moved
         // anywhere else
         if (app.isSystemApp()
-                || app.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+                || app.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY
+                || app.installLocation == PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
             return false;
         }
 
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index d50483e..29e8dd4 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -460,9 +460,6 @@
     // If set this fragment is being retained across the current config change.
     boolean mRetaining;
 
-    // If set this fragment's loaders are being retained across the current config change.
-    boolean mRetainLoader;
-
     // If set this fragment has menu items to contribute.
     boolean mHasMenu;
 
@@ -2404,7 +2401,7 @@
                 mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
             }
             if (mLoaderManager != null) {
-                if (mRetainLoader) {
+                if (mHost.getRetainLoaders()) {
                     mLoaderManager.doRetain();
                 } else {
                     mLoaderManager.doStop();
diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java
index 1b45137..28dadfa 100644
--- a/core/java/android/app/FragmentController.java
+++ b/core/java/android/app/FragmentController.java
@@ -341,7 +341,6 @@
      */
     public void doLoaderStop(boolean retain) {
         mHost.doLoaderStop(retain);
-        mHost.mFragmentManager.setRetainLoader(retain);
     }
 
     /**
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
index 7b01307..13517e6 100644
--- a/core/java/android/app/FragmentHostCallback.java
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -42,9 +42,14 @@
     private final Handler mHandler;
     final int mWindowAnimations;
     final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
+    /** The loader managers for individual fragments [i.e. Fragment#getLoaderManager()] */
     private ArrayMap<String, LoaderManager> mAllLoaderManagers;
+    /** Whether or not fragment loaders should retain their state */
+    private boolean mRetainLoaders;
+    /** The loader manger for the fragment host [i.e. Activity#getLoaderManager()] */
     private LoaderManagerImpl mLoaderManager;
     private boolean mCheckedForLoaderManager;
+    /** Whether or not the fragment host loader manager was started */
     private boolean mLoadersStarted;
 
     public FragmentHostCallback(Context context, Handler handler, int windowAnimations) {
@@ -166,6 +171,10 @@
         return true;
     }
 
+    boolean getRetainLoaders() {
+        return mRetainLoaders;
+    }
+
     Activity getActivity() {
         return mActivity;
     }
@@ -217,6 +226,8 @@
     }
 
     void doLoaderStop(boolean retain) {
+        mRetainLoaders = retain;
+
         if (mLoaderManager == null) {
             return;
         }
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 51d6132..696ccdb 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -869,17 +869,6 @@
         }
     }
 
-    void setRetainLoader(boolean retain) {
-        if (mActive != null) {
-            for (int i=0; i<mActive.size(); i++) {
-                Fragment f = mActive.get(i);
-                if (f != null) {
-                    f.mRetainLoader = retain;
-                }
-            }
-        }
-    }
-
     void moveToState(Fragment f, int newState, int transit, int transitionStyle,
             boolean keepActive) {
         if (DEBUG && false) Log.v(TAG, "moveToState: " + f
@@ -2221,6 +2210,7 @@
             // This fragment was retained from a previous instance; get it
             // going now.
             fragment.mInLayout = true;
+            fragment.mHost = mHost;
             // If this fragment is newly instantiated (either right now, or
             // from last saved state), then give it the attributes to
             // initialize itself.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4c7dd10..758b6ff 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -643,20 +643,18 @@
 
     /**
      * Open a private file associated with this Context's application package
-     * for writing.  Creates the file if it doesn't already exist.
-     *
-     * <p>No permissions are required to invoke this method, since it uses internal
-     * storage.
+     * for writing. Creates the file if it doesn't already exist.
+     * <p>
+     * No additional permissions are required for the calling app to read or
+     * write the returned file.
      *
      * @param name The name of the file to open; can not contain path
-     *             separators.
-     * @param mode Operating mode.  Use 0 or {@link #MODE_PRIVATE} for the
-     * default operation, {@link #MODE_APPEND} to append to an existing file,
-     * {@link #MODE_WORLD_READABLE} and {@link #MODE_WORLD_WRITEABLE} to control
-     * permissions.
-     *
+     *            separators.
+     * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the
+     *            default operation, {@link #MODE_APPEND} to append to an
+     *            existing file, {@link #MODE_WORLD_READABLE} and
+     *            {@link #MODE_WORLD_WRITEABLE} to control permissions.
      * @return The resulting {@link FileOutputStream}.
-     *
      * @see #MODE_APPEND
      * @see #MODE_PRIVATE
      * @see #MODE_WORLD_READABLE
@@ -689,6 +687,9 @@
     /**
      * Returns the absolute path on the filesystem where a file created with
      * {@link #openFileOutput} is stored.
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
      *
      * @param name The name of the file for which you would like to get
      *          its path.
@@ -702,14 +703,16 @@
     public abstract File getFileStreamPath(String name);
 
     /**
-     * Returns the absolute path to the directory on the filesystem where
-     * files created with {@link #openFileOutput} are stored.
-     *
-     * <p>No permissions are required to read or write to the returned path, since this
-     * path is internal storage.
+     * Returns the absolute path to the directory on the filesystem where files
+     * created with {@link #openFileOutput} are stored.
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     * <p>
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path.
      *
      * @return The path of the directory holding application files.
-     *
      * @see #openFileOutput
      * @see #getFileStreamPath
      * @see #getDir
@@ -718,17 +721,19 @@
 
     /**
      * Returns the absolute path to the directory on the filesystem similar to
-     * {@link #getFilesDir()}.  The difference is that files placed under this
-     * directory will be excluded from automatic backup to remote storage.  See
+     * {@link #getFilesDir()}. The difference is that files placed under this
+     * directory will be excluded from automatic backup to remote storage. See
      * {@link android.app.backup.BackupAgent BackupAgent} for a full discussion
      * of the automatic backup mechanism in Android.
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     * <p>
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path.
      *
-     * <p>No permissions are required to read or write to the returned path, since this
-     * path is internal storage.
-     *
-     * @return The path of the directory holding application files that will not be
-     *         automatically backed up to remote storage.
-     *
+     * @return The path of the directory holding application files that will not
+     *         be automatically backed up to remote storage.
      * @see #openFileOutput
      * @see #getFileStreamPath
      * @see #getDir
@@ -737,200 +742,256 @@
     public abstract File getNoBackupFilesDir();
 
     /**
-     * Returns the absolute path to the directory on the primary external filesystem
-     * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
-     * Environment.getExternalStorageDirectory()}) where the application can
-     * place persistent files it owns.  These files are internal to the
-     * applications, and not typically visible to the user as media.
-     *
-     * <p>This is like {@link #getFilesDir()} in that these
-     * files will be deleted when the application is uninstalled, however there
-     * are some important differences:
-     *
+     * Returns the absolute path to the directory on the primary shared/external
+     * storage device where the application can place persistent files it owns.
+     * These files are internal to the applications, and not typically visible
+     * to the user as media.
+     * <p>
+     * This is like {@link #getFilesDir()} in that these files will be deleted
+     * when the application is uninstalled, however there are some important
+     * differences:
      * <ul>
-     * <li>External files are not always available: they will disappear if the
-     * user mounts the external storage on a computer or removes it.  See the
-     * APIs on {@link android.os.Environment} for information in the storage state.
-     * <li>There is no security enforced with these files.  For example, any application
-     * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
      * these files.
      * </ul>
-     *
-     * <p>Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
+     * <p>
+     * If a shared storage device is emulated (as determined by
+     * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+     * backed by a private user data partition, which means there is little
+     * benefit to storing data here instead of the private directories returned
+     * by {@link #getFilesDir()}, etc.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
      * are required to read or write to the returned path; it's always
-     * accessible to the calling app.  This only applies to paths generated for
-     * package name of the calling application.  To access paths belonging
-     * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
-     * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
-     *
-     * <p>On devices with multiple users (as described by {@link UserManager}),
-     * each user has their own isolated external storage. Applications only
-     * have access to the external storage for the user they're running as.</p>
-     *
-     * <p>Here is an example of typical code to manipulate a file in
-     * an application's private storage:</p>
-     *
+     * accessible to the calling app. This only applies to paths generated for
+     * package name of the calling application. To access paths belonging to
+     * other packages,
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or
+     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
+     * <p>
+     * On devices with multiple users (as described by {@link UserManager}),
+     * each user has their own isolated shared storage. Applications only have
+     * access to the shared storage for the user they're running as.
+     * <p>
+     * The returned path may change over time if different shared storage media
+     * is inserted, so only relative paths should be persisted.
+     * <p>
+     * Here is an example of typical code to manipulate a file in an
+     * application's shared storage:
+     * </p>
      * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
      * private_file}
-     *
-     * <p>If you supply a non-null <var>type</var> to this function, the returned
-     * file will be a path to a sub-directory of the given type.  Though these files
-     * are not automatically scanned by the media scanner, you can explicitly
-     * add them to the media database with
-     * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[],
-     *      android.media.MediaScannerConnection.OnScanCompletedListener)
-     *      MediaScannerConnection.scanFile}.
-     * Note that this is not the same as
+     * <p>
+     * If you supply a non-null <var>type</var> to this function, the returned
+     * file will be a path to a sub-directory of the given type. Though these
+     * files are not automatically scanned by the media scanner, you can
+     * explicitly add them to the media database with
+     * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], android.media.MediaScannerConnection.OnScanCompletedListener)
+     * MediaScannerConnection.scanFile}. Note that this is not the same as
      * {@link android.os.Environment#getExternalStoragePublicDirectory
      * Environment.getExternalStoragePublicDirectory()}, which provides
-     * directories of media shared by all applications.  The
-     * directories returned here are
-     * owned by the application, and their contents will be removed when the
-     * application is uninstalled.  Unlike
+     * directories of media shared by all applications. The directories returned
+     * here are owned by the application, and their contents will be removed
+     * when the application is uninstalled. Unlike
      * {@link android.os.Environment#getExternalStoragePublicDirectory
-     * Environment.getExternalStoragePublicDirectory()}, the directory
-     * returned here will be automatically created for you.
-     *
-     * <p>Here is an example of typical code to manipulate a picture in
-     * an application's private storage and add it to the media database:</p>
-     *
+     * Environment.getExternalStoragePublicDirectory()}, the directory returned
+     * here will be automatically created for you.
+     * <p>
+     * Here is an example of typical code to manipulate a picture in an
+     * application's shared storage and add it to the media database:
+     * </p>
      * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
      * private_picture}
      *
-     * @param type The type of files directory to return.  May be null for
-     * the root of the files directory or one of
-     * the following Environment constants for a subdirectory:
-     * {@link android.os.Environment#DIRECTORY_MUSIC},
-     * {@link android.os.Environment#DIRECTORY_PODCASTS},
-     * {@link android.os.Environment#DIRECTORY_RINGTONES},
-     * {@link android.os.Environment#DIRECTORY_ALARMS},
-     * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
-     * {@link android.os.Environment#DIRECTORY_PICTURES}, or
-     * {@link android.os.Environment#DIRECTORY_MOVIES}.
-     *
-     * @return The path of the directory holding application files
-     * on external storage.  Returns null if external storage is not currently
-     * mounted so it could not ensure the path exists; you will need to call
-     * this method again when it is available.
-     *
+     * @param type The type of files directory to return. May be {@code null}
+     *            for the root of the files directory or one of the following
+     *            constants for a subdirectory:
+     *            {@link android.os.Environment#DIRECTORY_MUSIC},
+     *            {@link android.os.Environment#DIRECTORY_PODCASTS},
+     *            {@link android.os.Environment#DIRECTORY_RINGTONES},
+     *            {@link android.os.Environment#DIRECTORY_ALARMS},
+     *            {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
+     *            {@link android.os.Environment#DIRECTORY_PICTURES}, or
+     *            {@link android.os.Environment#DIRECTORY_MOVIES}.
+     * @return the absolute path to application-specific directory. May return
+     *         {@code null} if shared storage is not currently available.
      * @see #getFilesDir
-     * @see android.os.Environment#getExternalStoragePublicDirectory
+     * @see #getExternalFilesDirs(String)
+     * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
      */
     @Nullable
     public abstract File getExternalFilesDir(@Nullable String type);
 
     /**
      * Returns absolute paths to application-specific directories on all
-     * external storage devices where the application can place persistent files
-     * it owns. These files are internal to the application, and not typically
-     * visible to the user as media.
+     * shared/external storage devices where the application can place
+     * persistent files it owns. These files are internal to the application,
+     * and not typically visible to the user as media.
      * <p>
-     * This is like {@link #getFilesDir()} in that these files will be deleted when
-     * the application is uninstalled, however there are some important differences:
+     * This is like {@link #getFilesDir()} in that these files will be deleted
+     * when the application is uninstalled, however there are some important
+     * differences:
      * <ul>
-     * <li>External files are not always available: they will disappear if the
-     * user mounts the external storage on a computer or removes it.
-     * <li>There is no security enforced with these files.
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
      * </ul>
      * <p>
-     * External storage devices returned here are considered a permanent part of
-     * the device, including both emulated external storage and physical media
-     * slots, such as SD cards in a battery compartment. The returned paths do
-     * not include transient devices, such as USB flash drives.
+     * If a shared storage device is emulated (as determined by
+     * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+     * backed by a private user data partition, which means there is little
+     * benefit to storing data here instead of the private directories returned
+     * by {@link #getFilesDir()}, etc.
      * <p>
-     * An application may store data on any or all of the returned devices.  For
+     * Shared storage devices returned here are considered a stable part of the
+     * device, including physical media slots under a protective cover. The
+     * returned paths do not include transient devices, such as USB flash drives
+     * connected to handheld devices.
+     * <p>
+     * An application may store data on any or all of the returned devices. For
      * example, an app may choose to store large files on the device with the
      * most available space, as measured by {@link StatFs}.
      * <p>
-     * No permissions are required to read or write to the returned paths; they
-     * are always accessible to the calling app.  Write access outside of these
-     * paths on secondary external storage devices is not available.
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path. Write access outside of these paths
+     * on secondary external storage devices is not available.
      * <p>
-     * The first path returned is the same as {@link #getExternalFilesDir(String)}.
-     * Returned paths may be {@code null} if a storage device is unavailable.
+     * The returned path may change over time if different shared storage media
+     * is inserted, so only relative paths should be persisted.
      *
+     * @param type The type of files directory to return. May be {@code null}
+     *            for the root of the files directory or one of the following
+     *            constants for a subdirectory:
+     *            {@link android.os.Environment#DIRECTORY_MUSIC},
+     *            {@link android.os.Environment#DIRECTORY_PODCASTS},
+     *            {@link android.os.Environment#DIRECTORY_RINGTONES},
+     *            {@link android.os.Environment#DIRECTORY_ALARMS},
+     *            {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
+     *            {@link android.os.Environment#DIRECTORY_PICTURES}, or
+     *            {@link android.os.Environment#DIRECTORY_MOVIES}.
+     * @return the absolute paths to application-specific directories. Some
+     *         individual paths may be {@code null} if that shared storage is
+     *         not currently available. The first path returned is the same as
+     *         {@link #getExternalFilesDir(String)}.
      * @see #getExternalFilesDir(String)
      * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
      */
     public abstract File[] getExternalFilesDirs(String type);
 
     /**
-     * Return the primary external storage directory where this application's OBB
-     * files (if there are any) can be found. Note if the application does not have
-     * any OBB files, this directory may not exist.
+     * Return the primary shared/external storage directory where this
+     * application's OBB files (if there are any) can be found. Note if the
+     * application does not have any OBB files, this directory may not exist.
      * <p>
-     * This is like {@link #getFilesDir()} in that these files will be deleted when
-     * the application is uninstalled, however there are some important differences:
+     * This is like {@link #getFilesDir()} in that these files will be deleted
+     * when the application is uninstalled, however there are some important
+     * differences:
      * <ul>
-     * <li>External files are not always available: they will disappear if the
-     * user mounts the external storage on a computer or removes it.
-     * <li>There is no security enforced with these files.  For example, any application
-     * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
      * these files.
      * </ul>
      * <p>
      * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
      * are required to read or write to the returned path; it's always
-     * accessible to the calling app.  This only applies to paths generated for
-     * package name of the calling application.  To access paths belonging
-     * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
-     * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
+     * accessible to the calling app. This only applies to paths generated for
+     * package name of the calling application. To access paths belonging to
+     * other packages,
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or
+     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
      * <p>
      * On devices with multiple users (as described by {@link UserManager}),
      * multiple users may share the same OBB storage location. Applications
      * should ensure that multiple instances running under different users don't
      * interfere with each other.
+     *
+     * @return the absolute path to application-specific directory. May return
+     *         {@code null} if shared storage is not currently available.
+     * @see #getObbDirs()
+     * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
      */
     public abstract File getObbDir();
 
     /**
      * Returns absolute paths to application-specific directories on all
-     * external storage devices where the application's OBB files (if there are
-     * any) can be found. Note if the application does not have any OBB files,
-     * these directories may not exist.
+     * shared/external storage devices where the application's OBB files (if
+     * there are any) can be found. Note if the application does not have any
+     * OBB files, these directories may not exist.
      * <p>
-     * This is like {@link #getFilesDir()} in that these files will be deleted when
-     * the application is uninstalled, however there are some important differences:
+     * This is like {@link #getFilesDir()} in that these files will be deleted
+     * when the application is uninstalled, however there are some important
+     * differences:
      * <ul>
-     * <li>External files are not always available: they will disappear if the
-     * user mounts the external storage on a computer or removes it.
-     * <li>There is no security enforced with these files.
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
      * </ul>
      * <p>
-     * External storage devices returned here are considered a permanent part of
-     * the device, including both emulated external storage and physical media
-     * slots, such as SD cards in a battery compartment. The returned paths do
-     * not include transient devices, such as USB flash drives.
+     * Shared storage devices returned here are considered a stable part of the
+     * device, including physical media slots under a protective cover. The
+     * returned paths do not include transient devices, such as USB flash drives
+     * connected to handheld devices.
      * <p>
-     * An application may store data on any or all of the returned devices.  For
+     * An application may store data on any or all of the returned devices. For
      * example, an app may choose to store large files on the device with the
      * most available space, as measured by {@link StatFs}.
      * <p>
-     * No permissions are required to read or write to the returned paths; they
-     * are always accessible to the calling app.  Write access outside of these
-     * paths on secondary external storage devices is not available.
-     * <p>
-     * The first path returned is the same as {@link #getObbDir()}.
-     * Returned paths may be {@code null} if a storage device is unavailable.
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path. Write access outside of these paths
+     * on secondary external storage devices is not available.
      *
+     * @return the absolute paths to application-specific directories. Some
+     *         individual paths may be {@code null} if that shared storage is
+     *         not currently available. The first path returned is the same as
+     *         {@link #getObbDir()}
      * @see #getObbDir()
      * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
      */
     public abstract File[] getObbDirs();
 
     /**
-     * Returns the absolute path to the application specific cache directory
-     * on the filesystem. These files will be ones that get deleted first when the
-     * device runs low on storage.
-     * There is no guarantee when these files will be deleted.
-     *
+     * Returns the absolute path to the application specific cache directory on
+     * the filesystem. These files will be ones that get deleted first when the
+     * device runs low on storage. There is no guarantee when these files will
+     * be deleted.
+     * <p>
      * <strong>Note: you should not <em>rely</em> on the system deleting these
      * files for you; you should always have a reasonable maximum, such as 1 MB,
      * for the amount of space you consume with cache files, and prune those
      * files when exceeding that space.</strong>
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     * <p>
+     * Apps require no extra permissions to read or write to the returned path,
+     * since this path lives in their private storage.
      *
      * @return The path of the directory holding application cache files.
-     *
      * @see #openFileOutput
      * @see #getFileStreamPath
      * @see #getDir
@@ -946,6 +1007,9 @@
      * This location is optimal for storing compiled or optimized code generated
      * by your application at runtime.
      * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     * <p>
      * Apps require no extra permissions to read or write to the returned path,
      * since this path lives in their private storage.
      *
@@ -954,120 +1018,161 @@
     public abstract File getCodeCacheDir();
 
     /**
-     * Returns the absolute path to the directory on the primary external filesystem
-     * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
-     * Environment.getExternalStorageDirectory()} where the application can
-     * place cache files it owns. These files are internal to the application, and
-     * not typically visible to the user as media.
-     *
-     * <p>This is like {@link #getCacheDir()} in that these
-     * files will be deleted when the application is uninstalled, however there
-     * are some important differences:
-     *
+     * Returns absolute path to application-specific directory on the primary
+     * shared/external storage device where the application can place cache
+     * files it owns. These files are internal to the application, and not
+     * typically visible to the user as media.
+     * <p>
+     * This is like {@link #getCacheDir()} in that these files will be deleted
+     * when the application is uninstalled, however there are some important
+     * differences:
      * <ul>
-     * <li>The platform does not always monitor the space available in external
-     * storage, and thus may not automatically delete these files.  Currently
-     * the only time files here will be deleted by the platform is when running
-     * on {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and
-     * {@link android.os.Environment#isExternalStorageEmulated()
-     * Environment.isExternalStorageEmulated()} returns true.  Note that you should
-     * be managing the maximum space you will use for these anyway, just like
-     * with {@link #getCacheDir()}.
-     * <li>External files are not always available: they will disappear if the
-     * user mounts the external storage on a computer or removes it.  See the
-     * APIs on {@link android.os.Environment} for information in the storage state.
-     * <li>There is no security enforced with these files.  For example, any application
-     * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * <li>The platform does not always monitor the space available in shared
+     * storage, and thus may not automatically delete these files. Apps should
+     * always manage the maximum space used in this location. Currently the only
+     * time files here will be deleted by the platform is when running on
+     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and
+     * {@link Environment#isExternalStorageEmulated(File)} returns true.
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
      * these files.
      * </ul>
-     *
-     * <p>Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
+     * <p>
+     * If a shared storage device is emulated (as determined by
+     * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+     * backed by a private user data partition, which means there is little
+     * benefit to storing data here instead of the private directory returned by
+     * {@link #getCacheDir()}.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
      * are required to read or write to the returned path; it's always
-     * accessible to the calling app.  This only applies to paths generated for
-     * package name of the calling application.  To access paths belonging
-     * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
-     * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
+     * accessible to the calling app. This only applies to paths generated for
+     * package name of the calling application. To access paths belonging to
+     * other packages,
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or
+     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required.
+     * <p>
+     * On devices with multiple users (as described by {@link UserManager}),
+     * each user has their own isolated shared storage. Applications only have
+     * access to the shared storage for the user they're running as.
+     * <p>
+     * The returned path may change over time if different shared storage media
+     * is inserted, so only relative paths should be persisted.
      *
-     * <p>On devices with multiple users (as described by {@link UserManager}),
-     * each user has their own isolated external storage. Applications only
-     * have access to the external storage for the user they're running as.</p>
-     *
-     * @return The path of the directory holding application cache files
-     * on external storage.  Returns null if external storage is not currently
-     * mounted so it could not ensure the path exists; you will need to call
-     * this method again when it is available.
-     *
+     * @return the absolute path to application-specific directory. May return
+     *         {@code null} if shared storage is not currently available.
      * @see #getCacheDir
+     * @see #getExternalCacheDirs()
+     * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
      */
     @Nullable
     public abstract File getExternalCacheDir();
 
     /**
      * Returns absolute paths to application-specific directories on all
-     * external storage devices where the application can place cache files it
-     * owns. These files are internal to the application, and not typically
-     * visible to the user as media.
+     * shared/external storage devices where the application can place cache
+     * files it owns. These files are internal to the application, and not
+     * typically visible to the user as media.
      * <p>
-     * This is like {@link #getCacheDir()} in that these files will be deleted when
-     * the application is uninstalled, however there are some important differences:
+     * This is like {@link #getCacheDir()} in that these files will be deleted
+     * when the application is uninstalled, however there are some important
+     * differences:
      * <ul>
-     * <li>External files are not always available: they will disappear if the
-     * user mounts the external storage on a computer or removes it.
-     * <li>There is no security enforced with these files.
+     * <li>The platform does not always monitor the space available in shared
+     * storage, and thus may not automatically delete these files. Apps should
+     * always manage the maximum space used in this location. Currently the only
+     * time files here will be deleted by the platform is when running on
+     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and
+     * {@link Environment#isExternalStorageEmulated(File)} returns true.
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
      * </ul>
      * <p>
-     * External storage devices returned here are considered a permanent part of
-     * the device, including both emulated external storage and physical media
-     * slots, such as SD cards in a battery compartment. The returned paths do
-     * not include transient devices, such as USB flash drives.
+     * If a shared storage device is emulated (as determined by
+     * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+     * backed by a private user data partition, which means there is little
+     * benefit to storing data here instead of the private directory returned by
+     * {@link #getCacheDir()}.
      * <p>
-     * An application may store data on any or all of the returned devices.  For
+     * Shared storage devices returned here are considered a stable part of the
+     * device, including physical media slots under a protective cover. The
+     * returned paths do not include transient devices, such as USB flash drives
+     * connected to handheld devices.
+     * <p>
+     * An application may store data on any or all of the returned devices. For
      * example, an app may choose to store large files on the device with the
      * most available space, as measured by {@link StatFs}.
      * <p>
-     * No permissions are required to read or write to the returned paths; they
-     * are always accessible to the calling app.  Write access outside of these
-     * paths on secondary external storage devices is not available.
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path. Write access outside of these paths
+     * on secondary external storage devices is not available.
      * <p>
-     * The first path returned is the same as {@link #getExternalCacheDir()}.
-     * Returned paths may be {@code null} if a storage device is unavailable.
+     * The returned paths may change over time if different shared storage media
+     * is inserted, so only relative paths should be persisted.
      *
+     * @return the absolute paths to application-specific directories. Some
+     *         individual paths may be {@code null} if that shared storage is
+     *         not currently available. The first path returned is the same as
+     *         {@link #getExternalCacheDir()}.
      * @see #getExternalCacheDir()
      * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
      */
     public abstract File[] getExternalCacheDirs();
 
     /**
      * Returns absolute paths to application-specific directories on all
-     * external storage devices where the application can place media files.
-     * These files are scanned and made available to other apps through
+     * shared/external storage devices where the application can place media
+     * files. These files are scanned and made available to other apps through
      * {@link MediaStore}.
      * <p>
      * This is like {@link #getExternalFilesDirs} in that these files will be
      * deleted when the application is uninstalled, however there are some
      * important differences:
      * <ul>
-     * <li>External files are not always available: they will disappear if the
-     * user mounts the external storage on a computer or removes it.
-     * <li>There is no security enforced with these files.
+     * <li>Shared storage may not always be available, since removable media can
+     * be ejected by the user. Media state can be checked using
+     * {@link Environment#getExternalStorageState(File)}.
+     * <li>There is no security enforced with these files. For example, any
+     * application holding
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to
+     * these files.
      * </ul>
      * <p>
-     * External storage devices returned here are considered a permanent part of
-     * the device, including both emulated external storage and physical media
-     * slots, such as SD cards in a battery compartment. The returned paths do
-     * not include transient devices, such as USB flash drives.
+     * Shared storage devices returned here are considered a stable part of the
+     * device, including physical media slots under a protective cover. The
+     * returned paths do not include transient devices, such as USB flash drives
+     * connected to handheld devices.
      * <p>
      * An application may store data on any or all of the returned devices. For
      * example, an app may choose to store large files on the device with the
      * most available space, as measured by {@link StatFs}.
      * <p>
-     * No permissions are required to read or write to the returned paths; they
-     * are always accessible to the calling app. Write access outside of these
-     * paths on secondary external storage devices is not available.
+     * No additional permissions are required for the calling app to read or
+     * write files under the returned path. Write access outside of these paths
+     * on secondary external storage devices is not available.
      * <p>
-     * Returned paths may be {@code null} if a storage device is unavailable.
+     * The returned paths may change over time if different shared storage media
+     * is inserted, so only relative paths should be persisted.
      *
+     * @return the absolute paths to application-specific directories. Some
+     *         individual paths may be {@code null} if that shared storage is
+     *         not currently available.
      * @see Environment#getExternalStorageState(File)
+     * @see Environment#isExternalStorageEmulated(File)
+     * @see Environment#isExternalStorageRemovable(File)
      */
     public abstract File[] getExternalMediaDirs();
 
@@ -1090,6 +1195,12 @@
      * created through a File object will only be accessible by your own
      * application; you can only set the mode of the entire directory, not
      * of individual files.
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
+     * <p>
+     * Apps require no extra permissions to read or write to the returned path,
+     * since this path lives in their private storage.
      *
      * @param name Name of the directory to retrieve.  This is a directory
      * that is created as part of your application data.
@@ -1173,6 +1284,9 @@
     /**
      * Returns the absolute path on the filesystem where a database created with
      * {@link #openOrCreateDatabase} is stored.
+     * <p>
+     * The returned path may change over time if the calling app is moved to an
+     * adopted storage device, so only relative paths should be persisted.
      *
      * @param name The name of the database for which you would like to get
      *          its path.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index dac17d5..3d0c572 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3159,6 +3159,13 @@
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_HOME = "android.intent.category.HOME";
     /**
+     * This is the home activity that is displayed when the device is finished setting up and ready
+     * for use.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_HOME_MAIN = "android.intent.category.HOME_MAIN";
+    /**
      * This is the setup wizard activity, that is the first activity that is displayed
      * when the user sets up the device for the first time.
      * @hide
diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java
index 3efd89a..0441ccc 100644
--- a/core/java/android/content/PeriodicSync.java
+++ b/core/java/android/content/PeriodicSync.java
@@ -21,6 +21,8 @@
 import android.os.Parcel;
 import android.accounts.Account;
 
+import java.util.Objects;
+
 /**
  * Value type that contains information about a periodic sync.
  */
@@ -144,7 +146,9 @@
             if (!b2.containsKey(key)) {
                 return false;
             }
-            if (!b1.get(key).equals(b2.get(key))) {
+            // Null check. According to ContentResolver#validateSyncExtrasBundle null-valued keys
+            // are allowed in the bundle.
+            if (!Objects.equals(b1.get(key), b2.get(key))) {
                 return false;
             }
         }
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 43cc63b..e798eb8 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -33,12 +33,16 @@
  */
 public class ActivityInfo extends ComponentInfo
         implements Parcelable {
+
+     // NOTE: When adding new data members be sure to update the copy-constructor, Parcel
+     // constructor, and writeToParcel.
+
     /**
      * A style resource identifier (in the package's resources) of this
      * activity's theme.  From the "theme" attribute or, if not set, 0.
      */
     public int theme;
-    
+
     /**
      * Constant corresponding to <code>standard</code> in
      * the {@link android.R.attr#launchMode} attribute.
@@ -705,6 +709,7 @@
         super(orig);
         theme = orig.theme;
         launchMode = orig.launchMode;
+        documentLaunchMode = orig.documentLaunchMode;
         permission = orig.permission;
         taskAffinity = orig.taskAffinity;
         targetActivity = orig.targetActivity;
@@ -780,6 +785,7 @@
         super.writeToParcel(dest, parcelableFlags);
         dest.writeInt(theme);
         dest.writeInt(launchMode);
+        dest.writeInt(documentLaunchMode);
         dest.writeString(permission);
         dest.writeString(taskAffinity);
         dest.writeString(targetActivity);
@@ -809,6 +815,7 @@
         super(source);
         theme = source.readInt();
         launchMode = source.readInt();
+        documentLaunchMode = source.readInt();
         permission = source.readString();
         taskAffinity = source.readString();
         targetActivity = source.readString();
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 7032c9a..99bd390 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -894,6 +894,7 @@
             }
 
             pkg.volumeUuid = volumeUuid;
+            pkg.applicationInfo.volumeUuid = volumeUuid;
             pkg.baseCodePath = apkPath;
             pkg.mSignatures = null;
 
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 30cdfd3..2fe8fb6 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -78,14 +78,14 @@
                 sSensorModuleInitialized = true;
                 nativeClassInit();
             }
-        }
 
-        // initialize the sensor list
-        for (int index = 0;;++index) {
-            Sensor sensor = new Sensor();
-            if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
-            mFullSensorsList.add(sensor);
-            mHandleToSensor.append(sensor.getHandle(), sensor);
+            // initialize the sensor list
+            for (int index = 0;;++index) {
+                Sensor sensor = new Sensor();
+                if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
+                mFullSensorsList.add(sensor);
+                mHandleToSensor.append(sensor.getHandle(), sensor);
+            }
         }
     }
 
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index c8b45c7..3601b39 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -61,6 +61,8 @@
     // Registers an input devices changed listener.
     void registerInputDevicesChangedListener(IInputDevicesChangedListener listener);
 
+    // Queries whether the device is currently in tablet mode
+    int isInTabletMode();
     // Registers a tablet mode change listener
     void registerTabletModeChangedListener(ITabletModeChangedListener listener);
 
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index a754d6b..618864f 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -19,6 +19,7 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 
+import android.annotation.IntDef;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.Context;
@@ -39,6 +40,8 @@
 import android.view.InputDevice;
 import android.view.InputEvent;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -179,6 +182,31 @@
      */
     public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2;  // see InputDispatcher.h
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({SWITCH_STATE_UNKNOWN, SWITCH_STATE_OFF, SWITCH_STATE_ON})
+    public @interface SwitchState {}
+
+    /**
+     * Switch State: Unknown.
+     *
+     * The system has yet to report a valid value for the switch.
+     * @hide
+     */
+    public static final int SWITCH_STATE_UNKNOWN = -1;
+
+    /**
+     * Switch State: Off.
+     * @hide
+     */
+    public static final int SWITCH_STATE_OFF = 0;
+
+    /**
+     * Switch State: On.
+     * @hide
+     */
+    public static final int SWITCH_STATE_ON = 1;
+
     private InputManager(IInputManager im) {
         mIm = im;
     }
@@ -339,6 +367,23 @@
     }
 
     /**
+     * Queries whether the device is in tablet mode.
+     *
+     * @return The tablet switch state which is one of {@link #SWITCH_STATE_UNKNOWN},
+     * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}.
+     * @hide
+     */
+    @SwitchState
+    public int isInTabletMode() {
+        try {
+            return mIm.isInTabletMode();
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not get tablet mode state", ex);
+            return SWITCH_STATE_UNKNOWN;
+        }
+    }
+
+    /**
      * Register a tablet mode changed listener.
      *
      * @param listener The listener to register.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 9a2a241..4d9b759 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -895,8 +895,12 @@
      * Tells the underlying networking system that the caller wants to
      * begin using the named feature. The interpretation of {@code feature}
      * is completely up to each networking implementation.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
+     *
      * @param networkType specifies which network the request pertains to
      * @param feature the name of the feature to be used
      * @return an integer value representing the outcome of the request.
@@ -946,8 +950,12 @@
      * Tells the underlying networking system that the caller is finished
      * using the named feature. The interpretation of {@code feature}
      * is completely up to each networking implementation.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
+     *
      * @param networkType specifies which network the request pertains to
      * @param feature the name of the feature that is no longer needed
      * @return an integer value representing the outcome of the request.
@@ -1333,8 +1341,12 @@
      * Ensure that a network route exists to deliver traffic to the specified
      * host via the specified network interface. An attempt to add a route that
      * already exists is ignored, but treated as successful.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
+     *
      * @param networkType the type of the network over which traffic to the specified
      * host is to be routed
      * @param hostAddress the IP address of the host to which the route is desired
@@ -1354,8 +1366,12 @@
      * Ensure that a network route exists to deliver traffic to the specified
      * host via the specified network interface. An attempt to add a route that
      * already exists is ignored, but treated as successful.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
+     *
      * @param networkType the type of the network over which traffic to the specified
      * host is to be routed
      * @param hostAddress the IP address of the host to which the route is desired
@@ -1555,6 +1571,13 @@
         return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
     }
 
+    /** {@hide} */
+    public static final void enforceChangePermission(Context context) {
+        int uid = Binder.getCallingUid();
+        Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
+                .getPackageNameForUid(context, uid), true /* throwException */);
+    }
+
     /** {@hide */
     public static final void enforceTetherChangePermission(Context context) {
         if (context.getResources().getStringArray(
@@ -1565,8 +1588,8 @@
                     android.Manifest.permission.CONNECTIVITY_INTERNAL, "ConnectivityService");
         } else {
             int uid = Binder.getCallingUid();
-            Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
-                    .getPackageNameForUid(context, uid), true);
+            Settings.checkAndNoteWriteSettingsOperation(context, uid, Settings
+                    .getPackageNameForUid(context, uid), true /* throwException */);
         }
     }
 
@@ -1671,8 +1694,11 @@
      * allowed between the tethered devices and this device, though upstream net
      * access will of course fail until an upstream network interface becomes
      * active.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
      *
      * @param iface the interface name to tether.
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
@@ -1689,8 +1715,11 @@
 
     /**
      * Stop tethering the named interface.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
      *
      * @param iface the interface name to untether.
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
@@ -1790,8 +1819,11 @@
      * attempt to switch to Rndis and subsequently tether the resulting
      * interface on {@code true} or turn off tethering and switch off
      * Rndis on {@code false}.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
      *
      * @param enable a boolean - {@code true} to enable tethering
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
@@ -2461,8 +2493,11 @@
      * network may never attain, and whether a network will attain these states
      * is unknown prior to bringing up the network so the framework does not
      * know how to go about satisfing a request with these capabilities.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
      *
      * @param request {@link NetworkRequest} describing this request.
      * @param networkCallback The {@link NetworkCallback} to be utilized for this
@@ -2484,8 +2519,12 @@
      * network is not found within the given time (in milliseconds) the
      * {@link NetworkCallback#unavailable} callback is called.  The request must
      * still be released normally by calling {@link releaseNetworkRequest}.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
+     *
      * @param request {@link NetworkRequest} describing this request.
      * @param networkCallback The callbacks to be utilized for this request.  Note
      *                        the callbacks must not be shared - they uniquely specify
@@ -2558,8 +2597,12 @@
      * network may never attain, and whether a network will attain these states
      * is unknown prior to bringing up the network so the framework does not
      * know how to go about satisfing a request with these capabilities.
-     * <p>This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+     *
+     * <p>This method requires the caller to hold either the
+     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+     * or the ability to modify system settings as determined by
+     * {@link android.provider.Settings.System#canWrite}.</p>
+     *
      * @param request {@link NetworkRequest} describing this request.
      * @param operation Action to perform when the network is available (corresponds
      *                  to the {@link NetworkCallback#onAvailable} call.  Typically
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 87c063f..97bd5d2 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -21,7 +21,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.util.Objects;
 
@@ -34,7 +33,7 @@
 public class DhcpResults extends StaticIpConfiguration {
     private static final String TAG = "DhcpResults";
 
-    public InetAddress serverAddress;
+    public Inet4Address serverAddress;
 
     /** Vendor specific information (from RFC 2132). */
     public String vendorInfo;
@@ -142,7 +141,7 @@
     private static void readFromParcel(DhcpResults dhcpResults, Parcel in) {
         StaticIpConfiguration.readFromParcel(dhcpResults, in);
         dhcpResults.leaseDuration = in.readInt();
-        dhcpResults.serverAddress = NetworkUtils.unparcelInetAddress(in);
+        dhcpResults.serverAddress = (Inet4Address) NetworkUtils.unparcelInetAddress(in);
         dhcpResults.vendorInfo = in.readString();
     }
 
@@ -183,8 +182,8 @@
 
     public boolean setServerAddress(String addrString) {
         try {
-            serverAddress = NetworkUtils.numericToInetAddress(addrString);
-        } catch (IllegalArgumentException e) {
+            serverAddress = (Inet4Address) NetworkUtils.numericToInetAddress(addrString);
+        } catch (IllegalArgumentException|ClassCastException e) {
             Log.e(TAG, "setServerAddress failed with addrString " + addrString);
             return true;
         }
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 02dae8a..eab22b8 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -51,12 +51,13 @@
     public static final int POLICY_ALLOW_BACKGROUND_BATTERY_SAVE = 0x2;
 
     /* RULE_* are not masks and they must be exclusive */
+    public static final int RULE_UNKNOWN = -1;
     /** All network traffic should be allowed. */
-    public static final int RULE_ALLOW_ALL = 0x0;
+    public static final int RULE_ALLOW_ALL = 0;
     /** Reject traffic on metered networks. */
-    public static final int RULE_REJECT_METERED = 0x1;
+    public static final int RULE_REJECT_METERED = 1;
     /** Reject traffic on all networks. */
-    public static final int RULE_REJECT_ALL = 0x2;
+    public static final int RULE_REJECT_ALL = 2;
 
     public static final int FIREWALL_RULE_DEFAULT = 0;
     public static final int FIREWALL_RULE_ALLOW = 1;
@@ -375,25 +376,4 @@
         // nothing found above; we can apply policy to UID
         return true;
     }
-
-    /** {@hide} */
-    public static void dumpPolicy(PrintWriter fout, int policy) {
-        fout.write("[");
-        if ((policy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
-            fout.write("REJECT_METERED_BACKGROUND");
-        }
-        fout.write("]");
-    }
-
-    /** {@hide} */
-    public static void dumpRules(PrintWriter fout, int rules) {
-        fout.write("[");
-        if ((rules & RULE_REJECT_METERED) != 0) {
-            fout.write("REJECT_METERED");
-        } else if ((rules & RULE_REJECT_ALL) != 0) {
-            fout.write("REJECT_ALL");
-        }
-        fout.write("]");
-    }
-
 }
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 57eef83..b7a411e 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -288,7 +288,8 @@
         } else {
             final boolean matchesType = (sForceAllNetworkTypes
                     || contains(DATA_USAGE_NETWORK_TYPES, ident.mType));
-            return matchesType && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
+            return matchesType && !ArrayUtils.isEmpty(mMatchSubscriberIds)
+                    && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
         }
     }
 
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 2374899..862f4c4 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -577,7 +577,7 @@
         public static final int KITKAT = 19;
 
         /**
-         * Android 4.4W: KitKat for watches, snacks on the run.
+         * June 2014: Android 4.4W. KitKat for watches, snacks on the run.
          *
          * <p>Applications targeting this or a later release will get these
          * new changes in behavior:</p>
@@ -595,7 +595,7 @@
         public static final int L = 21;
 
         /**
-         * Lollipop.  A flat one with beautiful shadows.  But still tasty.
+         * November 2014: Lollipop.  A flat one with beautiful shadows.  But still tasty.
          *
          * <p>Applications targeting this or a later release will get these
          * new changes in behavior:</p>
@@ -626,12 +626,38 @@
         public static final int LOLLIPOP = 21;
 
         /**
-         * Lollipop with an extra sugar coating on the outside!
+         * March 2015: Lollipop with an extra sugar coating on the outside!
          */
         public static final int LOLLIPOP_MR1 = 22;
 
         /**
-         * M comes after L.
+         * M is for Marshmallow!
+         *
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li> Runtime permissions.  Dangerous permissions are no longer granted at
+         * install time, but must be requested by the application at runtime through
+         * {@link android.app.Activity#requestPermissions}.</li>
+         * <li> Bluetooth and Wi-Fi scanning now requires holding the location permission.</li>
+         * <li> {@link android.app.AlarmManager#setTimeZone AlarmManager.setTimeZone} will fail if
+         * the given timezone is non-Olson.</li>
+         * <li> Activity transitions will only return shared
+         * elements mapped in the returned view hierarchy back to the calling activity.</li>
+         * <li> {@link android.view.View} allows a number of behaviors that may break
+         * existing apps: Canvas throws an exception if restore() is called too many times,
+         * widgets may return a hint size when returning UNSPECIFIED measure specs, and it
+         * will respect the attributes {@link android.R.attr#foreground},
+         * {@link android.R.attr#foregroundGravity}, {@link android.R.attr#foregroundTint}, and
+         * {@link android.R.attr#foregroundTintMode}.</li>
+         * <li> {@link android.view.MotionEvent#getButtonState MotionEvent.getButtonState}
+         * will no longer report {@link android.view.MotionEvent#BUTTON_PRIMARY}
+         * and {@link android.view.MotionEvent#BUTTON_SECONDARY} as synonyms for
+         * {@link android.view.MotionEvent#BUTTON_STYLUS_PRIMARY} and
+         * {@link android.view.MotionEvent#BUTTON_STYLUS_SECONDARY}.</li>
+         * <li> {@link android.widget.ScrollView} now respects the layout param margins
+         * when measuring.</li>
+         * </ul>
          */
         public static final int M = 23;
     }
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 97b85e2..fdd34f5 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -471,7 +471,7 @@
        *     </tbody>
        * </table>
        */
-        public String getMemoryStat(String statName) {
+       public String getMemoryStat(String statName) {
             switch(statName) {
                 case "summary.java-heap":
                     return Integer.toString(getSummaryJavaHeap());
@@ -1538,7 +1538,13 @@
 
     /**
      * Retrieves information about this processes memory usages. This information is broken down by
-     * how much is in use by dalivk, the native heap, and everything else.
+     * how much is in use by dalvik, the native heap, and everything else.
+     *
+     * <p><b>Note:</b> this method directly retrieves memory information for the give process
+     * from low-level data available to it.  It may not be able to retrieve information about
+     * some protected allocations, such as graphics.  If you want to be sure you can see
+     * all information about allocations by the process, use instead
+     * {@link android.app.ActivityManager#getProcessMemoryInfo(int[])}.</p>
      */
     public static native void getMemoryInfo(MemoryInfo memoryInfo);
 
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 64d6da5..f346fe7 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -276,8 +276,8 @@
     }
 
     /**
-     * Return the primary external storage directory. This directory may not
-     * currently be accessible if it has been mounted by the user on their
+     * Return the primary shared/external storage directory. This directory may
+     * not currently be accessible if it has been mounted by the user on their
      * computer, has been removed from the device, or some other problem has
      * happened. You can determine its current state with
      * {@link #getExternalStorageState()}.
@@ -291,12 +291,15 @@
      * filesystem on a computer.</em>
      * <p>
      * On devices with multiple users (as described by {@link UserManager}),
-     * each user has their own isolated external storage. Applications only have
-     * access to the external storage for the user they're running as.
+     * each user has their own isolated shared storage. Applications only have
+     * access to the shared storage for the user they're running as.
      * <p>
-     * In devices with multiple "external" storage directories, this directory
-     * represents the "primary" external storage that the user will interact
+     * In devices with multiple shared/external storage directories, this
+     * directory represents the primary storage that the user will interact
      * with. Access to secondary storage is available through
+     * {@link Context#getExternalFilesDirs(String)},
+     * {@link Context#getExternalCacheDirs()}, and
+     * {@link Context#getExternalMediaDirs()}.
      * <p>
      * Applications should not directly use this top-level directory, in order
      * to avoid polluting the user's root namespace. Any files that are private
@@ -315,8 +318,9 @@
      * <p>
      * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, if your
      * application only needs to store internal data, consider using
-     * {@link Context#getExternalFilesDir(String)} or
-     * {@link Context#getExternalCacheDir()}, which require no permissions to
+     * {@link Context#getExternalFilesDir(String)},
+     * {@link Context#getExternalCacheDir()}, or
+     * {@link Context#getExternalMediaDirs()}, which require no permissions to
      * read or write.
      * <p>
      * This path may change between platform versions, so applications should
@@ -325,8 +329,7 @@
      * Here is an example of typical code to monitor the state of external
      * storage:
      * <p>
-     * {@sample
-     * development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
+     * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
      * monitor_storage}
      *
      * @see #getExternalStorageState()
@@ -446,32 +449,32 @@
     public static String DIRECTORY_DOCUMENTS = "Documents";
 
     /**
-     * Get a top-level public external storage directory for placing files of
-     * a particular type.  This is where the user will typically place and
-     * manage their own files, so you should be careful about what you put here
-     * to ensure you don't erase their files or get in the way of their own
+     * Get a top-level shared/external storage directory for placing files of a
+     * particular type. This is where the user will typically place and manage
+     * their own files, so you should be careful about what you put here to
+     * ensure you don't erase their files or get in the way of their own
      * organization.
-     * 
-     * <p>On devices with multiple users (as described by {@link UserManager}),
-     * each user has their own isolated external storage. Applications only
-     * have access to the external storage for the user they're running as.</p>
-     *
-     * <p>Here is an example of typical code to manipulate a picture on
-     * the public external storage:</p>
-     * 
+     * <p>
+     * On devices with multiple users (as described by {@link UserManager}),
+     * each user has their own isolated shared storage. Applications only have
+     * access to the shared storage for the user they're running as.
+     * </p>
+     * <p>
+     * Here is an example of typical code to manipulate a picture on the public
+     * shared storage:
+     * </p>
      * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
      * public_picture}
      * 
-     * @param type The type of storage directory to return.  Should be one of
-     * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS},
-     * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS},
-     * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES},
-     * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or
-     * {@link #DIRECTORY_DCIM}.  May not be null.
-     * 
-     * @return Returns the File path for the directory.  Note that this
-     * directory may not yet exist, so you must make sure it exists before
-     * using it such as with {@link File#mkdirs File.mkdirs()}.
+     * @param type The type of storage directory to return. Should be one of
+     *            {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS},
+     *            {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS},
+     *            {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES},
+     *            {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or
+     *            {@link #DIRECTORY_DCIM}. May not be null.
+     * @return Returns the File path for the directory. Note that this directory
+     *         may not yet exist, so you must make sure it exists before using
+     *         it such as with {@link File#mkdirs File.mkdirs()}.
      */
     public static File getExternalStoragePublicDirectory(String type) {
         throwIfUserRequired();
@@ -623,7 +626,7 @@
     public static final String MEDIA_EJECTING = "ejecting";
 
     /**
-     * Returns the current state of the primary "external" storage device.
+     * Returns the current state of the primary shared/external storage media.
      * 
      * @see #getExternalStorageDirectory()
      * @return one of {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED},
@@ -646,8 +649,8 @@
     }
 
     /**
-     * Returns the current state of the storage device that provides the given
-     * path.
+     * Returns the current state of the shared/external storage media at the
+     * given path.
      *
      * @return one of {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED},
      *         {@link #MEDIA_UNMOUNTED}, {@link #MEDIA_CHECKING},
@@ -665,7 +668,8 @@
     }
 
     /**
-     * Returns whether the primary "external" storage device is removable.
+     * Returns whether the primary shared/external storage media is physically
+     * removable.
      *
      * @return true if the storage device can be removed (such as an SD card),
      *         or false if the storage device is built in and cannot be
@@ -678,8 +682,8 @@
     }
 
     /**
-     * Returns whether the storage device that provides the given path is
-     * removable.
+     * Returns whether the shared/external storage media at the given path is
+     * physically removable.
      *
      * @return true if the storage device can be removed (such as an SD card),
      *         or false if the storage device is built in and cannot be
@@ -697,9 +701,15 @@
     }
 
     /**
-     * Returns whether the primary "external" storage device is emulated. If
-     * true, data stored on this device will be stored on a portion of the
-     * internal storage system.
+     * Returns whether the primary shared/external storage media is emulated.
+     * <p>
+     * The contents of emulated storage devices are backed by a private user
+     * data partition, which means there is little benefit to apps storing data
+     * here instead of the private directories returned by
+     * {@link Context#getFilesDir()}, etc.
+     * <p>
+     * This returns true when emulated storage is backed by either internal
+     * storage or an adopted storage device.
      *
      * @see DevicePolicyManager#setStorageEncryption(android.content.ComponentName,
      *      boolean)
@@ -711,9 +721,16 @@
     }
 
     /**
-     * Returns whether the storage device that provides the given path is
-     * emulated. If true, data stored on this device will be stored on a portion
-     * of the internal storage system.
+     * Returns whether the shared/external storage media at the given path is
+     * emulated.
+     * <p>
+     * The contents of emulated storage devices are backed by a private user
+     * data partition, which means there is little benefit to apps storing data
+     * here instead of the private directories returned by
+     * {@link Context#getFilesDir()}, etc.
+     * <p>
+     * This returns true when emulated storage is backed by either internal
+     * storage or an adopted storage device.
      *
      * @throws IllegalArgumentException if the path is not a valid storage
      *             device.
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 1273772b..5852f5f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.IntegerRes;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -42,6 +43,8 @@
 import java.util.Map;
 import java.util.Set;
 
+import dalvik.system.VMRuntime;
+
 /**
  * Container for a message (data and object references) that can
  * be sent through an IBinder.  A Parcel can contain both flattened data
@@ -193,6 +196,7 @@
      * indicating that we're responsible for its lifecycle.
      */
     private boolean mOwnsNativeParcelObject;
+    private long mNativeSize;
 
     private RuntimeException mStack;
 
@@ -244,7 +248,7 @@
     private static native int nativeDataAvail(long nativePtr);
     private static native int nativeDataPosition(long nativePtr);
     private static native int nativeDataCapacity(long nativePtr);
-    private static native void nativeSetDataSize(long nativePtr, int size);
+    private static native long nativeSetDataSize(long nativePtr, int size);
     private static native void nativeSetDataPosition(long nativePtr, int pos);
     private static native void nativeSetDataCapacity(long nativePtr, int size);
 
@@ -259,7 +263,7 @@
     private static native void nativeWriteDouble(long nativePtr, double val);
     private static native void nativeWriteString(long nativePtr, String val);
     private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
-    private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
+    private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
 
     private static native byte[] nativeCreateByteArray(long nativePtr);
     private static native byte[] nativeReadBlob(long nativePtr);
@@ -272,13 +276,13 @@
     private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
 
     private static native long nativeCreate();
-    private static native void nativeFreeBuffer(long nativePtr);
+    private static native long nativeFreeBuffer(long nativePtr);
     private static native void nativeDestroy(long nativePtr);
 
     private static native byte[] nativeMarshall(long nativePtr);
-    private static native void nativeUnmarshall(
+    private static native long nativeUnmarshall(
             long nativePtr, byte[] data, int offset, int length);
-    private static native void nativeAppendFrom(
+    private static native long nativeAppendFrom(
             long thisNativePtr, long otherNativePtr, int offset, int length);
     private static native boolean nativeHasFileDescriptors(long nativePtr);
     private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName);
@@ -390,7 +394,7 @@
      * @param size The new number of bytes in the Parcel.
      */
     public final void setDataSize(int size) {
-        nativeSetDataSize(mNativePtr, size);
+        updateNativeSize(nativeSetDataSize(mNativePtr, size));
     }
 
     /**
@@ -442,11 +446,11 @@
      * Set the bytes in data to be the raw bytes of this Parcel.
      */
     public final void unmarshall(byte[] data, int offset, int length) {
-        nativeUnmarshall(mNativePtr, data, offset, length);
+        updateNativeSize(nativeUnmarshall(mNativePtr, data, offset, length));
     }
 
     public final void appendFrom(Parcel parcel, int offset, int length) {
-        nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
+        updateNativeSize(nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length));
     }
 
     /**
@@ -599,7 +603,24 @@
      * if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p>
      */
     public final void writeFileDescriptor(FileDescriptor val) {
-        nativeWriteFileDescriptor(mNativePtr, val);
+        updateNativeSize(nativeWriteFileDescriptor(mNativePtr, val));
+    }
+
+    private void updateNativeSize(long newNativeSize) {
+        if (mOwnsNativeParcelObject) {
+            if (newNativeSize > Integer.MAX_VALUE) {
+                newNativeSize = Integer.MAX_VALUE;
+            }
+            if (newNativeSize != mNativeSize) {
+                int delta = (int) (newNativeSize - mNativeSize);
+                if (delta > 0) {
+                    VMRuntime.getRuntime().registerNativeAllocation(delta);
+                } else {
+                    VMRuntime.getRuntime().registerNativeFree(-delta);
+                }
+                mNativeSize = newNativeSize;
+            }
+        }
     }
 
     /**
@@ -2545,7 +2566,7 @@
 
     private void freeBuffer() {
         if (mOwnsNativeParcelObject) {
-            nativeFreeBuffer(mNativePtr);
+            updateNativeSize(nativeFreeBuffer(mNativePtr));
         }
     }
 
@@ -2553,6 +2574,7 @@
         if (mNativePtr != 0) {
             if (mOwnsNativeParcelObject) {
                 nativeDestroy(mNativePtr);
+                updateNativeSize(0);
             }
             mNativePtr = 0;
         }
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 979c828..2445bc2 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -383,6 +383,7 @@
                 final IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
                 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
                 filter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
+                filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
                 mContext.registerReceiver(this, filter);
             } else {
                 mContext.unregisterReceiver(this);
@@ -395,13 +396,7 @@
             if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) {
                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
                 int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
-                final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType)
-                        : (streamType == mStreamType);
-                if (mSeekBar != null && streamMatch && streamValue != -1) {
-                    final boolean muted = mAudioManager.isStreamMute(mStreamType)
-                            || streamValue == 0;
-                    mUiHandler.postUpdateSlider(streamValue, mLastAudibleStreamVolume, muted);
-                }
+                updateVolumeSlider(streamType, streamValue);
             } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
                 if (mNotificationOrRing) {
                     mRingerMode = mAudioManager.getRingerModeInternal();
@@ -409,10 +404,24 @@
                 if (mAffectedByRingerMode) {
                     updateSlider();
                 }
+            } else if (AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) {
+                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+                int streamVolume = mAudioManager.getStreamVolume(streamType);
+                updateVolumeSlider(streamType, streamVolume);
             } else if (NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED.equals(action)) {
                 mZenMode = mNotificationManager.getZenMode();
                 updateSlider();
             }
         }
+
+        private void updateVolumeSlider(int streamType, int streamValue) {
+            final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType)
+                    : (streamType == mStreamType);
+            if (mSeekBar != null && streamMatch && streamValue != -1) {
+                final boolean muted = mAudioManager.isStreamMute(mStreamType)
+                        || streamValue == 0;
+                mUiHandler.postUpdateSlider(streamValue, mLastAudibleStreamVolume, muted);
+            }
+        }
     }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 33f9476..746e110 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1434,25 +1434,6 @@
     }
 
     /**
-     * An app can use this method to check if it is currently allowed to change the network
-     * state. In order to be allowed to do so, an app must first declare either the
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} or
-     * {@link android.Manifest.permission#WRITE_SETTINGS} permission in its manifest. If it
-     * is currently disallowed, it can prompt the user to grant it this capability through a
-     * management UI by sending an Intent with action
-     * {@link android.provider.Settings#ACTION_MANAGE_WRITE_SETTINGS}.
-     *
-     * @param context A context
-     * @return true if the calling app can change the state of network, false otherwise.
-     * @hide
-     */
-    public static boolean canChangeNetworkState(Context context) {
-        int uid = Binder.getCallingUid();
-        return Settings.isCallingPackageAllowedToChangeNetworkState(context, uid, Settings
-                .getPackageNameForUid(context, uid), false);
-    }
-
-    /**
      * System settings, containing miscellaneous system preferences.  This
      * table holds simple name/value pairs.  There are convenience
      * functions for accessing individual settings entries.
@@ -8350,7 +8331,7 @@
      * write/modify system settings, as the condition differs for pre-M, M+, and
      * privileged/preinstalled apps. If the provided uid does not match the
      * callingPackage, a negative result will be returned. The caller is expected to have
-     * either WRITE_SETTINGS or CHANGE_NETWORK_STATE permission declared.
+     * the WRITE_SETTINGS permission declared.
      *
      * Note: if the check is successful, the operation of this app will be updated to the
      * current time.
@@ -8366,31 +8347,22 @@
     /**
      * Performs a strict and comprehensive check of whether a calling package is allowed to
      * change the state of network, as the condition differs for pre-M, M+, and
-     * privileged/preinstalled apps. If the provided uid does not match the
-     * callingPackage, a negative result will be returned. The caller is expected to have
-     * either of CHANGE_NETWORK_STATE or WRITE_SETTINGS permission declared.
-     * @hide
-     */
-    public static boolean isCallingPackageAllowedToChangeNetworkState(Context context, int uid,
-            String callingPackage, boolean throwException) {
-        return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
-                callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS,
-                PM_CHANGE_NETWORK_STATE, false);
-    }
-
-    /**
-     * Performs a strict and comprehensive check of whether a calling package is allowed to
-     * change the state of network, as the condition differs for pre-M, M+, and
-     * privileged/preinstalled apps. If the provided uid does not match the
-     * callingPackage, a negative result will be returned. The caller is expected to have
-     * either CHANGE_NETWORK_STATE or WRITE_SETTINGS permission declared.
+     * privileged/preinstalled apps. The caller is expected to have either the
+     * CHANGE_NETWORK_STATE or the WRITE_SETTINGS permission declared. Either of these
+     * permissions allow changing network state; WRITE_SETTINGS is a runtime permission and
+     * can be revoked, but (except in M, excluding M MRs), CHANGE_NETWORK_STATE is a normal
+     * permission and cannot be revoked. See http://b/23597341
      *
-     * Note: if the check is successful, the operation of this app will be updated to the
-     * current time.
+     * Note: if the check succeeds because the application holds WRITE_SETTINGS, the operation
+     * of this app will be updated to the current time.
      * @hide
      */
     public static boolean checkAndNoteChangeNetworkStateOperation(Context context, int uid,
             String callingPackage, boolean throwException) {
+        if (context.checkCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE)
+                == PackageManager.PERMISSION_GRANTED) {
+            return true;
+        }
         return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
                 callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS,
                 PM_CHANGE_NETWORK_STATE, true);
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index db19f7a..8763496 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -41,6 +41,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.GregorianCalendar;
 import java.util.Locale;
 import java.util.Objects;
 import java.util.UUID;
@@ -68,6 +69,7 @@
     public static final int[] MINUTE_BUCKETS = generateMinuteBuckets();
     private static final int SECONDS_MS = 1000;
     private static final int MINUTES_MS = 60 * SECONDS_MS;
+    private static final int DAY_MINUTES = 24 * 60;
     private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
 
     private static final boolean DEFAULT_ALLOW_CALLS = true;
@@ -652,40 +654,68 @@
             boolean shortVersion) {
         final long now = System.currentTimeMillis();
         final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
-        return toTimeCondition(context, now + millis, minutesFromNow, now, userHandle,
-                shortVersion);
+        return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion);
     }
 
-    public static Condition toTimeCondition(Context context, long time, int minutes, long now,
+    public static Condition toTimeCondition(Context context, long time, int minutes,
             int userHandle, boolean shortVersion) {
-        final int num, summaryResId, line1ResId;
+        final int num;
+        String summary, line1, line2;
+        final CharSequence formattedTime = getFormattedTime(context, time, userHandle);
+        final Resources res = context.getResources();
         if (minutes < 60) {
             // display as minutes
             num = minutes;
-            summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short
+            int summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short
                     : R.plurals.zen_mode_duration_minutes_summary;
-            line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short
+            summary = res.getQuantityString(summaryResId, num, num, formattedTime);
+            int line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short
                     : R.plurals.zen_mode_duration_minutes;
-        } else {
+            line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
+            line2 = res.getString(R.string.zen_mode_until, formattedTime);
+        } else if (minutes < DAY_MINUTES) {
             // display as hours
             num =  Math.round(minutes / 60f);
-            summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short
+            int summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short
                     : R.plurals.zen_mode_duration_hours_summary;
-            line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short
+            summary = res.getQuantityString(summaryResId, num, num, formattedTime);
+            int line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short
                     : R.plurals.zen_mode_duration_hours;
+            line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
+            line2 = res.getString(R.string.zen_mode_until, formattedTime);
+        } else {
+            // display as day/time
+            summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime);
         }
-        final String skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma";
-        final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
-        final CharSequence formattedTime = DateFormat.format(pattern, time);
-        final Resources res = context.getResources();
-        final String summary = res.getQuantityString(summaryResId, num, num, formattedTime);
-        final String line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
-        final String line2 = res.getString(R.string.zen_mode_until, formattedTime);
         final Uri id = toCountdownConditionId(time);
         return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE,
                 Condition.FLAG_RELEVANT_NOW);
     }
 
+    public static Condition toNextAlarmCondition(Context context, long now, long alarm,
+            int userHandle) {
+        final CharSequence formattedTime = getFormattedTime(context, alarm, userHandle);
+        final Resources res = context.getResources();
+        final String line1 = res.getString(R.string.zen_mode_alarm, formattedTime);
+        final Uri id = toCountdownConditionId(alarm);
+        return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE,
+                Condition.FLAG_RELEVANT_NOW);
+    }
+
+    private static CharSequence getFormattedTime(Context context, long time, int userHandle) {
+        String skeleton = "EEE " + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
+        GregorianCalendar now = new GregorianCalendar();
+        GregorianCalendar endTime = new GregorianCalendar();
+        endTime.setTimeInMillis(time);
+        if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR)
+                && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH)
+                && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) {
+            skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma";
+        }
+        final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+        return DateFormat.format(pattern, time);
+    }
+
     // ==== Built-in system conditions ====
 
     public static final String SYSTEM_AUTHORITY = "android";
@@ -883,11 +913,6 @@
         return UUID.randomUUID().toString().replace("-", "");
     }
 
-    public static String getConditionLine1(Context context, ZenModeConfig config,
-            int userHandle, boolean shortVersion) {
-        return getConditionLine(context, config, userHandle, true /*useLine1*/, shortVersion);
-    }
-
     public static String getConditionSummary(Context context, ZenModeConfig config,
             int userHandle, boolean shortVersion) {
         return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion);
@@ -906,8 +931,8 @@
             if (time > 0) {
                 final long now = System.currentTimeMillis();
                 final long span = time - now;
-                c = toTimeCondition(context,
-                        time, Math.round(span / (float) MINUTES_MS), now, userHandle, shortVersion);
+                c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
+                        userHandle, shortVersion);
             }
             final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
             return TextUtils.isEmpty(rt) ? "" : rt;
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 10a994a..f2b6041 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -16,15 +16,17 @@
 
 package android.text;
 
-import com.android.internal.annotations.GuardedBy;
-
 import android.annotation.Nullable;
 import android.util.Log;
 
-import libcore.io.IoUtils;
+import com.android.internal.annotations.GuardedBy;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
 import java.util.HashMap;
 import java.util.Locale;
 
@@ -45,19 +47,29 @@
     @GuardedBy("sLock")
     final static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>();
 
-    final static Hyphenator sEmptyHyphenator = new Hyphenator(StaticLayout.nLoadHyphenator(""));
+    final static Hyphenator sEmptyHyphenator =
+            new Hyphenator(StaticLayout.nLoadHyphenator(null, 0), null);
 
     final private long mNativePtr;
 
-    private Hyphenator(long nativePtr) {
+    // We retain a reference to the buffer to keep the memory mapping valid
+    @SuppressWarnings("unused")
+    final private ByteBuffer mBuffer;
+
+    private Hyphenator(long nativePtr, ByteBuffer b) {
         mNativePtr = nativePtr;
+        mBuffer = b;
     }
 
-    public static long get(@Nullable Locale locale) {
+    public long getNativePtr() {
+        return mNativePtr;
+    }
+
+    public static Hyphenator get(@Nullable Locale locale) {
         synchronized (sLock) {
             Hyphenator result = sMap.get(locale);
             if (result != null) {
-                return result.mNativePtr;
+                return result;
             }
 
             // TODO: Convert this a proper locale-fallback system
@@ -67,7 +79,7 @@
             result = sMap.get(languageOnlyLocale);
             if (result != null) {
                 sMap.put(locale, result);
-                return result.mNativePtr;
+                return result;
             }
 
             // Fall back to script-only, if available
@@ -80,22 +92,28 @@
                 result = sMap.get(scriptOnlyLocale);
                 if (result != null) {
                     sMap.put(locale, result);
-                    return result.mNativePtr;
+                    return result;
                 }
             }
 
             sMap.put(locale, sEmptyHyphenator);  // To remember we found nothing.
         }
-        return sEmptyHyphenator.mNativePtr;
+        return sEmptyHyphenator;
     }
 
     private static Hyphenator loadHyphenator(String languageTag) {
-        String patternFilename = "hyph-"+languageTag.toLowerCase(Locale.US)+".pat.txt";
+        String patternFilename = "hyph-" + languageTag.toLowerCase(Locale.US) + ".hyb";
         File patternFile = new File(getSystemHyphenatorLocation(), patternFilename);
         try {
-            String patternData = IoUtils.readFileAsString(patternFile.getAbsolutePath());
-            long nativePtr = StaticLayout.nLoadHyphenator(patternData);
-            return new Hyphenator(nativePtr);
+            RandomAccessFile f = new RandomAccessFile(patternFile, "r");
+            try {
+                FileChannel fc = f.getChannel();
+                MappedByteBuffer buf = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
+                long nativePtr = StaticLayout.nLoadHyphenator(buf, 0);
+                return new Hyphenator(nativePtr, buf);
+            } finally {
+                f.close();
+            }
         } catch (IOException e) {
             Log.e(TAG, "error loading hyphenation " + patternFile, e);
             return null;
@@ -148,7 +166,7 @@
         sMap.put(null, null);
 
         // TODO: replace this with a discovery-based method that looks into /system/usr/hyphen-data
-        String[] availableLanguages = {"en-US", "eu", "hu", "hy", "nb", "nn", "sa", "und-Ethi"};
+        String[] availableLanguages = {"en-US", "eu", "hu", "hy", "nb", "nn", "und-Ethi"};
         for (int i = 0; i < availableLanguages.length; i++) {
             String languageTag = availableLanguages[i];
             Hyphenator h = loadHyphenator(languageTag);
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 3b0def2..fdc6cb1 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -29,6 +29,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
 
+import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.Locale;
 
@@ -341,7 +342,8 @@
 
         private void setLocale(Locale locale) {
             if (!locale.equals(mLocale)) {
-                nSetLocale(mNativePtr, locale.toLanguageTag(), Hyphenator.get(locale));
+                nSetLocale(mNativePtr, locale.toLanguageTag(),
+                        Hyphenator.get(locale).getNativePtr());
                 mLocale = locale;
             }
         }
@@ -625,7 +627,9 @@
 
                 chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
 
-                if (chooseHt.length != 0) {
+                if (chooseHt.length == 0) {
+                    chooseHt = null; // So that out() would not assume it has any contents
+                } else {
                     if (chooseHtv == null ||
                         chooseHtv.length < chooseHt.length) {
                         chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
@@ -808,7 +812,7 @@
 
                     v = out(source, here, endPos,
                             fmAscent, fmDescent, fmTop, fmBottom,
-                            v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, flags[breakIndex],
+                            v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, flags[breakIndex],
                             needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad,
                             chs, widths, paraStart, ellipsize, ellipsizedWidth,
                             lineWidths[breakIndex], paint, moreChars);
@@ -1243,7 +1247,7 @@
     private static native void nFreeBuilder(long nativePtr);
     private static native void nFinishBuilder(long nativePtr);
 
-    /* package */ static native long nLoadHyphenator(String patternData);
+    /* package */ static native long nLoadHyphenator(ByteBuffer buf, int offset);
 
     private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator);
 
diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java
index c82587b..6d1d893 100644
--- a/core/java/android/transition/ChangeBounds.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -432,23 +432,24 @@
                 return anim;
             }
         } else {
-            int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X);
-            int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y);
-            int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X);
-            int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y);
+            sceneRoot.getLocationInWindow(tempLocation);
+            int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X) - tempLocation[0];
+            int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y) - tempLocation[1];
+            int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X) - tempLocation[0];
+            int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y) - tempLocation[1];
             // TODO: also handle size changes: check bounds and animate size changes
             if (startX != endX || startY != endY) {
-                sceneRoot.getLocationInWindow(tempLocation);
-                Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
-                        Bitmap.Config.ARGB_8888);
+                final int width = view.getWidth();
+                final int height = view.getHeight();
+                Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                 Canvas canvas = new Canvas(bitmap);
                 view.draw(canvas);
                 final BitmapDrawable drawable = new BitmapDrawable(bitmap);
+                drawable.setBounds(startX, startY, startX + width, startY + height);
                 final float transitionAlpha = view.getTransitionAlpha();
                 view.setTransitionAlpha(0);
                 sceneRoot.getOverlay().add(drawable);
-                Path topLeftPath = getPathMotion().getPath(startX - tempLocation[0],
-                        startY - tempLocation[1], endX - tempLocation[0], endY - tempLocation[1]);
+                Path topLeftPath = getPathMotion().getPath(startX, startY, endX, endY);
                 PropertyValuesHolder origin = PropertyValuesHolder.ofObject(
                         DRAWABLE_ORIGIN_PROPERTY, null, topLeftPath);
                 ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, origin);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index da3a99b..d85ea65 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2355,7 +2355,13 @@
      *                            1      PFLAG3_APPLYING_INSETS
      *                           1       PFLAG3_FITTING_SYSTEM_WINDOWS
      *                          1        PFLAG3_NESTED_SCROLLING_ENABLED
-     *                         1         PFLAG3_ASSIST_BLOCKED
+     *                         1         PFLAG3_SCROLL_INDICATOR_TOP
+     *                        1          PFLAG3_SCROLL_INDICATOR_BOTTOM
+     *                       1           PFLAG3_SCROLL_INDICATOR_LEFT
+     *                      1            PFLAG3_SCROLL_INDICATOR_RIGHT
+     *                     1             PFLAG3_SCROLL_INDICATOR_START
+     *                    1              PFLAG3_SCROLL_INDICATOR_END
+     *                   1               PFLAG3_ASSIST_BLOCKED
      * |-------|-------|-------|-------|
      */
 
@@ -2549,7 +2555,7 @@
      * <p>Indicates that we are allowing {@link ViewStructure} to traverse
      * into this view.<p>
      */
-    static final int PFLAG3_ASSIST_BLOCKED = 0x100;
+    static final int PFLAG3_ASSIST_BLOCKED = 0x4000;
 
     /**
      * Always allow a user to over-scroll this view, provided it is a
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 5b042c6..13c1937 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,12 +16,6 @@
 
 package android.widget;
 
-import java.text.BreakIterator;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-
 import android.R;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
@@ -112,6 +106,12 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.EditableInputConnection;
 
+import java.text.BreakIterator;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
 
 /**
  * Helper class used by TextView to handle editable text views.
@@ -1004,8 +1004,7 @@
         }
 
         if (!handled && mTextActionMode != null) {
-            // TODO: Fix dragging in extracted mode.
-            if (touchPositionIsInSelection() && !mTextView.isInExtractedMode()) {
+            if (touchPositionIsInSelection()) {
                 // Start a drag
                 final int start = mTextView.getSelectionStart();
                 final int end = mTextView.getSelectionEnd();
@@ -4254,10 +4253,14 @@
             positionAtCursorOffset(offset, false);
         }
 
+        /**
+         * @param offset Cursor offset. Must be in [-1, length].
+         * @param parentScrolled If the parent has been scrolled or not.
+         */
         @Override
         protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
             super.positionAtCursorOffset(offset, parentScrolled);
-            mInWord = !getWordIteratorWithText().isBoundary(offset);
+            mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset);
         }
 
         @Override
@@ -4490,10 +4493,14 @@
             positionAtCursorOffset(offset, false);
         }
 
+        /**
+         * @param offset Cursor offset. Must be in [-1, length].
+         * @param parentScrolled If the parent has been scrolled or not.
+         */
         @Override
         protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
             super.positionAtCursorOffset(offset, parentScrolled);
-            mInWord = !getWordIteratorWithText().isBoundary(offset);
+            mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset);
         }
 
         @Override
@@ -4860,9 +4867,8 @@
                         mEndHandle.showAtLocation(endOffset);
 
                         // No longer the first dragging motion, reset.
-                        if (!(mTextView.isInExtractedMode())) {
-                            startSelectionActionMode();
-                        }
+                        startSelectionActionMode();
+
                         mDragAcceleratorActive = false;
                         mStartOffset = -1;
                         mSwitchedLines = false;
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index ddbaa9d..cc6ab65 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -234,6 +234,8 @@
                 if (w != mDrawableWidth || h != mDrawableHeight) {
                     mDrawableWidth = w;
                     mDrawableHeight = h;
+                    // updates the matrix, which is dependent on the bounds
+                    configureBounds();
                 }
             }
             /* we invalidate the whole view in this case because it's very
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 4dfa7db..abcd614 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -704,7 +704,7 @@
             }
 
             final int heightMode;
-            if (params.width == LayoutParams.MATCH_PARENT) {
+            if (params.height == LayoutParams.MATCH_PARENT) {
                 heightMode = MeasureSpec.EXACTLY;
             } else {
                 heightMode = MeasureSpec.AT_MOST;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a1462c4..0c4b60b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,6 +16,8 @@
 
 package android.widget;
 
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+
 import android.R;
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
@@ -115,14 +117,14 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewParent;
-import android.view.ViewStructure;
 import android.view.ViewConfiguration;
 import android.view.ViewDebug;
 import android.view.ViewGroup.LayoutParams;
-import android.view.ViewRootImpl;
-import android.view.ViewTreeObserver;
 import android.view.ViewHierarchyEncoder;
+import android.view.ViewParent;
+import android.view.ViewRootImpl;
+import android.view.ViewStructure;
+import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -149,8 +151,6 @@
 import java.util.ArrayList;
 import java.util.Locale;
 
-import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
-
 /**
  * Displays text to the user and optionally allows them to edit it.  A TextView
  * is a complete text editor, however the basic class is configured to not
@@ -1474,7 +1474,12 @@
                         }
                     }
                 }
+            } else if (mText instanceof Spannable) {
+                // Reset the selection.
+                stopTextActionMode();
+                Selection.setSelection((Spannable) mText, getSelectionStart(), getSelectionEnd());
             }
+
             if (mEditor.hasSelectionController()) {
                 mEditor.startSelectionActionMode();
             }
@@ -5243,14 +5248,6 @@
             mEditor.mCreatedWithASelection = false;
         }
 
-        // Phone specific code (there is no ExtractEditText on tablets).
-        // ExtractEditText does not call onFocus when it is displayed, and mHasSelectionOnFocus can
-        // not be set. Do the test here instead.
-        if (isInExtractedMode() && hasSelection() && mEditor != null
-                && mEditor.mTextActionMode == null && isShown() && hasWindowFocus()) {
-            mEditor.startSelectionActionMode();
-        }
-
         unregisterForPreDraw();
 
         return true;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 2e0bf06..7699673 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.app;
 
+import android.animation.ObjectAnimator;
+import android.annotation.NonNull;
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
@@ -29,6 +31,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.database.DataSetObserver;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
@@ -46,13 +49,18 @@
 import android.service.chooser.IChooserTargetResult;
 import android.service.chooser.IChooserTargetService;
 import android.text.TextUtils;
+import android.util.FloatProperty;
 import android.util.Log;
 import android.util.Slog;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.MeasureSpec;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.AbsListView;
 import android.widget.BaseAdapter;
 import android.widget.ListView;
@@ -80,6 +88,7 @@
     private Intent mReferrerFillInIntent;
 
     private ChooserListAdapter mChooserListAdapter;
+    private ChooserRowAdapter mChooserRowAdapter;
 
     private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>();
 
@@ -253,7 +262,9 @@
             boolean alwaysUseOption) {
         final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
         mChooserListAdapter = (ChooserListAdapter) adapter;
-        adapterView.setAdapter(new ChooserRowAdapter(mChooserListAdapter));
+        mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter);
+        mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView));
+        adapterView.setAdapter(mChooserRowAdapter);
         if (listView != null) {
             listView.setItemsCanFocus(true);
         }
@@ -362,6 +373,11 @@
         int targetsToQuery = 0;
         for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) {
             final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i);
+            if (adapter.getScore(dri) == 0) {
+                // A score of 0 means the app hasn't been used in some time;
+                // don't query it as it's not likely to be relevant.
+                continue;
+            }
             final ActivityInfo ai = dri.getResolveInfo().activityInfo;
             final Bundle md = ai.metaData;
             final String serviceName = md != null ? convertServiceName(ai.packageName,
@@ -909,7 +925,63 @@
         @Override
         public int compare(ChooserTarget lhs, ChooserTarget rhs) {
             // Descending order
-            return (int) Math.signum(lhs.getScore() - rhs.getScore());
+            return (int) Math.signum(rhs.getScore() - lhs.getScore());
+        }
+    }
+
+    static class RowScale {
+        private static final int DURATION = 400;
+
+        float mScale;
+        ChooserRowAdapter mAdapter;
+        private final ObjectAnimator mAnimator;
+
+        public static final FloatProperty<RowScale> PROPERTY =
+                new FloatProperty<RowScale>("scale") {
+            @Override
+            public void setValue(RowScale object, float value) {
+                object.mScale = value;
+                object.mAdapter.notifyDataSetChanged();
+            }
+
+            @Override
+            public Float get(RowScale object) {
+                return object.mScale;
+            }
+        };
+
+        public RowScale(@NonNull ChooserRowAdapter adapter, float from, float to) {
+            mAdapter = adapter;
+            mScale = from;
+            if (from == to) {
+                mAnimator = null;
+                return;
+            }
+
+            mAnimator = ObjectAnimator.ofFloat(this, PROPERTY, from, to).setDuration(DURATION);
+        }
+
+        public RowScale setInterpolator(Interpolator interpolator) {
+            if (mAnimator != null) {
+                mAnimator.setInterpolator(interpolator);
+            }
+            return this;
+        }
+
+        public float get() {
+            return mScale;
+        }
+
+        public void startAnimation() {
+            if (mAnimator != null) {
+                mAnimator.start();
+            }
+        }
+
+        public void cancelAnimation() {
+            if (mAnimator != null) {
+                mAnimator.cancel();
+            }
         }
     }
 
@@ -917,15 +989,51 @@
         private ChooserListAdapter mChooserListAdapter;
         private final LayoutInflater mLayoutInflater;
         private final int mColumnCount = 4;
+        private RowScale[] mServiceTargetScale;
+        private final Interpolator mInterpolator;
 
         public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) {
             mChooserListAdapter = wrappedAdapter;
             mLayoutInflater = LayoutInflater.from(ChooserActivity.this);
 
+            mInterpolator = AnimationUtils.loadInterpolator(ChooserActivity.this,
+                    android.R.interpolator.decelerate_quint);
+
             wrappedAdapter.registerDataSetObserver(new DataSetObserver() {
                 @Override
                 public void onChanged() {
                     super.onChanged();
+                    final int rcount = getServiceTargetRowCount();
+                    if (mServiceTargetScale == null
+                            || mServiceTargetScale.length != rcount) {
+                        RowScale[] old = mServiceTargetScale;
+                        int oldRCount = old != null ? old.length : 0;
+                        mServiceTargetScale = new RowScale[rcount];
+                        if (old != null && rcount > 0) {
+                            System.arraycopy(old, 0, mServiceTargetScale, 0,
+                                    Math.min(old.length, rcount));
+                        }
+
+                        for (int i = rcount; i < oldRCount; i++) {
+                            old[i].cancelAnimation();
+                        }
+
+                        for (int i = oldRCount; i < rcount; i++) {
+                            final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f)
+                                    .setInterpolator(mInterpolator);
+                            mServiceTargetScale[i] = rs;
+                        }
+
+                        // Start the animations in a separate loop.
+                        // The process of starting animations will result in
+                        // binding views to set up initial values, and we must
+                        // have ALL of the new RowScale objects created above before
+                        // we get started.
+                        for (int i = oldRCount; i < rcount; i++) {
+                            mServiceTargetScale[i].startAnimation();
+                        }
+                    }
+
                     notifyDataSetChanged();
                 }
 
@@ -933,19 +1041,43 @@
                 public void onInvalidated() {
                     super.onInvalidated();
                     notifyDataSetInvalidated();
+                    if (mServiceTargetScale != null) {
+                        for (RowScale rs : mServiceTargetScale) {
+                            rs.cancelAnimation();
+                        }
+                    }
                 }
             });
         }
 
+        private float getRowScale(int rowPosition) {
+            final int start = getCallerTargetRowCount();
+            final int end = start + getServiceTargetRowCount();
+            if (rowPosition >= start && rowPosition < end) {
+                return mServiceTargetScale[rowPosition - start].get();
+            }
+            return 1.f;
+        }
+
         @Override
         public int getCount() {
             return (int) (
-                    Math.ceil((float) mChooserListAdapter.getCallerTargetCount() / mColumnCount)
-                    + Math.ceil((float) mChooserListAdapter.getServiceTargetCount() / mColumnCount)
+                    getCallerTargetRowCount()
+                    + getServiceTargetRowCount()
                     + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount)
             );
         }
 
+        public int getCallerTargetRowCount() {
+            return (int) Math.ceil(
+                    (float) mChooserListAdapter.getCallerTargetCount() / mColumnCount);
+        }
+
+        public int getServiceTargetRowCount() {
+            return (int) Math.ceil(
+                    (float) mChooserListAdapter.getServiceTargetCount() / mColumnCount);
+        }
+
         @Override
         public Object getItem(int position) {
             // We have nothing useful to return here.
@@ -959,33 +1091,69 @@
 
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
-            final View[] holder;
+            final RowViewHolder holder;
             if (convertView == null) {
                 holder = createViewHolder(parent);
             } else {
-                holder = (View[]) convertView.getTag();
+                holder = (RowViewHolder) convertView.getTag();
             }
             bindViewHolder(position, holder);
 
-            // We keep the actual list item view as the last item in the holder array
-            return holder[mColumnCount];
+            return holder.row;
         }
 
-        View[] createViewHolder(ViewGroup parent) {
-            final View[] holder = new View[mColumnCount + 1];
-
+        RowViewHolder createViewHolder(ViewGroup parent) {
             final ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row,
                     parent, false);
+            final RowViewHolder holder = new RowViewHolder(row, mColumnCount);
+            final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+
             for (int i = 0; i < mColumnCount; i++) {
-                holder[i] = mChooserListAdapter.createView(row);
-                row.addView(holder[i]);
+                final View v = mChooserListAdapter.createView(row);
+                final int column = i;
+                v.setOnClickListener(new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        startSelected(holder.itemIndices[column], false, true);
+                    }
+                });
+                v.setOnLongClickListener(new OnLongClickListener() {
+                    @Override
+                    public boolean onLongClick(View v) {
+                        showAppDetails(
+                                mChooserListAdapter.resolveInfoForPosition(
+                                        holder.itemIndices[column], true));
+                        return true;
+                    }
+                });
+                row.addView(v);
+                holder.cells[i] = v;
+
+                // Force height to be a given so we don't have visual disruption during scaling.
+                LayoutParams lp = v.getLayoutParams();
+                v.measure(spec, spec);
+                if (lp == null) {
+                    lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight());
+                    row.setLayoutParams(lp);
+                } else {
+                    lp.height = v.getMeasuredHeight();
+                }
+            }
+
+            // Pre-measure so we can scale later.
+            holder.measure();
+            LayoutParams lp = row.getLayoutParams();
+            if (lp == null) {
+                lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.measuredRowHeight);
+                row.setLayoutParams(lp);
+            } else {
+                lp.height = holder.measuredRowHeight;
             }
             row.setTag(holder);
-            holder[mColumnCount] = row;
             return holder;
         }
 
-        void bindViewHolder(int rowPosition, View[] holder) {
+        void bindViewHolder(int rowPosition, RowViewHolder holder) {
             final int start = getFirstRowPosition(rowPosition);
             final int startType = mChooserListAdapter.getPositionTargetType(start);
 
@@ -994,34 +1162,26 @@
                 end--;
             }
 
-            final ViewGroup row = (ViewGroup) holder[mColumnCount];
-
             if (startType == ChooserListAdapter.TARGET_SERVICE) {
-                row.setBackgroundColor(getColor(R.color.chooser_service_row_background_color));
+                holder.row.setBackgroundColor(
+                        getColor(R.color.chooser_service_row_background_color));
             } else {
-                row.setBackground(null);
+                holder.row.setBackgroundColor(Color.TRANSPARENT);
+            }
+
+            final int oldHeight = holder.row.getLayoutParams().height;
+            holder.row.getLayoutParams().height = Math.max(1,
+                    (int) (holder.measuredRowHeight * getRowScale(rowPosition)));
+            if (holder.row.getLayoutParams().height != oldHeight) {
+                holder.row.requestLayout();
             }
 
             for (int i = 0; i < mColumnCount; i++) {
-                final View v = holder[i];
+                final View v = holder.cells[i];
                 if (start + i <= end) {
                     v.setVisibility(View.VISIBLE);
-                    final int itemIndex = start + i;
-                    mChooserListAdapter.bindView(itemIndex, v);
-                    v.setOnClickListener(new OnClickListener() {
-                        @Override
-                        public void onClick(View v) {
-                            startSelected(itemIndex, false, true);
-                        }
-                    });
-                    v.setOnLongClickListener(new OnLongClickListener() {
-                        @Override
-                        public boolean onLongClick(View v) {
-                            showAppDetails(
-                                    mChooserListAdapter.resolveInfoForPosition(itemIndex, true));
-                            return true;
-                        }
-                    });
+                    holder.itemIndices[i] = start + i;
+                    mChooserListAdapter.bindView(holder.itemIndices[i], v);
                 } else {
                     v.setVisibility(View.GONE);
                 }
@@ -1048,6 +1208,25 @@
         }
     }
 
+    static class RowViewHolder {
+        final View[] cells;
+        final ViewGroup row;
+        int measuredRowHeight;
+        int[] itemIndices;
+
+        public RowViewHolder(ViewGroup row, int cellCount) {
+            this.row = row;
+            this.cells = new View[cellCount];
+            this.itemIndices = new int[cellCount];
+        }
+
+        public void measure() {
+            final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+            row.measure(spec, spec);
+            measuredRowHeight = row.getMeasuredHeight();
+        }
+    }
+
     static class ChooserTargetServiceConnection implements ServiceConnection {
         private final DisplayResolveInfo mOriginalTarget;
         private ComponentName mConnectedComponent;
@@ -1199,4 +1378,44 @@
             mSelectedTarget = null;
         }
     }
+
+    class OffsetDataSetObserver extends DataSetObserver {
+        private final AbsListView mListView;
+        private int mCachedViewType = -1;
+        private View mCachedView;
+
+        public OffsetDataSetObserver(AbsListView listView) {
+            mListView = listView;
+        }
+
+        @Override
+        public void onChanged() {
+            if (mResolverDrawerLayout == null) {
+                return;
+            }
+
+            final int chooserTargetRows = mChooserRowAdapter.getServiceTargetRowCount();
+            int offset = 0;
+            for (int i = 0; i < chooserTargetRows; i++)  {
+                final int pos = mChooserRowAdapter.getCallerTargetRowCount() + i;
+                final int vt = mChooserRowAdapter.getItemViewType(pos);
+                if (vt != mCachedViewType) {
+                    mCachedView = null;
+                }
+                final View v = mChooserRowAdapter.getView(pos, mCachedView, mListView);
+                int height = ((RowViewHolder) (v.getTag())).measuredRowHeight;
+
+                offset += (int) (height * mChooserRowAdapter.getRowScale(pos));
+
+                if (vt >= 0) {
+                    mCachedViewType = vt;
+                    mCachedView = v;
+                } else {
+                    mCachedViewType = -1;
+                }
+            }
+
+            mResolverDrawerLayout.setCollapsibleHeightReserved(offset);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 1710489..4ba678c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -26,6 +26,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
+import android.view.View.OnAttachStateChangeListener;
 import android.widget.AbsListView;
 import com.android.internal.R;
 import com.android.internal.content.PackageMonitor;
@@ -103,6 +104,8 @@
     private ResolverComparator mResolverComparator;
     private PickTargetOptionRequest mPickOptionRequest;
 
+    protected ResolverDrawerLayout mResolverDrawerLayout;
+
     private boolean mRegistered;
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
         @Override public void onSomePackagesChanged() {
@@ -253,6 +256,7 @@
             if (isVoiceInteraction()) {
                 rdl.setCollapsed(false);
             }
+            mResolverDrawerLayout = rdl;
         }
 
         if (title == null) {
@@ -328,6 +332,18 @@
         if (isVoiceInteraction()) {
             onSetupVoiceInteraction();
         }
+
+        getWindow().getDecorView().addOnAttachStateChangeListener(
+                new OnAttachStateChangeListener() {
+            @Override
+            public void onViewAttachedToWindow(View v) {
+                v.getViewRootImpl().setDrawDuringWindowsAnimating(true);
+            }
+
+            @Override
+            public void onViewDetachedFromWindow(View v) {
+            }
+        });
     }
 
     /**
@@ -1570,7 +1586,10 @@
 
         private void onBindView(View view, TargetInfo info) {
             final ViewHolder holder = (ViewHolder) view.getTag();
-            holder.text.setText(info.getDisplayLabel());
+            final CharSequence label = info.getDisplayLabel();
+            if (!TextUtils.equals(holder.text.getText(), label)) {
+                holder.text.setText(info.getDisplayLabel());
+            }
             if (showsExtendedInfo(info)) {
                 holder.text2.setVisibility(View.VISIBLE);
                 holder.text2.setText(info.getExtendedInfo());
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index a7bdbe0..8e8d352 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -3010,16 +3010,16 @@
                 int vis = show ? VISIBLE : INVISIBLE;
                 visibilityChanged = state.targetVisibility != vis;
                 state.targetVisibility = vis;
+                LayoutParams lp = (LayoutParams) view.getLayoutParams();
+                if (lp.height != resolvedHeight || lp.width != resolvedWidth
+                        || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
+                    lp.height = resolvedHeight;
+                    lp.width = resolvedWidth;
+                    lp.gravity = resolvedGravity;
+                    lp.rightMargin = rightMargin;
+                    view.setLayoutParams(lp);
+                }
                 if (show) {
-                    LayoutParams lp = (LayoutParams) view.getLayoutParams();
-                    if (lp.height != resolvedHeight || lp.width != resolvedWidth
-                            || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
-                        lp.height = resolvedHeight;
-                        lp.width = resolvedWidth;
-                        lp.gravity = resolvedGravity;
-                        lp.rightMargin = rightMargin;
-                        view.setLayoutParams(lp);
-                    }
                     view.setBackgroundColor(color);
                 }
             }
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index 44df0ce..b44baa2 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -35,7 +35,7 @@
 public class FloatingActionMode extends ActionMode {
 
     private static final int MAX_HIDE_DURATION = 3000;
-    private static final int MOVING_HIDE_DELAY = 300;
+    private static final int MOVING_HIDE_DELAY = 50;
 
     private final Context mContext;
     private final ActionMode.Callback2 mCallback;
@@ -181,7 +181,6 @@
                 // Content rect is moving.
                 mOriginatingView.removeCallbacks(mMovingOff);
                 mFloatingToolbarVisibilityHelper.setMoving(true);
-                mFloatingToolbarVisibilityHelper.updateToolbarVisibility();
                 mOriginatingView.postDelayed(mMovingOff, MOVING_HIDE_DELAY);
 
                 mFloatingToolbar.setContentRect(mContentRectOnScreen);
@@ -189,9 +188,9 @@
             }
         } else {
             mFloatingToolbarVisibilityHelper.setOutOfBounds(true);
-            mFloatingToolbarVisibilityHelper.updateToolbarVisibility();
             mContentRectOnScreen.setEmpty();
         }
+        mFloatingToolbarVisibilityHelper.updateToolbarVisibility();
 
         mPreviousContentRectOnScreen.set(mContentRectOnScreen);
     }
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index ca6fe61..2a25db6 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1488,10 +1488,9 @@
     private static AnimatorSet createEnterAnimation(View view) {
         AnimatorSet animation =  new AnimatorSet();
         animation.playTogether(
-                ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(200),
+                ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150),
                 // Make sure that view.x is always fixed throughout the duration of this animation.
                 ObjectAnimator.ofFloat(view, View.X, view.getX(), view.getX()));
-        animation.setStartDelay(50);
         return animation;
     }
 
@@ -1506,7 +1505,7 @@
             View view, int startDelay, Animator.AnimatorListener listener) {
         AnimatorSet animation =  new AnimatorSet();
         animation.playTogether(
-                ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(200));
+                ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(100));
         animation.setStartDelay(startDelay);
         animation.addListener(listener);
         return animation;
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 7679624..c4347f8 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -69,6 +69,12 @@
     private int mCollapsibleHeight;
     private int mUncollapsibleHeight;
 
+    /**
+     * The height in pixels of reserved space added to the top of the collapsed UI;
+     * e.g. chooser targets
+     */
+    private int mCollapsibleHeightReserved;
+
     private int mTopOffset;
 
     private boolean mIsDragging;
@@ -153,12 +159,62 @@
         }
     }
 
+    public void setCollapsibleHeightReserved(int heightPixels) {
+        final int oldReserved = mCollapsibleHeightReserved;
+        mCollapsibleHeightReserved = heightPixels;
+
+        final int dReserved = mCollapsibleHeightReserved - oldReserved;
+        if (dReserved != 0 && mIsDragging) {
+            mLastTouchY -= dReserved;
+        }
+
+        final int oldCollapsibleHeight = mCollapsibleHeight;
+        mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight());
+
+        if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) {
+            return;
+        }
+
+        invalidate();
+    }
+
     private boolean isMoving() {
         return mIsDragging || !mScroller.isFinished();
     }
 
+    private boolean isDragging() {
+        return mIsDragging || getNestedScrollAxes() == SCROLL_AXIS_VERTICAL;
+    }
+
+    private boolean updateCollapseOffset(int oldCollapsibleHeight, boolean remainClosed) {
+        if (oldCollapsibleHeight == mCollapsibleHeight) {
+            return false;
+        }
+
+        if (isLaidOut()) {
+            final boolean isCollapsedOld = mCollapseOffset != 0;
+            if (remainClosed && (oldCollapsibleHeight < mCollapsibleHeight
+                    && mCollapseOffset == oldCollapsibleHeight)) {
+                // Stay closed even at the new height.
+                mCollapseOffset = mCollapsibleHeight;
+            } else {
+                mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight);
+            }
+            final boolean isCollapsedNew = mCollapseOffset != 0;
+            if (isCollapsedOld != isCollapsedNew) {
+                notifyViewAccessibilityStateChangedIfNeeded(
+                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+            }
+        } else {
+            // Start out collapsed at first unless we restored state for otherwise
+            mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight;
+        }
+        return true;
+    }
+
     private int getMaxCollapsedHeight() {
-        return isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight;
+        return (isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight)
+                + mCollapsibleHeightReserved;
     }
 
     public void setOnDismissedListener(OnDismissedListener listener) {
@@ -676,7 +732,7 @@
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (lp.alwaysShow && child.getVisibility() != GONE) {
                 measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
-                heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin;
+                heightUsed += getHeightUsed(child);
             }
         }
 
@@ -688,7 +744,7 @@
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (!lp.alwaysShow && child.getVisibility() != GONE) {
                 measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
-                heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin;
+                heightUsed += getHeightUsed(child);
             }
         }
 
@@ -697,30 +753,43 @@
                 heightUsed - alwaysShowHeight - getMaxCollapsedHeight());
         mUncollapsibleHeight = heightUsed - mCollapsibleHeight;
 
-        if (isLaidOut()) {
-            final boolean isCollapsedOld = mCollapseOffset != 0;
-            if (oldCollapsibleHeight < mCollapsibleHeight
-                    && mCollapseOffset == oldCollapsibleHeight) {
-                // Stay closed even at the new height.
-                mCollapseOffset = mCollapsibleHeight;
-            } else {
-                mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight);
-            }
-            final boolean isCollapsedNew = mCollapseOffset != 0;
-            if (isCollapsedOld != isCollapsedNew) {
-                notifyViewAccessibilityStateChangedIfNeeded(
-                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
-            }
-        } else {
-            // Start out collapsed at first unless we restored state for otherwise
-            mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight;
-        }
+        updateCollapseOffset(oldCollapsibleHeight, !isDragging());
 
         mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset;
 
         setMeasuredDimension(sourceWidth, heightSize);
     }
 
+    private int getHeightUsed(View child) {
+        // This method exists because we're taking a fast path at measuring ListViews that
+        // lets us get away with not doing the more expensive wrap_content measurement which
+        // imposes double child view measurement costs. If we're looking at a ListView, we can
+        // check against the lowest child view plus padding and margin instead of the actual
+        // measured height of the ListView. This lets the ListView hang off the edge when
+        // all of the content would fit on-screen.
+
+        int heightUsed = child.getMeasuredHeight();
+        if (child instanceof AbsListView) {
+            final AbsListView lv = (AbsListView) child;
+            final int lvPaddingBottom = lv.getPaddingBottom();
+
+            int lowest = 0;
+            for (int i = 0, N = lv.getChildCount(); i < N; i++) {
+                final int bottom = lv.getChildAt(i).getBottom() + lvPaddingBottom;
+                if (bottom > lowest) {
+                    lowest = bottom;
+                }
+            }
+
+            if (lowest < heightUsed) {
+                heightUsed = lowest;
+            }
+        }
+
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+        return lp.topMargin + heightUsed + lp.bottomMargin;
+    }
+
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         final int width = getWidth();
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index 738a62a..cb0abb6 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -1660,10 +1660,11 @@
         uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
         uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
         if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
+            // GainMap rectangle is relative to the active area origin.
             err = builder.addGainMapsForMetadata(lsmWidth,
                                                  lsmHeight,
-                                                 ymin,
-                                                 xmin,
+                                                 0,
+                                                 0,
                                                  height,
                                                  width,
                                                  opcodeCfaLayout,
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index a14afa0..41aa9ca 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -114,7 +114,7 @@
     return parcel ? parcel->dataCapacity() : 0;
 }
 
-static void android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size)
+static jlong android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
     if (parcel != NULL) {
@@ -122,7 +122,9 @@
         if (err != NO_ERROR) {
             signalExceptionForError(env, clazz, err);
         }
+        return parcel->getOpenAshmemSize();
     }
+    return 0;
 }
 
 static void android_os_Parcel_setDataPosition(JNIEnv* env, jclass clazz, jlong nativePtr, jint pos)
@@ -304,7 +306,7 @@
     }
 }
 
-static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
+static jlong android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
     if (parcel != NULL) {
@@ -313,7 +315,9 @@
         if (err != NO_ERROR) {
             signalExceptionForError(env, clazz, err);
         }
+        return parcel->getOpenAshmemSize();
     }
+    return 0;
 }
 
 static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -546,12 +550,14 @@
     return reinterpret_cast<jlong>(parcel);
 }
 
-static void android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
+static jlong android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
     if (parcel != NULL) {
         parcel->freeData();
+        return parcel->getOpenAshmemSize();
     }
+    return 0;
 }
 
 static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -589,12 +595,12 @@
     return ret;
 }
 
-static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
-                                         jbyteArray data, jint offset, jint length)
+static jlong android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
+                                          jbyteArray data, jint offset, jint length)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
     if (parcel == NULL || length < 0) {
-       return;
+       return 0;
     }
 
     jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0);
@@ -608,24 +614,26 @@
 
         env->ReleasePrimitiveArrayCritical(data, array, 0);
     }
+    return parcel->getOpenAshmemSize();
 }
 
-static void android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr,
-                                         jlong otherNativePtr, jint offset, jint length)
+static jlong android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr,
+                                          jlong otherNativePtr, jint offset, jint length)
 {
     Parcel* thisParcel = reinterpret_cast<Parcel*>(thisNativePtr);
     if (thisParcel == NULL) {
-       return;
+       return 0;
     }
     Parcel* otherParcel = reinterpret_cast<Parcel*>(otherNativePtr);
     if (otherParcel == NULL) {
-       return;
+       return thisParcel->getOpenAshmemSize();
     }
 
     status_t err = thisParcel->appendFrom(otherParcel, offset, length);
     if (err != NO_ERROR) {
         signalExceptionForError(env, clazz, err);
     }
+    return thisParcel->getOpenAshmemSize();
 }
 
 static jboolean android_os_Parcel_hasFileDescriptors(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -718,7 +726,7 @@
     {"nativeDataAvail",           "(J)I", (void*)android_os_Parcel_dataAvail},
     {"nativeDataPosition",        "(J)I", (void*)android_os_Parcel_dataPosition},
     {"nativeDataCapacity",        "(J)I", (void*)android_os_Parcel_dataCapacity},
-    {"nativeSetDataSize",         "(JI)V", (void*)android_os_Parcel_setDataSize},
+    {"nativeSetDataSize",         "(JI)J", (void*)android_os_Parcel_setDataSize},
     {"nativeSetDataPosition",     "(JI)V", (void*)android_os_Parcel_setDataPosition},
     {"nativeSetDataCapacity",     "(JI)V", (void*)android_os_Parcel_setDataCapacity},
 
@@ -733,7 +741,7 @@
     {"nativeWriteDouble",         "(JD)V", (void*)android_os_Parcel_writeDouble},
     {"nativeWriteString",         "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString},
     {"nativeWriteStrongBinder",   "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},
-    {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor},
+    {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", (void*)android_os_Parcel_writeFileDescriptor},
 
     {"nativeCreateByteArray",     "(J)[B", (void*)android_os_Parcel_createByteArray},
     {"nativeReadBlob",            "(J)[B", (void*)android_os_Parcel_readBlob},
@@ -751,12 +759,12 @@
     {"clearFileDescriptor",       "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_clearFileDescriptor},
 
     {"nativeCreate",              "()J", (void*)android_os_Parcel_create},
-    {"nativeFreeBuffer",          "(J)V", (void*)android_os_Parcel_freeBuffer},
+    {"nativeFreeBuffer",          "(J)J", (void*)android_os_Parcel_freeBuffer},
     {"nativeDestroy",             "(J)V", (void*)android_os_Parcel_destroy},
 
     {"nativeMarshall",            "(J)[B", (void*)android_os_Parcel_marshall},
-    {"nativeUnmarshall",          "(J[BII)V", (void*)android_os_Parcel_unmarshall},
-    {"nativeAppendFrom",          "(JJII)V", (void*)android_os_Parcel_appendFrom},
+    {"nativeUnmarshall",          "(J[BII)J", (void*)android_os_Parcel_unmarshall},
+    {"nativeAppendFrom",          "(JJII)J", (void*)android_os_Parcel_appendFrom},
     {"nativeHasFileDescriptors",  "(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
     {"nativeWriteInterfaceToken", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken},
     {"nativeEnforceInterface",    "(JLjava/lang/String;)V", (void*)android_os_Parcel_enforceInterface},
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index a151e00..83f76ea 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -117,9 +117,17 @@
     b->finish();
 }
 
-static jlong nLoadHyphenator(JNIEnv* env, jclass, jstring patternData) {
-    ScopedStringChars str(env, patternData);
-    Hyphenator* hyphenator = Hyphenator::load(str.get(), str.size());
+static jlong nLoadHyphenator(JNIEnv* env, jclass, jobject buffer, jint offset) {
+    const uint8_t* bytebuf = nullptr;
+    if (buffer != nullptr) {
+        void* rawbuf = env->GetDirectBufferAddress(buffer);
+        if (rawbuf != nullptr) {
+            bytebuf = reinterpret_cast<const uint8_t*>(rawbuf) + offset;
+        } else {
+            ALOGE("failed to get direct buffer address");
+        }
+    }
+    Hyphenator* hyphenator = Hyphenator::loadBinary(bytebuf);
     return reinterpret_cast<jlong>(hyphenator);
 }
 
@@ -177,7 +185,7 @@
     {"nNewBuilder", "()J", (void*) nNewBuilder},
     {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
     {"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
-    {"nLoadHyphenator", "(Ljava/lang/String;)J", (void*) nLoadHyphenator},
+    {"nLoadHyphenator", "(Ljava/nio/ByteBuffer;I)J", (void*) nLoadHyphenator},
     {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale},
     {"nSetupParagraph", "(J[CIFIF[IIII)V", (void*) nSetupParagraph},
     {"nSetIndents", "(J[I)V", (void*) nSetIndents},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0614d2c..1f6b6d3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1707,12 +1707,12 @@
         android:protectionLevel="signature|privileged" />
 
     <!-- Allows applications to change network connectivity state.
-         <p>Protection level: signature
+         <p>Protection level: normal
     -->
     <permission android:name="android.permission.CHANGE_NETWORK_STATE"
         android:description="@string/permdesc_changeNetworkState"
         android:label="@string/permlab_changeNetworkState"
-        android:protectionLevel="signature|preinstalled|appop|pre23" />
+        android:protectionLevel="normal" />
 
     <!-- Allows an application to clear the caches of all installed
          applications on the device.
@@ -2067,10 +2067,11 @@
     <permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
         android:protectionLevel="signature" />
 
-    <!-- Allows an application to monitor changes in tablet mode.
+    <!-- Allows an application to query tablet mode state and monitor changes
+         in it.
          <p>Not for use by third-party applications.
          @hide -->
-    <permission android:name="android.permission.TABLET_MODE_LISTENER"
+    <permission android:name="android.permission.TABLET_MODE"
         android:protectionLevel="signature" />
 
     <!-- Allows an application to request installing packages. Apps
diff --git a/core/res/res/drawable/spinner_background_material.xml b/core/res/res/drawable/spinner_background_material.xml
index d37f5b7..c2a2a26 100644
--- a/core/res/res/drawable/spinner_background_material.xml
+++ b/core/res/res/drawable/spinner_background_material.xml
@@ -17,18 +17,18 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android"
             android:paddingMode="stack"
             android:paddingStart="0dp"
-            android:paddingEnd="24dp"
+            android:paddingEnd="48dp"
             android:paddingLeft="0dp"
             android:paddingRight="0dp">
     <item
-        android:gravity="end|center_vertical"
-        android:width="24dp"
-        android:height="24dp"
+        android:gravity="end|fill_vertical"
+        android:width="48dp"
         android:drawable="@drawable/control_background_40dp_material" />
 
     <item
         android:drawable="@drawable/ic_spinner_caret"
         android:gravity="end|center_vertical"
         android:width="24dp"
-        android:height="24dp" />
+        android:height="24dp"
+        android:end="12dp" />
 </layer-list>
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index dcdfb6c..41726fb 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -85,7 +85,7 @@
 
     <ListView
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:id="@+id/resolver_list"
             android:clipToPadding="false"
             android:scrollbarStyle="outsideOverlay"
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 1c496f6..0a7ac77 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -70,6 +70,7 @@
               android:minLines="2"
               android:maxLines="2"
               android:gravity="top|center_horizontal"
-              android:ellipsize="marquee" />
+              android:ellipsize="marquee"
+              android:visibility="gone" />
 </LinearLayout>
 
diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml
index fb639ca..0f39e42 100755
--- a/core/res/res/values-mcc204-mnc04/config.xml
+++ b/core/res/res/values-mcc204-mnc04/config.xml
@@ -44,14 +44,5 @@
         <item>false</item>
     </string-array>
 
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-        <item>[ApnSettingV3]SaskTel Tethering,inet.stm.sk.ca,,,,,,,,,204,04,,DUN,,,true,0,,,,,,,gid,5A</item>
-    </string-array>
-
     <string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true;BAE0000000000000</string>
 </resources>
diff --git a/core/res/res/values-mcc302-mnc780/config.xml b/core/res/res/values-mcc302-mnc780/config.xml
index 51abd36..a48f695 100644
--- a/core/res/res/values-mcc302-mnc780/config.xml
+++ b/core/res/res/values-mcc302-mnc780/config.xml
@@ -21,25 +21,6 @@
      for different hardware and product builds. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
 
-    <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
-    <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
-    <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
-    <integer-array translatable="false" name="config_tether_upstream_types">
-      <item>1</item>
-      <item>4</item>
-      <item>7</item>
-      <item>9</item>
-    </integer-array>
-
-    <!-- String containing the apn value for tethering.  May be overriden by secure settings
-         TETHER_DUN_APN.  Value is a comma separated series of strings:
-         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
-         Or string format of ApnSettingV3.
-         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
-    <string-array translatable="false" name="config_tether_apndata">
-      <item>SaskTel Tethering,inet.stm.sk.ca,,,,,,,,,302,780,,DUN</item>
-    </string-array>
-
     <!-- Don't use roaming icon for considered operators -->
     <string-array translatable="false" name="config_operatorConsideredNonRoaming">
         <item>302</item>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 1a45b3a..cf08dea 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2095,6 +2095,10 @@
             <enum name="hdpi" value="240" />
             <!-- An extra high density screen, approximately 320dpi. -->
             <enum name="xhdpi" value="320" />
+            <!-- An extra extra high density screen, approximately 480dpi. -->
+            <enum name="xxhdpi" value="480" />
+            <!-- An extra extra extra high density screen, approximately 640dpi. -->
+            <enum name="xxxhdpi" value="640" />
         </attr>
     </declare-styleable>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0859e5a..392e503 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -938,6 +938,9 @@
     <!-- Is the device capable of hot swapping an UICC Card -->
     <bool name="config_hotswapCapable">false</bool>
 
+    <!-- Component name of the ICC hotswap prompt for restart dialog -->
+    <string name="config_iccHotswapPromptForRestartDialogComponent" translateable="false">@null</string>
+
     <!-- Enable puk unlockscreen by default.
          If unlock screen is disabled, the puk should be unlocked through Emergency Dialer -->
     <bool name="config_enable_puk_unlock_screen">true</bool>
@@ -1246,6 +1249,9 @@
     <!-- Operating volatage for bluetooth controller. 0 by default-->
     <integer translatable="false" name="config_bluetooth_operating_voltage_mv">4</integer>
 
+    <!-- Whether supported profiles should be reloaded upon enabling bluetooth -->
+    <bool name="config_bluetooth_reload_supported_profiles_when_enabled">false</bool>
+
     <!-- The default data-use polling period. -->
     <integer name="config_datause_polling_period_sec">600</integer>
 
@@ -1282,6 +1288,10 @@
          device is data-only. -->
     <bool name="config_voice_capable">true</bool>
 
+    <!-- Flag indicating that an outbound call must have a call capable phone account
+         that has declared it can process the call's handle. -->
+    <bool name="config_requireCallCapableAccountForHandle">false</bool>
+
     <!-- Flag indicating if the user is notified when the mobile network access is restricted -->
     <bool name="config_user_notification_of_restrictied_mobile_access">true</bool>
 
@@ -2348,4 +2358,8 @@
     <!-- Name of the component to handle network policy notifications. If present,
          disables NetworkPolicyManagerService's presentation of data-usage notifications. -->
     <string translatable="false" name="config_networkPolicyNotificationComponent"></string>
+
+    <!-- The BT name of the keyboard packaged with the device. If this is defined, SystemUI will
+         automatically try to pair with it when the device exits tablet mode. -->
+    <string translatable="false" name="config_packagedKeyboardName"></string>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d9fa287..8a9b9cf 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4036,6 +4036,9 @@
     <!-- Zen mode condition - line two: ending time. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_until">Until <xliff:g id="formattedTime" example="10:00 PM">%1$s</xliff:g></string>
 
+    <!-- Zen mode condition - line one: Until next alarm. [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_alarm">Until <xliff:g id="formattedTime" example="10:00 PM">%1$s</xliff:g> (next alarm)</string>
+
     <!-- Zen mode condition: no exit criteria. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_forever">Until you turn this off</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6d8c38f..7754f86 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -274,6 +274,7 @@
   <java-symbol type="bool" name="config_ui_enableFadingMarquee" />
   <java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
   <java-symbol type="bool" name="config_voice_capable" />
+  <java-symbol type="bool" name="config_requireCallCapableAccountForHandle" />
   <java-symbol type="bool" name="config_user_notification_of_restrictied_mobile_access" />
   <java-symbol type="bool" name="config_wifiDisplaySupportsProtectedBuffers" />
   <java-symbol type="bool" name="preferences_prefer_dual_pane" />
@@ -369,6 +370,7 @@
   <java-symbol type="integer" name="config_bluetooth_rx_cur_ma" />
   <java-symbol type="integer" name="config_bluetooth_tx_cur_ma" />
   <java-symbol type="integer" name="config_bluetooth_operating_voltage_mv" />
+  <java-symbol type="bool" name="config_bluetooth_reload_supported_profiles_when_enabled" />
   <java-symbol type="integer" name="config_cursorWindowSize" />
   <java-symbol type="integer" name="config_drawLockTimeoutMillis" />
   <java-symbol type="integer" name="config_doublePressOnPowerBehavior" />
@@ -2088,6 +2090,7 @@
   <java-symbol type="string" name="zen_mode_default_events_name" />
   <java-symbol type="array" name="config_system_condition_providers" />
   <java-symbol type="string" name="muted_by" />
+  <java-symbol type="string" name="zen_mode_alarm" />
 
   <java-symbol type="string" name="select_day" />
   <java-symbol type="string" name="select_year" />
@@ -2336,4 +2339,7 @@
 
   <java-symbol type="string" name="config_networkPolicyNotificationComponent" />
   <java-symbol type="drawable" name="platlogo_m" />
+
+  <java-symbol type="string" name="config_iccHotswapPromptForRestartDialogComponent" />
+  <java-symbol type="string" name="config_packagedKeyboardName" />
 </resources>
diff --git a/data/keyboards/Vendor_18d1_Product_5018.kcm b/data/keyboards/Vendor_18d1_Product_5018.kcm
new file mode 100644
index 0000000..0ca85a2
--- /dev/null
+++ b/data/keyboards/Vendor_18d1_Product_5018.kcm
@@ -0,0 +1,321 @@
+# Copyright (C) 2015 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.
+
+#
+# Key character map for Google Pixel C Keyboard
+#
+
+type FULL
+
+### Basic QWERTY keys ###
+
+key A {
+    label:                              'A'
+    base:                               'a'
+    shift, capslock:                    'A'
+}
+
+key B {
+    label:                              'B'
+    base:                               'b'
+    shift, capslock:                    'B'
+}
+
+key C {
+    label:                              'C'
+    base:                               'c'
+    shift, capslock:                    'C'
+    alt:                                '\u00e7'
+    shift+alt:                          '\u00c7'
+}
+
+key D {
+    label:                              'D'
+    base:                               'd'
+    shift, capslock:                    'D'
+}
+
+key E {
+    label:                              'E'
+    base:                               'e'
+    shift, capslock:                    'E'
+    alt:                                '\u0301'
+}
+
+key F {
+    label:                              'F'
+    base:                               'f'
+    shift, capslock:                    'F'
+}
+
+key G {
+    label:                              'G'
+    base:                               'g'
+    shift, capslock:                    'G'
+}
+
+key H {
+    label:                              'H'
+    base:                               'h'
+    shift, capslock:                    'H'
+}
+
+key I {
+    label:                              'I'
+    base:                               'i'
+    shift, capslock:                    'I'
+    alt:                                '\u0302'
+}
+
+key J {
+    label:                              'J'
+    base:                               'j'
+    shift, capslock:                    'J'
+}
+
+key K {
+    label:                              'K'
+    base:                               'k'
+    shift, capslock:                    'K'
+}
+
+key L {
+    label:                              'L'
+    base:                               'l'
+    shift, capslock:                    'L'
+}
+
+key M {
+    label:                              'M'
+    base:                               'm'
+    shift, capslock:                    'M'
+}
+
+key N {
+    label:                              'N'
+    base:                               'n'
+    shift, capslock:                    'N'
+    alt:                                '\u0303'
+}
+
+key O {
+    label:                              'O'
+    base:                               'o'
+    shift, capslock:                    'O'
+    ralt:                               '['
+    ralt+shift:                         '{'
+}
+
+key P {
+    label:                              'P'
+    base:                               'p'
+    shift, capslock:                    'P'
+    ralt:                               ']'
+    ralt+shift:                         '}'
+}
+
+key Q {
+    label:                              'Q'
+    base:                               'q'
+    shift, capslock:                    'Q'
+}
+
+key R {
+    label:                              'R'
+    base:                               'r'
+    shift, capslock:                    'R'
+}
+
+key S {
+    label:                              'S'
+    base:                               's'
+    shift, capslock:                    'S'
+    alt:                                '\u00df'
+}
+
+key T {
+    label:                              'T'
+    base:                               't'
+    shift, capslock:                    'T'
+}
+
+key U {
+    label:                              'U'
+    base:                               'u'
+    shift, capslock:                    'U'
+    alt:                                '\u0308'
+}
+
+key V {
+    label:                              'V'
+    base:                               'v'
+    shift, capslock:                    'V'
+}
+
+key W {
+    label:                              'W'
+    base:                               'w'
+    shift, capslock:                    'W'
+}
+
+key X {
+    label:                              'X'
+    base:                               'x'
+    shift, capslock:                    'X'
+}
+
+key Y {
+    label:                              'Y'
+    base:                               'y'
+    shift, capslock:                    'Y'
+}
+
+key Z {
+    label:                              'Z'
+    base:                               'z'
+    shift, capslock:                    'Z'
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '0'
+    shift:                              ')'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '1'
+    shift:                              '!'
+    ralt:                               replace ESCAPE
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '2'
+    shift:                              '@'
+    ralt:                               '`'
+    ralt+shift:                         '~'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '3'
+    shift:                              '#'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '4'
+    shift:                              '$'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '5'
+    shift:                              '%'
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '6'
+    shift:                              '^'
+    alt+shift:                          '\u0302'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '7'
+    shift:                              '&'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '8'
+    shift:                              '*'
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '9'
+    shift:                              '('
+}
+
+key SPACE {
+    label:                              ' '
+    base:                               ' '
+    alt, meta:                          fallback SEARCH
+    ctrl:                               fallback LANGUAGE_SWITCH
+}
+
+key ENTER {
+    label:                              '\n'
+    base:                               '\n'
+}
+
+key TAB {
+    label:                              '\t'
+    base:                               '\t'
+}
+
+key COMMA {
+    label:                              ','
+    base:                               ','
+    shift:                              '<'
+}
+
+key PERIOD {
+    label:                              '.'
+    base:                               '.'
+    shift:                              '>'
+}
+
+key SLASH {
+    label:                              '/'
+    base:                               '/'
+    shift:                              '?'
+}
+
+key MINUS {
+    label:                              '-'
+    base:                               '-'
+    shift:                              '_'
+}
+
+key EQUALS {
+    label:                              '='
+    base:                               '='
+    shift:                              '+'
+    ralt:                               '\\'
+    ralt+shift:                         '|'
+}
+
+key SEMICOLON {
+    label:                              ';'
+    base:                               ';'
+    shift:                              ':'
+}
+
+key APOSTROPHE {
+    label:                              '\''
+    base:                               '\''
+    shift:                              '"'
+}
+
+### Non-printing keys ###
+
+key ESCAPE {
+    base:                               fallback BACK
+    alt, meta:                          fallback HOME
+    ctrl:                               fallback MENU
+}
diff --git a/data/keyboards/Vendor_18d1_Product_5018.kl b/data/keyboards/Vendor_18d1_Product_5018.kl
new file mode 100644
index 0000000..e95ccb5
--- /dev/null
+++ b/data/keyboards/Vendor_18d1_Product_5018.kl
@@ -0,0 +1,84 @@
+# Copyright (C) 2015 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.
+
+#
+# Key layout for Google Pixel C Keyboard
+#
+
+# Row 1
+key 2     1
+key 3     2
+key 4     3
+key 5     4
+key 6     5
+key 7     6
+key 8     7
+key 9     8
+key 10    9
+key 11    0
+key 12    MINUS
+key 14    DEL         # Backspace
+
+# Row 2
+key 15    TAB
+key 16    Q
+key 17    W
+key 18    E
+key 19    R
+key 20    T
+key 21    Y
+key 22    U
+key 23    I
+key 24    O
+key 25    P
+key 13    EQUALS
+key 28    ENTER
+
+# Row 3
+key 125   META_LEFT   # "Search key"
+key 30    A
+key 31    S
+key 32    D
+key 33    F
+key 34    G
+key 35    H
+key 36    J
+key 37    K
+key 38    L
+key 39    SEMICOLON
+key 40    APOSTROPHE
+
+# Row 4
+key 42    SHIFT_LEFT
+key 44    Z
+key 45    X
+key 46    C
+key 47    V
+key 48    B
+key 49    N
+key 50    M
+key 51    COMMA
+key 52    PERIOD
+key 53    SLASH
+key 54    SHIFT_RIGHT
+
+# Row 5
+key 29    CTRL_LEFT
+key 56    ALT_LEFT
+key 57    SPACE
+key 100   ALT_RIGHT
+key 103   DPAD_UP
+key 105   DPAD_LEFT
+key 106   DPAD_RIGHT
+key 108   DPAD_DOWN
diff --git a/docs/html/guide/topics/manifest/compatible-screens-element.jd b/docs/html/guide/topics/manifest/compatible-screens-element.jd
index 3606b15..de921d2 100644
--- a/docs/html/guide/topics/manifest/compatible-screens-element.jd
+++ b/docs/html/guide/topics/manifest/compatible-screens-element.jd
@@ -9,7 +9,7 @@
 <pre>
 &lt;<a href="#compatible-screens">compatible-screens</a>&gt;
     &lt;<a href="#screen">screen</a> android:<a href="#screenSize">screenSize</a>=["small" | "normal" | "large" | "xlarge"]
-            android:<a href="#screenDensity">screenDensity</a>=["ldpi" | "mdpi" | "hdpi" | "xhdpi"] /&gt;
+            android:<a href="#screenDensity">screenDensity</a>=["ldpi" | "mdpi" | "hdpi" | "xhdpi" | "xxhdpi" | "xxxhdpi"] /&gt;
     ...
 &lt;/compatible-screens&gt;
 </pre>
@@ -94,11 +94,9 @@
             <li>{@code mdpi}</li>
             <li>{@code hdpi}</li>
             <li>{@code xhdpi}</li>
+            <li>{@code xxhdpi}</li>
+            <li>{@code xxxhdpi}</li>
           </ul>
-          <p class="note"><strong>Note:</strong> This attribute currently does not accept
-          {@code xxhdpi} as a valid value, but you can instead specify {@code 480}
-          as the value, which is the approximate threshold for xhdpi screens.</p>
-
           <p>For information about the different screen densities, see <a
 href="{@docRoot}guide/practices/screens_support.html#range">Supporting Multiple Screens</a>.</p>
         </dd>
@@ -110,8 +108,8 @@
 <dt>example</dt>
 <dd>
 <p>If your application is compatible with only small and normal screens, regardless
-of screen density, then you must specify eight different {@code &lt;screen&gt;} elements,
-because each screen size has four different density configurations. You must declare each one of
+of screen density, then you must specify twelve different {@code &lt;screen&gt;} elements,
+because each screen size has six different density configurations. You must declare each one of
 these; any combination of size and density that you do <em>not</em> specify is considered a screen
 configuration with which your application is <em>not</em> compatible. Here's what the manifest
 entry looks like if your application is compatible with only small and normal screens:</p>
@@ -125,11 +123,15 @@
         &lt;screen android:screenSize="small" android:screenDensity="mdpi" />
         &lt;screen android:screenSize="small" android:screenDensity="hdpi" />
         &lt;screen android:screenSize="small" android:screenDensity="xhdpi" />
+        &lt;screen android:screenSize="small" android:screenDensity="xxhdpi" />
+        &lt;screen android:screenSize="small" android:screenDensity="xxxhdpi" />
         &lt;!-- all normal size screens -->
         &lt;screen android:screenSize="normal" android:screenDensity="ldpi" />
         &lt;screen android:screenSize="normal" android:screenDensity="mdpi" />
         &lt;screen android:screenSize="normal" android:screenDensity="hdpi" />
         &lt;screen android:screenSize="normal" android:screenDensity="xhdpi" />
+        &lt;screen android:screenSize="normal" android:screenDensity="xxhdpi" />
+        &lt;screen android:screenSize="normal" android:screenDensity="xxxhdpi" />
     &lt;/compatible-screens>
     &lt;application ... >
         ...
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index bbd4c30..1c2c940 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -479,11 +479,6 @@
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
  *         the object state. </p></td></tr>
- * <tr><td>setPlaybackRate</p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
  * <tr><td>setPlaybackParams</p></td>
  *     <td>any </p></td>
  *     <td>{} </p></td>
@@ -2243,10 +2238,14 @@
         final InputStream fIs = is;
         final MediaFormat fFormat = format;
 
-        // Ensure all input streams are closed.  It is also a handy
-        // way to implement timeouts in the future.
-        synchronized(mOpenSubtitleSources) {
-            mOpenSubtitleSources.add(is);
+        if (is != null) {
+            // Ensure all input streams are closed.  It is also a handy
+            // way to implement timeouts in the future.
+            synchronized(mOpenSubtitleSources) {
+                mOpenSubtitleSources.add(is);
+            }
+        } else {
+            Log.w(TAG, "addSubtitleSource called with null InputStream");
         }
 
         // process each subtitle in its own thread
diff --git a/media/java/android/media/midi/IBluetoothMidiService.aidl b/media/java/android/media/midi/IBluetoothMidiService.aidl
new file mode 100644
index 0000000..fe5566d
--- /dev/null
+++ b/media/java/android/media/midi/IBluetoothMidiService.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 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.media.midi;
+
+import android.bluetooth.BluetoothDevice;
+import android.os.IBinder;
+
+/** @hide */
+interface IBluetoothMidiService
+{
+    IBinder addBluetoothDevice(in BluetoothDevice bluetoothDevice);
+}
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index 7197dc0..266b0d9 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -169,6 +169,13 @@
     /**
      * Registers a callback to receive notifications when MIDI devices are added and removed.
      *
+     * The {@link  DeviceCallback#onDeviceStatusChanged} method will be called immediately
+     * for any devices that have open ports. This allows applications to know which input
+     * ports are already in use and, therefore, unavailable.
+     *
+     * Applications should call {@link #getDevices} before registering the callback
+     * to get a list of devices already added.
+     *
      * @param callback a {@link DeviceCallback} for MIDI device notifications
      * @param handler The {@link android.os.Handler Handler} that will be used for delivering the
      *                device notifications. If handler is null, then the thread used for the
@@ -288,7 +295,6 @@
                         // fetch MidiDeviceInfo from the server
                         MidiDeviceInfo deviceInfo = server.getDeviceInfo();
                         device = new MidiDevice(deviceInfo, server, mService, mToken, deviceToken);
-                        sendOpenDeviceResponse(device, listenerF, handlerF);
                     } catch (RemoteException e) {
                         Log.e(TAG, "remote exception in getDeviceInfo()");
                     }
diff --git a/media/packages/BluetoothMidiService/Android.mk b/media/packages/BluetoothMidiService/Android.mk
index 2c9c3c5..0565925 100644
--- a/media/packages/BluetoothMidiService/Android.mk
+++ b/media/packages/BluetoothMidiService/Android.mk
@@ -3,7 +3,8 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES += \
+      $(call all-java-files-under,src)
 
 LOCAL_PACKAGE_NAME := BluetoothMidiService
 LOCAL_CERTIFICATE := platform
diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml
index b0b389a..1cfd55d 100644
--- a/media/packages/BluetoothMidiService/AndroidManifest.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifest.xml
@@ -8,7 +8,7 @@
 
     <application
         android:label="@string/app_name">
-        <service android:name="BluetoothMidiService"
+        <service android:name=".BluetoothMidiService"
             android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
             <intent-filter>
                 <action android:name="android.media.midi.BluetoothMidiService" />
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index e6d59e4..444705c 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -147,14 +147,22 @@
             // switch to receiving notifications after initial characteristic read
             mBluetoothGatt.setCharacteristicNotification(characteristic, true);
 
+            // Use writeType that requests acknowledgement.
+            // This improves compatibility with various BLE-MIDI devices.
+            int originalWriteType = characteristic.getWriteType();
+            characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
+
             BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                     CLIENT_CHARACTERISTIC_CONFIG);
             if (descriptor != null) {
                 descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
-                mBluetoothGatt.writeDescriptor(descriptor);
+                boolean result = mBluetoothGatt.writeDescriptor(descriptor);
+                Log.d(TAG, "writeDescriptor returned " + result);
             } else {
                 Log.e(TAG, "No CLIENT_CHARACTERISTIC_CONFIG for device " + mBluetoothDevice);
             }
+
+            characteristic.setWriteType(originalWriteType);
         }
 
         @Override
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java
index fbde2b4..5541f9f 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java
@@ -19,6 +19,7 @@
 import android.app.Service;
 import android.bluetooth.BluetoothDevice;
 import android.content.Intent;
+import android.media.midi.IBluetoothMidiService;
 import android.media.midi.MidiManager;
 import android.os.IBinder;
 import android.util.Log;
@@ -34,24 +35,31 @@
 
     @Override
     public IBinder onBind(Intent intent) {
-        if (MidiManager.BLUETOOTH_MIDI_SERVICE_INTENT.equals(intent.getAction())) {
-            BluetoothDevice bluetoothDevice = (BluetoothDevice)intent.getParcelableExtra("device");
+        // Return the interface
+        return mBinder;
+    }
+
+
+    private final IBluetoothMidiService.Stub mBinder = new IBluetoothMidiService.Stub() {
+
+        public IBinder addBluetoothDevice(BluetoothDevice bluetoothDevice) {
+            BluetoothMidiDevice device;
             if (bluetoothDevice == null) {
-                Log.e(TAG, "no BluetoothDevice in onBind intent");
+                Log.e(TAG, "no BluetoothDevice in addBluetoothDevice()");
                 return null;
             }
-
-            BluetoothMidiDevice device;
             synchronized (mDeviceServerMap) {
                 device = mDeviceServerMap.get(bluetoothDevice);
                 if (device == null) {
-                    device = new BluetoothMidiDevice(this, bluetoothDevice, this);
+                    device = new BluetoothMidiDevice(BluetoothMidiService.this,
+                            bluetoothDevice, BluetoothMidiService.this);
+                    mDeviceServerMap.put(bluetoothDevice, device);
                 }
             }
             return device.getBinder();
         }
-        return null;
-    }
+
+    };
 
     void deviceClosed(BluetoothDevice device) {
         synchronized (mDeviceServerMap) {
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 382b2d0..ff14f94 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -3,6 +3,7 @@
 
     <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
     <uses-permission android:name="android.permission.REMOVE_TASKS" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
 
     <application
         android:name=".DocumentsApplication"
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index 506ec5833..f270d9e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -32,6 +32,7 @@
 import android.os.CancellationSignal;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.provider.DocumentsContract;
@@ -65,6 +66,8 @@
     // TODO: Move it to a shared file when more operations are implemented.
     public static final int FAILURE_COPY = 1;
 
+    private PowerManager mPowerManager;
+
     private NotificationManager mNotificationManager;
     private Notification.Builder mProgressBuilder;
 
@@ -129,10 +132,14 @@
             return;
         }
 
+        final PowerManager.WakeLock wakeLock = mPowerManager
+                .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
         final ArrayList<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST);
         final DocumentStack stack = intent.getParcelableExtra(EXTRA_STACK);
 
         try {
+            wakeLock.acquire();
+
             // Acquire content providers.
             mSrcClient = DocumentsApplication.acquireUnstableProviderOrThrow(getContentResolver(),
                     srcs.get(0).authority);
@@ -151,6 +158,8 @@
             ContentProviderClient.releaseQuietly(mSrcClient);
             ContentProviderClient.releaseQuietly(mDstClient);
 
+            wakeLock.release();
+
             // Dismiss the ongoing copy notification when the copy is done.
             mNotificationManager.cancel(mJobId, 0);
 
@@ -179,7 +188,8 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        mPowerManager = getSystemService(PowerManager.class);
+        mNotificationManager = getSystemService(NotificationManager.class);
     }
 
     /**
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
index f5908c5..407838a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
@@ -36,6 +36,7 @@
 
 import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.model.RootInfo;
+import com.android.internal.annotations.GuardedBy;
 import com.google.android.collect.Maps;
 import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.AbstractFuture;
@@ -81,6 +82,7 @@
     private final RootsCache mRoots;
     private final State mState;
 
+    @GuardedBy("mTasks")
     private final HashMap<RootInfo, RecentTask> mTasks = Maps.newHashMap();
 
     private final int mSortOrder = State.SORT_ORDER_LAST_MODIFIED;
@@ -167,6 +169,12 @@
 
     @Override
     public DirectoryResult loadInBackground() {
+        synchronized (mTasks) {
+            return loadInBackgroundLocked();
+        }
+    }
+
+    private DirectoryResult loadInBackgroundLocked() {
         if (mFirstPassLatch == null) {
             // First time through we kick off all the recent tasks, and wait
             // around to see if everyone finishes quickly.
@@ -304,8 +312,10 @@
         // Ensure the loader is stopped
         onStopLoading();
 
-        for (RecentTask task : mTasks.values()) {
-            IoUtils.closeQuietly(task);
+        synchronized (mTasks) {
+            for (RecentTask task : mTasks.values()) {
+                IoUtils.closeQuietly(task);
+            }
         }
 
         IoUtils.closeQuietly(mResult);
diff --git a/packages/FusedLocation/res/values-pt-rBR/strings.xml b/packages/FusedLocation/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..4f8277a
--- /dev/null
+++ b/packages/FusedLocation/res/values-pt-rBR/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="5379477904423203699">"Localização combinada"</string>
+</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index 0380e21..f935f31 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -19,6 +19,7 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
+import android.bluetooth.le.BluetoothLeScanner;
 import android.content.Context;
 import android.os.ParcelUuid;
 import android.util.Log;
@@ -106,6 +107,10 @@
         return mAdapter.getScanMode();
     }
 
+    public BluetoothLeScanner getBluetoothLeScanner() {
+        return mAdapter.getBluetoothLeScanner();
+    }
+
     public int getState() {
         return mAdapter.getState();
     }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 372fa03..80f4d4c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -127,6 +127,9 @@
     <!-- Assist -->
     <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
 
+    <!-- Listen for keyboard attachment / detachment -->
+    <uses-permission android:name="android.permission.TABLET_MODE" />
+
     <!-- Self permission for internal broadcasts. -->
     <permission android:name="com.android.systemui.permission.SELF"
             android:protectionLevel="signature" />
diff --git a/packages/SystemUI/res/values-en/donottranslate.xml b/packages/SystemUI/res/values-en/donottranslate.xml
deleted file mode 100644
index 9f04e1f..0000000
--- a/packages/SystemUI/res/values-en/donottranslate.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 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>
-    <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
-    <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time_fast</item>
-    <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
-    <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time_slowly</item>
-</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 527248c..49f7bdb 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -20,6 +20,7 @@
         <attr name="keyCode" format="integer" />
         <!-- does this button generate longpress / repeat events? -->
         <attr name="keyRepeat" format="boolean" />
+        <attr name="android:contentDescription" />
     </declare-styleable>
     <declare-styleable name="ToggleSlider">
         <attr name="text" format="string" />
diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml
index 30ff704..351a1fd 100644
--- a/packages/SystemUI/res/values/donottranslate.xml
+++ b/packages/SystemUI/res/values/donottranslate.xml
@@ -20,10 +20,4 @@
     <!-- Date format for display: should match the lockscreen in /policy.  -->
     <string name="system_ui_date_pattern">@*android:string/system_ui_date_pattern</string>
 
-    <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
-    <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time</item>
-
-    <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
-    <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time</item>
-
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index dfa85ce..dc9c9fd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1126,4 +1126,14 @@
     <!-- Dialog asking if the tuner should really be removed from settings [CHAR LIMIT=NONE]-->
     <string name="remove_from_settings_prompt">Remove System UI Tuner from Settings and stop using all of its features?"</string>
 
+    <!-- Dialog title asking if Bluetooth should be enabled [CHAR LIMIT=NONE] -->
+    <string name="enable_bluetooth_title">Turn on Bluetooth?</string>
+
+    <!-- Dialog message explaining why Bluetooth should be enabled when a packaged keyboard is
+         conncted to the device [CHAR LIMIT=NONE] -->
+    <string name="enable_bluetooth_message">To connect your keyboard with your tablet, you first have to turn on Bluetooth.</string>
+
+    <!-- Bluetooth enablement ok text [CHAR LIMIT=40] -->
+    <string name="enable_bluetooth_confirmation_ok">Turn on</string>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 33bd726..0b066af 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -48,6 +48,7 @@
             com.android.systemui.usb.StorageNotification.class,
             com.android.systemui.power.PowerUI.class,
             com.android.systemui.media.RingtonePlayer.class,
+            com.android.systemui.keyboard.KeyboardUI.class,
     };
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java b/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java
new file mode 100644
index 0000000..64f3e13f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+public class BluetoothDialog extends SystemUIDialog {
+
+    public BluetoothDialog(Context context) {
+        super(context);
+
+        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+        setShowForAllUsers(true);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
new file mode 100644
index 0000000..96ee397
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard;
+
+import android.app.AlertDialog;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.view.WindowManager;
+
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeChangedListener {
+    private static final String TAG = "KeyboardUI";
+    private static final boolean DEBUG = false;
+
+    // Give BT some time to start after SyUI comes up. This avoids flashing a dialog in the user's
+    // face because BT starts a little bit later in the boot process than SysUI and it takes some
+    // time for us to receive the signal that it's starting.
+    private static final long BLUETOOTH_START_DELAY_MILLIS = 10 * 1000;
+
+    private static final int STATE_NOT_ENABLED = -1;
+    private static final int STATE_UNKNOWN = 0;
+    private static final int STATE_WAITING_FOR_BOOT_COMPLETED = 1;
+    private static final int STATE_WAITING_FOR_TABLET_MODE_EXIT = 2;
+    private static final int STATE_WAITING_FOR_DEVICE_DISCOVERY = 3;
+    private static final int STATE_WAITING_FOR_BLUETOOTH = 4;
+    private static final int STATE_WAITING_FOR_STATE_PAIRED = 5;
+    private static final int STATE_PAIRING = 6;
+    private static final int STATE_PAIRED = 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;
+    private static final int MSG_PROCESS_KEYBOARD_STATE = 2;
+    private static final int MSG_ENABLE_BLUETOOTH = 3;
+    private static final int MSG_ON_BLUETOOTH_STATE_CHANGED = 4;
+    private static final int MSG_ON_DEVICE_BOND_STATE_CHANGED = 5;
+    private static final int MSG_ON_BLUETOOTH_DEVICE_ADDED = 6;
+    private static final int MSG_ON_BLE_SCAN_FAILED = 7;
+    private static final int MSG_SHOW_BLUETOOTH_DIALOG = 8;
+    private static final int MSG_DISMISS_BLUETOOTH_DIALOG = 9;
+
+    private volatile KeyboardHandler mHandler;
+    private volatile KeyboardUIHandler mUIHandler;
+
+    protected volatile Context mContext;
+
+    private boolean mEnabled;
+    private String mKeyboardName;
+    private CachedBluetoothDeviceManager mCachedDeviceManager;
+    private LocalBluetoothAdapter mLocalBluetoothAdapter;
+    private LocalBluetoothProfileManager mProfileManager;
+    private boolean mBootCompleted;
+    private long mBootCompletedTime;
+
+    private int mInTabletMode = InputManager.SWITCH_STATE_UNKNOWN;
+    private ScanCallback mScanCallback;
+    private BluetoothDialog mDialog;
+
+    private int mState;
+
+    @Override
+    public void start() {
+        mContext = super.mContext;
+        HandlerThread thread = new HandlerThread("Keyboard", Process.THREAD_PRIORITY_BACKGROUND);
+        thread.start();
+        mHandler = new KeyboardHandler(thread.getLooper());
+        mHandler.sendEmptyMessage(MSG_INIT);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("KeyboardUI:");
+        pw.println("  mEnabled=" + mEnabled);
+        pw.println("  mBootCompleted=" + mEnabled);
+        pw.println("  mBootCompletedTime=" + mBootCompletedTime);
+        pw.println("  mKeyboardName=" + mKeyboardName);
+        pw.println("  mInTabletMode=" + mInTabletMode);
+        pw.println("  mState=" + stateToString(mState));
+    }
+
+    @Override
+    protected void onBootCompleted() {
+        mHandler.sendEmptyMessage(MSG_ON_BOOT_COMPLETED);
+    }
+
+    @Override
+    public void onTabletModeChanged(long whenNanos, boolean inTabletMode) {
+        if (DEBUG) {
+            Slog.d(TAG, "onTabletModeChanged(" + whenNanos + ", " + inTabletMode + ")");
+        }
+
+        if (inTabletMode && mInTabletMode != InputManager.SWITCH_STATE_ON
+                || !inTabletMode && mInTabletMode != InputManager.SWITCH_STATE_OFF) {
+            mInTabletMode = inTabletMode ?
+                    InputManager.SWITCH_STATE_ON : InputManager.SWITCH_STATE_OFF;
+            processKeyboardState();
+        }
+    }
+
+    // Shoud only be called on the handler thread
+    private void init() {
+        Context context = mContext;
+        mKeyboardName =
+                context.getString(com.android.internal.R.string.config_packagedKeyboardName);
+        if (TextUtils.isEmpty(mKeyboardName)) {
+            if (DEBUG) {
+                Slog.d(TAG, "No packaged keyboard name given.");
+            }
+            return;
+        }
+
+        LocalBluetoothManager bluetoothManager = LocalBluetoothManager.getInstance(context, null);
+        if (bluetoothManager == null)  {
+            if (DEBUG) {
+                Slog.e(TAG, "Failed to retrieve LocalBluetoothManager instance");
+            }
+            return;
+        }
+        mEnabled = true;
+        mCachedDeviceManager = bluetoothManager.getCachedDeviceManager();
+        mLocalBluetoothAdapter = bluetoothManager.getBluetoothAdapter();
+        mProfileManager = bluetoothManager.getProfileManager();
+        bluetoothManager.getEventManager().registerCallback(new BluetoothCallbackHandler());
+
+        InputManager im = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
+        im.registerOnTabletModeChangedListener(this, mHandler);
+        mInTabletMode = im.isInTabletMode();
+
+        processKeyboardState();
+        mUIHandler = new KeyboardUIHandler();
+    }
+
+    // Should only be called on the handler thread
+    private void processKeyboardState() {
+        mHandler.removeMessages(MSG_PROCESS_KEYBOARD_STATE);
+
+        if (!mEnabled) {
+            mState = STATE_NOT_ENABLED;
+            return;
+        }
+
+        if (!mBootCompleted) {
+            mState = STATE_WAITING_FOR_BOOT_COMPLETED;
+            return;
+        }
+
+        if (mInTabletMode != InputManager.SWITCH_STATE_OFF) {
+            if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY) {
+                stopScanning();
+            }
+            mState = STATE_WAITING_FOR_TABLET_MODE_EXIT;
+            return;
+        }
+
+        final int btState = mLocalBluetoothAdapter.getState();
+        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.
+            mUIHandler.sendEmptyMessage(MSG_DISMISS_BLUETOOTH_DIALOG);
+        }
+
+        if (btState == BluetoothAdapter.STATE_TURNING_ON) {
+            mState = STATE_WAITING_FOR_BLUETOOTH;
+            // Wait for bluetooth to fully come on.
+            return;
+        }
+
+        if (btState != BluetoothAdapter.STATE_ON) {
+            mState = STATE_WAITING_FOR_BLUETOOTH;
+            showBluetoothDialog();
+            return;
+        }
+
+        CachedBluetoothDevice device = getPairedKeyboard();
+        if (mState == STATE_WAITING_FOR_TABLET_MODE_EXIT || mState == STATE_WAITING_FOR_BLUETOOTH) {
+            if (device != null) {
+                // If we're just coming out of tablet mode or BT just turned on,
+                // then we want to go ahead and automatically connect to the
+                // keyboard. We want to avoid this in other cases because we might
+                // be spuriously called after the user has manually disconnected
+                // the keyboard, meaning we shouldn't try to automtically connect
+                // it again.
+                mState = STATE_PAIRED;
+                device.connect(false);
+                return;
+            }
+            mCachedDeviceManager.clearNonBondedDevices();
+        }
+
+        device = getDiscoveredKeyboard();
+        if (device != null) {
+            mState = STATE_PAIRING;
+            device.startPairing();
+        } else {
+            mState = STATE_WAITING_FOR_DEVICE_DISCOVERY;
+            startScanning();
+        }
+    }
+
+    // Should only be called on the handler thread
+    public void onBootCompletedInternal() {
+        mBootCompleted = true;
+        mBootCompletedTime = SystemClock.uptimeMillis();
+        if (mState == STATE_WAITING_FOR_BOOT_COMPLETED) {
+            processKeyboardState();
+        }
+    }
+
+    // Should only be called on the handler thread
+    private void showBluetoothDialog() {
+        if (isUserSetupComplete()) {
+            long now = SystemClock.uptimeMillis();
+            long earliestDialogTime = mBootCompletedTime + BLUETOOTH_START_DELAY_MILLIS;
+            if (earliestDialogTime < now) {
+                mUIHandler.sendEmptyMessage(MSG_SHOW_BLUETOOTH_DIALOG);
+            } else {
+                mHandler.sendEmptyMessageAtTime(MSG_PROCESS_KEYBOARD_STATE, earliestDialogTime);
+            }
+        } else {
+            // If we're in setup wizard and the keyboard is docked, just automatically enable BT.
+            mLocalBluetoothAdapter.enable();
+        }
+    }
+
+    private boolean isUserSetupComplete() {
+        ContentResolver resolver = mContext.getContentResolver();
+        return Secure.getIntForUser(
+                resolver, Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+    }
+
+    private CachedBluetoothDevice getPairedKeyboard() {
+        Set<BluetoothDevice> devices = mLocalBluetoothAdapter.getBondedDevices();
+        for (BluetoothDevice d : devices) {
+            if (mKeyboardName.equals(d.getName())) {
+                return getCachedBluetoothDevice(d);
+            }
+        }
+        return null;
+    }
+
+    private CachedBluetoothDevice getDiscoveredKeyboard() {
+        Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+        for (CachedBluetoothDevice d : devices) {
+            if (d.getName().equals(mKeyboardName)) {
+                return d;
+            }
+        }
+        return null;
+    }
+
+
+    private CachedBluetoothDevice getCachedBluetoothDevice(BluetoothDevice d) {
+        CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(d);
+        if (cachedDevice == null) {
+            cachedDevice = mCachedDeviceManager.addDevice(
+                    mLocalBluetoothAdapter, mProfileManager, d);
+        }
+        return cachedDevice;
+    }
+
+    private void startScanning() {
+        BluetoothLeScanner scanner = mLocalBluetoothAdapter.getBluetoothLeScanner();
+        ScanFilter filter = (new ScanFilter.Builder()).setDeviceName(mKeyboardName).build();
+        ScanSettings settings = (new ScanSettings.Builder())
+            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
+            .setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
+            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+            .setReportDelay(0)
+            .build();
+        mScanCallback = new KeyboardScanCallback();
+        scanner.startScan(Arrays.asList(filter), settings, mScanCallback);
+    }
+
+    private void stopScanning() {
+        if (mScanCallback != null) {
+            mLocalBluetoothAdapter.getBluetoothLeScanner().stopScan(mScanCallback);
+            mScanCallback = null;
+        }
+    }
+
+    // Should only be called on the handler thread
+    private void onDeviceAddedInternal(CachedBluetoothDevice d) {
+        if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY && d.getName().equals(mKeyboardName)) {
+            stopScanning();
+            d.startPairing();
+            mState = STATE_PAIRING;
+        }
+    }
+
+    // Should only be called on the handler thread
+    private void onBluetoothStateChangedInternal(int bluetoothState) {
+        if (bluetoothState == BluetoothAdapter.STATE_ON && mState == STATE_WAITING_FOR_BLUETOOTH) {
+            processKeyboardState();
+        }
+    }
+
+    // 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;
+        }
+    }
+
+    // Should only be called on the handler thread
+    private void onBleScanFailedInternal() {
+        mScanCallback = null;
+        if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY) {
+            mState = STATE_DEVICE_NOT_FOUND;
+        }
+    }
+
+    private final class KeyboardUIHandler extends Handler {
+        public KeyboardUIHandler() {
+            super(Looper.getMainLooper(), null, true /*async*/);
+        }
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case MSG_SHOW_BLUETOOTH_DIALOG: {
+                    DialogInterface.OnClickListener listener = new BluetoothDialogClickListener();
+                    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.show();
+                    break;
+                }
+                case MSG_DISMISS_BLUETOOTH_DIALOG: {
+                    if (mDialog != null) {
+                        mDialog.dismiss();
+                        mDialog = null;
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
+    private final class KeyboardHandler extends Handler {
+        public KeyboardHandler(Looper looper) {
+            super(looper, null, true /*async*/);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case MSG_INIT: {
+                    init();
+                    break;
+                }
+                case MSG_ON_BOOT_COMPLETED: {
+                    onBootCompletedInternal();
+                    break;
+                }
+                case MSG_PROCESS_KEYBOARD_STATE: {
+                    processKeyboardState();
+                    break;
+                }
+                case MSG_ENABLE_BLUETOOTH: {
+                    boolean enable = msg.arg1 == 1;
+                    if (enable) {
+                        mLocalBluetoothAdapter.enable();
+                    } else {
+                        mState = STATE_USER_CANCELLED;
+                    }
+                }
+                case MSG_ON_BLUETOOTH_STATE_CHANGED: {
+                    int bluetoothState = msg.arg1;
+                    onBluetoothStateChangedInternal(bluetoothState);
+                    break;
+                }
+                case MSG_ON_DEVICE_BOND_STATE_CHANGED: {
+                    CachedBluetoothDevice d = (CachedBluetoothDevice)msg.obj;
+                    int bondState = msg.arg1;
+                    onDeviceBondStateChangedInternal(d, bondState);
+                    break;
+                }
+                case MSG_ON_BLUETOOTH_DEVICE_ADDED: {
+                    BluetoothDevice d = (BluetoothDevice)msg.obj;
+                    CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(d);
+                    onDeviceAddedInternal(cachedDevice);
+                    break;
+
+                }
+                case MSG_ON_BLE_SCAN_FAILED: {
+                    onBleScanFailedInternal();
+                    break;
+                }
+            }
+        }
+    }
+
+    private final class BluetoothDialogClickListener implements DialogInterface.OnClickListener {
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            int enable = DialogInterface.BUTTON_POSITIVE == which ? 1 : 0;
+            mHandler.obtainMessage(MSG_ENABLE_BLUETOOTH, enable, 0).sendToTarget();
+            mDialog = null;
+        }
+    }
+
+    private final class KeyboardScanCallback extends ScanCallback {
+
+        private boolean isDeviceDiscoverable(ScanResult result) {
+            final ScanRecord scanRecord = result.getScanRecord();
+            final int flags = scanRecord.getAdvertiseFlags();
+            final int BT_DISCOVERABLE_MASK = 0x03;
+
+            return (flags & BT_DISCOVERABLE_MASK) != 0;
+        }
+
+        @Override
+        public void onBatchScanResults(List<ScanResult> results) {
+            if (DEBUG) {
+                Slog.d(TAG, "onBatchScanResults(" + results.size() + ")");
+            }
+
+            BluetoothDevice bestDevice = null;
+            int bestRssi = Integer.MIN_VALUE;
+
+            for (ScanResult result : results) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onBatchScanResults: considering " + result);
+                }
+
+                if (isDeviceDiscoverable(result) && result.getRssi() > bestRssi) {
+                    bestDevice = result.getDevice();
+                    bestRssi = result.getRssi();
+                }
+            }
+
+            if (bestDevice != null) {
+                mHandler.obtainMessage(MSG_ON_BLUETOOTH_DEVICE_ADDED, bestDevice).sendToTarget();
+            }
+        }
+
+        @Override
+        public void onScanFailed(int errorCode) {
+            if (DEBUG) {
+                Slog.d(TAG, "onScanFailed(" + errorCode + ")");
+            }
+            mHandler.obtainMessage(MSG_ON_BLE_SCAN_FAILED).sendToTarget();
+        }
+
+        @Override
+        public void onScanResult(int callbackType, ScanResult result) {
+            if (DEBUG) {
+                Slog.d(TAG, "onScanResult(" + callbackType + ", " + result + ")");
+            }
+
+            if (isDeviceDiscoverable(result)) {
+                mHandler.obtainMessage(MSG_ON_BLUETOOTH_DEVICE_ADDED,
+                        result.getDevice()).sendToTarget();
+            } else if (DEBUG) {
+                Slog.d(TAG, "onScanResult: device " + result.getDevice() +
+                       " is not discoverable, ignoring");
+            }
+        }
+    }
+
+    private final class BluetoothCallbackHandler implements BluetoothCallback {
+        @Override
+        public void onBluetoothStateChanged(int bluetoothState) {
+            mHandler.obtainMessage(MSG_ON_BLUETOOTH_STATE_CHANGED,
+                    bluetoothState, 0).sendToTarget();
+        }
+
+        @Override
+        public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+            mHandler.obtainMessage(MSG_ON_DEVICE_BOND_STATE_CHANGED,
+                    bondState, 0, cachedDevice).sendToTarget();
+        }
+
+        @Override
+        public void onDeviceAdded(CachedBluetoothDevice cachedDevice) { }
+        @Override
+        public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) { }
+        @Override
+        public void onScanningStateChanged(boolean started) { }
+        @Override
+        public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
+    }
+
+    private static String stateToString(int state) {
+        switch (state) {
+            case STATE_NOT_ENABLED:
+                return "STATE_NOT_ENABLED";
+            case STATE_WAITING_FOR_BOOT_COMPLETED:
+                return "STATE_WAITING_FOR_BOOT_COMPLETED";
+            case STATE_WAITING_FOR_TABLET_MODE_EXIT:
+                return "STATE_WAITING_FOR_TABLET_MODE_EXIT";
+            case STATE_WAITING_FOR_DEVICE_DISCOVERY:
+                return "STATE_WAITING_FOR_DEVICE_DISCOVERY";
+            case STATE_WAITING_FOR_BLUETOOTH:
+                return "STATE_WAITING_FOR_BLUETOOTH";
+            case STATE_WAITING_FOR_STATE_PAIRED:
+                return "STATE_WAITING_FOR_STATE_PAIRED";
+            case STATE_PAIRING:
+                return "STATE_PAIRING";
+            case STATE_PAIRED:
+                return "STATE_PAIRED";
+            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";
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 803a014..728d558 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -21,6 +21,7 @@
 import android.media.AudioManager;
 import android.media.MediaPlayer;
 import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
 import android.net.Uri;
 import android.os.Looper;
 import android.os.PowerManager;
@@ -36,7 +37,7 @@
  * - whenever audio is played, audio focus is requested,
  * - whenever audio playback is stopped or the playback completed, audio focus is abandoned.
  */
-public class NotificationPlayer implements OnCompletionListener {
+public class NotificationPlayer implements OnCompletionListener, OnErrorListener {
     private static final int PLAY = 1;
     private static final int STOP = 2;
     private static final boolean mDebug = false;
@@ -112,6 +113,7 @@
                     //  done playing. This class should be modified to use a single thread, on which
                     //  command are issued, and on which it receives the completion callbacks.
                     player.setOnCompletionListener(NotificationPlayer.this);
+                    player.setOnErrorListener(NotificationPlayer.this);
                     player.start();
                     if (mPlayer != null) {
                         mPlayer.release();
@@ -245,6 +247,13 @@
         }
     }
 
+    public boolean onError(MediaPlayer mp, int what, int extra) {
+        Log.e(mTag, "error " + what + " (extra=" + extra + ") playing notification");
+        // error happened, handle it just like a completion
+        onCompletion(mp);
+        return true;
+    }
+
     private String mTag;
     private CmdThread mThread;
     private CreationAndCompletionThread mCompletionThread;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index b330582..e4a37fb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs;
 
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Animatable;
@@ -320,6 +321,7 @@
 
     public interface Host {
         void startActivityDismissingKeyguard(Intent intent);
+        void startActivityDismissingKeyguard(PendingIntent intent);
         void warn(String message, Throwable t);
         void collapsePanels();
         Looper getLooper();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
index 3d0dc7b..c7f2284 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -99,7 +99,7 @@
         try {
             if (pi != null) {
                 if (pi.isActivity()) {
-                    getHost().startActivityDismissingKeyguard(pi.getIntent());
+                    getHost().startActivityDismissingKeyguard(pi);
                 } else {
                     pi.send();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 2e0b80a..be618e2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -162,6 +162,14 @@
                 mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
                 break;
             }
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                final int index = ev.getActionIndex();
+                mActivePointerId = ev.getPointerId(index);
+                mLastMotionX = (int) ev.getX(index);
+                mLastMotionY = (int) ev.getY(index);
+                mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
+                break;
+            }
             case MotionEvent.ACTION_MOVE: {
                 if (mActivePointerId == INACTIVE_POINTER_ID) break;
 
@@ -187,6 +195,20 @@
                 mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
                 break;
             }
+            case MotionEvent.ACTION_POINTER_UP: {
+                int pointerIndex = ev.getActionIndex();
+                int pointerId = ev.getPointerId(pointerIndex);
+                if (pointerId == mActivePointerId) {
+                    // Select a new active pointer id and reset the motion state
+                    final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
+                    mActivePointerId = ev.getPointerId(newPointerIndex);
+                    mLastMotionX = (int) ev.getX(newPointerIndex);
+                    mLastMotionY = (int) ev.getY(newPointerIndex);
+                    mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
+                    mVelocityTracker.clear();
+                }
+                break;
+            }
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP: {
                 // Animate the scroll back if we've cancelled
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index eecac63..9ff86eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1490,6 +1490,59 @@
         return true;
     }
 
+    public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
+        if (!isDeviceProvisioned()) return;
+
+        final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
+        final boolean afterKeyguardGone = intent.isActivity()
+                && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
+                mCurrentUserId);
+        dismissKeyguardThenExecute(new OnDismissAction() {
+            public boolean onDismiss() {
+                new Thread() {
+                    @Override
+                    public void run() {
+                        try {
+                            if (keyguardShowing && !afterKeyguardGone) {
+                                ActivityManagerNative.getDefault()
+                                        .keyguardWaitingForActivityDrawn();
+                            }
+
+                            // The intent we are sending is for the application, which
+                            // won't have permission to immediately start an activity after
+                            // the user switches to home.  We know it is safe to do at this
+                            // point, so make sure new activity switches are now allowed.
+                            ActivityManagerNative.getDefault().resumeAppSwitches();
+                        } catch (RemoteException e) {
+                        }
+
+                        try {
+                            intent.send();
+                        } catch (PendingIntent.CanceledException e) {
+                            // the stack trace isn't very helpful here.
+                            // Just log the exception message.
+                            Log.w(TAG, "Sending intent failed: " + e);
+
+                            // TODO: Dismiss Keyguard.
+                        }
+                        if (intent.isActivity()) {
+                            mAssistManager.hideAssist();
+                            overrideActivityPendingAppTransition(keyguardShowing
+                                    && !afterKeyguardGone);
+                        }
+                    }
+                }.start();
+
+                // close the shade if it was open
+                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+                        true /* force */, true /* delayed */);
+                visibilityChanged(false);
+
+                return true;
+            }
+        }, afterKeyguardGone);
+    }
+
     private final class NotificationClicker implements View.OnClickListener {
         public void onClick(final View v) {
             if (!(v instanceof ExpandableNotificationRow)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 5f01306..56e9af5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -32,6 +32,7 @@
 import android.view.ViewStub;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.LinearInterpolator;
+import android.widget.Chronometer;
 import android.widget.ImageView;
 
 import com.android.systemui.R;
@@ -88,6 +89,7 @@
     private NotificationGuts mGuts;
     private StatusBarNotification mStatusBarNotification;
     private boolean mIsHeadsUp;
+    private boolean mLastChronometerRunning = true;
     private View mExpandButton;
     private View mExpandButtonDivider;
     private ViewStub mExpandButtonStub;
@@ -294,6 +296,7 @@
      */
     public void setPinned(boolean pinned) {
         mIsPinned = pinned;
+        setChronometerRunning(mLastChronometerRunning);
     }
 
     public boolean isPinned() {
@@ -319,6 +322,41 @@
         return mJustClicked;
     }
 
+    public void setChronometerRunning(boolean running) {
+        mLastChronometerRunning = running;
+        setChronometerRunning(running, mPrivateLayout);
+        setChronometerRunning(running, mPublicLayout);
+        if (mChildrenContainer != null) {
+            List<ExpandableNotificationRow> notificationChildren =
+                    mChildrenContainer.getNotificationChildren();
+            for (int i = 0; i < notificationChildren.size(); i++) {
+                ExpandableNotificationRow child = notificationChildren.get(i);
+                child.setChronometerRunning(running);
+            }
+        }
+    }
+
+    private void setChronometerRunning(boolean running, NotificationContentView layout) {
+        if (layout != null) {
+            running = running || isPinned();
+            View contractedChild = layout.getContractedChild();
+            View expandedChild = layout.getExpandedChild();
+            View headsUpChild = layout.getHeadsUpChild();
+            setChronometerRunningForChild(running, contractedChild);
+            setChronometerRunningForChild(running, expandedChild);
+            setChronometerRunningForChild(running, headsUpChild);
+        }
+    }
+
+    private void setChronometerRunningForChild(boolean running, View child) {
+        if (child != null) {
+            View chronometer = child.findViewById(com.android.internal.R.id.chronometer);
+            if (chronometer instanceof Chronometer) {
+                ((Chronometer) chronometer).setStarted(running);
+            }
+        }
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 50d274d..fd84345 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -200,12 +200,12 @@
         switch (mChargingSpeed) {
             case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
                 chargingId = hasChargingTime
-                        ? R.string.keyguard_indication_charging_time_fast_if_translated
+                        ? R.string.keyguard_indication_charging_time_fast
                         : R.string.keyguard_plugged_in_charging_fast;
                 break;
             case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
                 chargingId = hasChargingTime
-                        ? R.string.keyguard_indication_charging_time_slowly_if_translated
+                        ? R.string.keyguard_indication_charging_time_slowly
                         : R.string.keyguard_plugged_in_charging_slowly;
                 break;
             default:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index f7c3c67..cc30882 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -250,6 +250,7 @@
     @Override
     public void setNoSims(boolean show) {
         mNoSimsVisible = show && !mBlockMobile;
+        apply();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java
index 9ef320bc..8f689c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.app.PendingIntent;
 import android.content.Intent;
 
 /**
@@ -24,6 +25,7 @@
  * Keyguard.
  */
 public interface ActivityStarter {
+    void startPendingIntentDismissingKeyguard(PendingIntent intent);
     void startActivity(Intent intent, boolean dismissShade);
     void startActivity(Intent intent, boolean dismissShade, Callback callback);
     void preventNextAnimation();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 8465101..73361bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3210,6 +3210,15 @@
         return !isDeviceProvisioned() || (mDisabled1 & StatusBarManager.DISABLE_SEARCH) != 0;
     }
 
+    public void postStartActivityDismissingKeyguard(final PendingIntent intent) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                startPendingIntentDismissingKeyguard(intent);
+            }
+        });
+    }
+
     public void postStartActivityDismissingKeyguard(final Intent intent, int delay) {
         mHandler.postDelayed(new Runnable() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 540b9d0..fa9c4bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -109,6 +109,14 @@
         }
     };
 
+    private Runnable mRemoveCastIconRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW");
+            mService.setIconVisibility(SLOT_CAST, false);
+        }
+    };
+
     public PhoneStatusBarPolicy(Context context, CastController cast, HotspotController hotspot,
             UserInfoController userInfoController, BluetoothController bluetooth) {
         mContext = context;
@@ -328,11 +336,17 @@
             }
         }
         if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting);
+        mHandler.removeCallbacks(mRemoveCastIconRunnable);
         if (isCasting) {
             mService.setIcon(SLOT_CAST, R.drawable.stat_sys_cast, 0,
                     mContext.getString(R.string.accessibility_casting));
+            mService.setIconVisibility(SLOT_CAST, true);
+        } else {
+            // don't turn off the screen-record icon for a few seconds, just to make sure the user
+            // has seen it
+            if (DEBUG) Log.v(TAG, "updateCast: hiding icon in 3 sec...");
+            mHandler.postDelayed(mRemoveCastIconRunnable, 3000);
         }
-        mService.setIconVisibility(SLOT_CAST, isCasting);
     }
 
     private void profileChanged(int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 12434ac..e66c63b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -134,6 +135,11 @@
     }
 
     @Override
+    public void startActivityDismissingKeyguard(PendingIntent intent) {
+        mStatusBar.postStartActivityDismissingKeyguard(intent);
+    }
+
+    @Override
     public void warn(String message, Throwable t) {
         // already logged
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index e9c4e49..971978d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -527,8 +527,8 @@
             startBatteryActivity();
         } else if (v == mAlarmStatus && mNextAlarm != null) {
             PendingIntent showIntent = mNextAlarm.getShowIntent();
-            if (showIntent != null && showIntent.isActivity()) {
-                mActivityStarter.startActivity(showIntent.getIntent(), true /* dismissShade */);
+            if (showIntent != null) {
+                mActivityStarter.startPendingIntentDismissingKeyguard(showIntent);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index ed9b123..4a95d3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -169,7 +169,6 @@
      */
     public void showNotification(NotificationData.Entry headsUp) {
         if (DEBUG) Log.v(TAG, "showNotification");
-        MetricsLogger.count(mContext, "note_peek", 1);
         addHeadsUpEntry(headsUp);
         updateNotification(headsUp, true);
         headsUp.setInterruption();
@@ -246,6 +245,9 @@
             return;
         }
         mHasPinnedNotification = hasPinnedNotification;
+        if (mHasPinnedNotification) {
+            MetricsLogger.count(mContext, "note_peek", 1);
+        }
         updateTouchableRegionListener();
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpPinnedModeChanged(hasPinnedNotification);
@@ -580,6 +582,13 @@
 
         @Override
         public int compareTo(HeadsUpEntry o) {
+            boolean selfFullscreen = hasFullScreenIntent(entry);
+            boolean otherFullscreen = hasFullScreenIntent(o.entry);
+            if (selfFullscreen && !otherFullscreen) {
+                return -1;
+            } else if (!selfFullscreen && otherFullscreen) {
+                return 1;
+            }
             return postTime < o.postTime ? 1
                     : postTime == o.postTime ? entry.key.compareTo(o.entry.key)
                             : -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 4c99792..4d268ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -18,12 +18,14 @@
 
 import android.app.ActivityManager;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.hardware.input.InputManager;
 import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.view.HapticFeedbackConstants;
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
@@ -43,6 +45,7 @@
 
 public class KeyButtonView extends ImageView {
 
+    private int mContentDescriptionRes;
     private long mDownTime;
     private int mCode;
     private int mTouchSlop;
@@ -79,8 +82,14 @@
 
         mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
 
+        TypedValue value = new TypedValue();
+        if (a.getValue(R.styleable.KeyButtonView_android_contentDescription, value)) {
+            mContentDescriptionRes = value.resourceId;
+        }
+
         a.recycle();
 
+
         setClickable(true);
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -88,6 +97,15 @@
     }
 
     @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        if (mContentDescriptionRes != 0) {
+            setContentDescription(mContext.getString(mContentDescriptionRes));
+        }
+    }
+
+    @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         if (mCode != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index b2df40a..0e91b0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -27,7 +27,6 @@
     void removeCallback(Callback callback);
     void setZen(int zen, Uri conditionId, String reason);
     int getZen();
-    void requestConditions(boolean request);
     ZenRule getManualRule();
     ZenModeConfig getConfig();
     long getNextAlarm();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index c07f1a8..96efea1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -121,15 +121,6 @@
     }
 
     @Override
-    public void requestConditions(boolean request) {
-        mRequesting = request;
-        mNoMan.requestZenModeConditions(mListener, request ? Condition.FLAG_RELEVANT_NOW : 0);
-        if (!mRequesting) {
-            mConditions.clear();
-        }
-    }
-
-    @Override
     public ZenRule getManualRule() {
         return mConfig == null ? null : mConfig.manualRule;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index ae086d4..36c4531 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1778,6 +1778,7 @@
         ((ExpandableView) child).setOnHeightChangedListener(this);
         generateAddAnimation(child, false /* fromMoreCard */);
         updateAnimationState(child);
+        updateChronometerForChild(child);
         if (canChildBeDismissed(child)) {
             // Make sure the dismissButton is visible and not in the animated state.
             // We need to do this to avoid a race where a clearable notification is added after the
@@ -2287,6 +2288,21 @@
         mStackScrollAlgorithm.setIsExpanded(isExpanded);
         if (changed) {
             updateNotificationAnimationStates();
+            updateChronometers();
+        }
+    }
+
+    private void updateChronometers() {
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            updateChronometerForChild(getChildAt(i));
+        }
+    }
+
+    private void updateChronometerForChild(View child) {
+        if (child instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+            row.setChronometerRunning(mIsExpanded);
         }
     }
 
@@ -2309,6 +2325,7 @@
         }
         mStackScrollAlgorithm.onReset(view);
         updateAnimationState(view);
+        updateChronometerForChild(view);
     }
 
     private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 7836411..e9f1095 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -104,6 +104,7 @@
     private final SpTexts mSpTexts;
     private final SparseBooleanArray mDynamic = new SparseBooleanArray();
     private final KeyguardManager mKeyguard;
+    private final AudioManager mAudioManager;
     private final int mExpandButtonAnimationDuration;
     private final ZenFooter mZenFooter;
     private final LayoutTransition mLayoutTransition;
@@ -135,6 +136,7 @@
         mCallback = callback;
         mSpTexts = new SpTexts(mContext);
         mKeyguard = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
 
         mDialog = new CustomDialog(mContext);
 
@@ -636,7 +638,8 @@
     private void updateFooterH() {
         if (D.BUG) Log.d(TAG, "updateFooterH");
         final boolean wasVisible = mZenFooter.getVisibility() == View.VISIBLE;
-        final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF;
+        final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF
+                && mAudioManager.isStreamAffectedByRingerMode(mActiveStream);
         if (wasVisible != visible && !visible) {
             prepareForCollapse();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 3c9a7fc..3337714 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Copyright (C) 2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -58,6 +58,8 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
 import java.util.Locale;
 import java.util.Objects;
 
@@ -76,6 +78,8 @@
     private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
     private static final int FOREVER_CONDITION_INDEX = 0;
     private static final int COUNTDOWN_CONDITION_INDEX = 1;
+    private static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2;
+    private static final int COUNTDOWN_CONDITION_COUNT = 2;
 
     public static final Intent ZEN_SETTINGS
             = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
@@ -86,7 +90,6 @@
     private final LayoutInflater mInflater;
     private final H mHandler = new H();
     private final ZenPrefs mPrefs;
-    private final IconPulser mIconPulser;
     private final TransitionHelper mTransitionHelper = new TransitionHelper();
     private final Uri mForeverId;
     private final SpTexts mSpTexts;
@@ -104,9 +107,6 @@
     private Callback mCallback;
     private ZenModeController mController;
     private boolean mCountdownConditionSupported;
-    private int mMaxConditions;
-    private int mMaxOptionalConditions;
-    private int mFirstConditionIndex;
     private boolean mRequestingConditions;
     private Condition mExitCondition;
     private int mBucketIndex = -1;
@@ -125,7 +125,6 @@
         mContext = context;
         mPrefs = new ZenPrefs();
         mInflater = LayoutInflater.from(mContext.getApplicationContext());
-        mIconPulser = new IconPulser(mContext);
         mForeverId = Condition.newId(mContext).appendPath("forever").build();
         mSpTexts = new SpTexts(mContext);
         mVoiceCapable = Util.isVoiceCapable(mContext);
@@ -135,7 +134,6 @@
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("ZenModePanel state:");
         pw.print("  mCountdownConditionSupported="); pw.println(mCountdownConditionSupported);
-        pw.print("  mMaxConditions="); pw.println(mMaxConditions);
         pw.print("  mRequestingConditions="); pw.println(mRequestingConditions);
         pw.print("  mAttached="); pw.println(mAttached);
         pw.print("  mHidden="); pw.println(mHidden);
@@ -286,14 +284,6 @@
         if (mRequestingConditions == requesting) return;
         if (DEBUG) Log.d(mTag, "setRequestingConditions " + requesting);
         mRequestingConditions = requesting;
-        if (mController != null) {
-            AsyncTask.execute(new Runnable() {
-                @Override
-                public void run() {
-                    mController.requestConditions(requesting);
-                }
-            });
-        }
         if (mRequestingConditions) {
             mTimeCondition = parseExistingTimeCondition(mContext, mExitCondition);
             if (mTimeCondition != null) {
@@ -304,6 +294,7 @@
                         MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
             }
             if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex);
+
             mConditions = null; // reset conditions
             handleUpdateConditions();
         } else {
@@ -314,13 +305,9 @@
     public void init(ZenModeController controller) {
         mController = controller;
         mCountdownConditionSupported = mController.isCountdownConditionSupported();
-        final int countdownDelta = mCountdownConditionSupported ? 1 : 0;
-        mFirstConditionIndex = COUNTDOWN_CONDITION_INDEX + countdownDelta;
+        final int countdownDelta = mCountdownConditionSupported ? COUNTDOWN_CONDITION_COUNT : 0;
         final int minConditions = 1 /*forever*/ + countdownDelta;
-        mMaxConditions = MathUtils.constrain(mContext.getResources()
-                .getInteger(R.integer.zen_mode_max_conditions), minConditions, 100);
-        mMaxOptionalConditions = mMaxConditions - minConditions;
-        for (int i = 0; i < mMaxConditions; i++) {
+        for (int i = 0; i < minConditions; i++) {
             mZenConditions.addView(mInflater.inflate(R.layout.zen_mode_condition, this, false));
         }
         mSessionZen = getSelectedZen(-1);
@@ -357,28 +344,10 @@
         return condition == null ? null : condition.copy();
     }
 
-    public static String getExitConditionText(Context context, Condition exitCondition) {
-        if (exitCondition == null) {
-            return foreverSummary(context);
-        } else if (isCountdown(exitCondition)) {
-            final Condition condition = parseExistingTimeCondition(context, exitCondition);
-            return condition != null ? condition.summary : foreverSummary(context);
-        } else {
-            return exitCondition.summary;
-        }
-    }
-
     public void setCallback(Callback callback) {
         mCallback = callback;
     }
 
-    public void showSilentHint() {
-        if (DEBUG) Log.d(mTag, "showSilentHint");
-        if (mZenButtons == null || mZenButtons.getChildCount() == 0) return;
-        final View noneButton = mZenButtons.getChildAt(0);
-        mIconPulser.start(noneButton);
-    }
-
     private void handleUpdateManualRule(ZenRule rule) {
         final int zen = rule != null ? rule.zenMode : Global.ZEN_MODE_OFF;
         handleUpdateZen(zen);
@@ -410,7 +379,7 @@
             final ConditionTag tag = getConditionTagAt(i);
             if (tag != null) {
                 if (sameConditionId(tag.condition, mExitCondition)) {
-                    bind(exitCondition, mZenConditions.getChildAt(i));
+                    bind(exitCondition, mZenConditions.getChildAt(i), i);
                 }
             }
         }
@@ -495,64 +464,32 @@
         final long span = time - now;
         if (span <= 0 || span > MAX_BUCKET_MINUTES * MINUTES_MS) return null;
         return ZenModeConfig.toTimeCondition(context,
-                time, Math.round(span / (float) MINUTES_MS), now, ActivityManager.getCurrentUser(),
+                time, Math.round(span / (float) MINUTES_MS), ActivityManager.getCurrentUser(),
                 false /*shortVersion*/);
     }
 
-    private void handleUpdateConditions(Condition[] conditions) {
-        conditions = trimConditions(conditions);
-        if (Arrays.equals(conditions, mConditions)) {
-            final int count = mConditions == null ? 0 : mConditions.length;
-            if (DEBUG) Log.d(mTag, "handleUpdateConditions unchanged conditionCount=" + count);
-            return;
-        }
-        mConditions = conditions;
-        handleUpdateConditions();
-    }
-
-    private Condition[] trimConditions(Condition[] conditions) {
-        if (conditions == null || conditions.length <= mMaxOptionalConditions) {
-            // no need to trim
-            return conditions;
-        }
-        // look for current exit condition, ensure it is included if found
-        int found = -1;
-        for (int i = 0; i < conditions.length; i++) {
-            final Condition c = conditions[i];
-            if (mSessionExitCondition != null && sameConditionId(mSessionExitCondition, c)) {
-                found = i;
-                break;
-            }
-        }
-        final Condition[] rt = Arrays.copyOf(conditions, mMaxOptionalConditions);
-        if (found >= mMaxOptionalConditions) {
-            // found after the first N, promote to the end of the first N
-            rt[mMaxOptionalConditions - 1] = conditions[found];
-        }
-        return rt;
-    }
-
     private void handleUpdateConditions() {
         if (mTransitionHelper.isTransitioning()) {
-            mTransitionHelper.pendingUpdateConditions();
             return;
         }
         final int conditionCount = mConditions == null ? 0 : mConditions.length;
         if (DEBUG) Log.d(mTag, "handleUpdateConditions conditionCount=" + conditionCount);
         // forever
-        bind(forever(), mZenConditions.getChildAt(FOREVER_CONDITION_INDEX));
+        bind(forever(), mZenConditions.getChildAt(FOREVER_CONDITION_INDEX),
+                FOREVER_CONDITION_INDEX);
         // countdown
         if (mCountdownConditionSupported && mTimeCondition != null) {
-            bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX));
+            bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX),
+                    COUNTDOWN_CONDITION_INDEX);
         }
-        // provider conditions
-        for (int i = 0; i < conditionCount; i++) {
-            bind(mConditions[i], mZenConditions.getChildAt(mFirstConditionIndex + i));
-        }
-        // hide the rest
-        for (int i = mZenConditions.getChildCount() - 1; i > mFirstConditionIndex + conditionCount;
-                i--) {
-            mZenConditions.getChildAt(i).setVisibility(GONE);
+        // countdown until alarm
+        if (mCountdownConditionSupported) {
+            Condition nextAlarmCondition = getTimeUntilNextAlarmCondition();
+            if (nextAlarmCondition != null) {
+                bind(nextAlarmCondition,
+                        mZenConditions.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX),
+                        COUNTDOWN_ALARM_CONDITION_INDEX);
+            }
         }
         // ensure something is selected
         if (mExpanded && isShown()) {
@@ -569,6 +506,33 @@
         return context.getString(com.android.internal.R.string.zen_mode_forever);
     }
 
+    // Returns a time condition if the next alarm is within the next week.
+    private Condition getTimeUntilNextAlarmCondition() {
+        GregorianCalendar weekRange = new GregorianCalendar();
+        final long now = weekRange.getTimeInMillis();
+        setToMidnight(weekRange);
+        weekRange.roll(Calendar.DATE, 6);
+        final long nextAlarmMs = mController.getNextAlarm();
+        if (nextAlarmMs > 0) {
+            GregorianCalendar nextAlarm = new GregorianCalendar();
+            nextAlarm.setTimeInMillis(nextAlarmMs);
+            setToMidnight(nextAlarm);
+
+            if (weekRange.compareTo(nextAlarm) >= 0) {
+                return ZenModeConfig.toNextAlarmCondition(mContext, now, nextAlarmMs,
+                        ActivityManager.getCurrentUser());
+            }
+        }
+        return null;
+    }
+
+    private void setToMidnight(Calendar calendar) {
+        calendar.set(Calendar.HOUR_OF_DAY, 0);
+        calendar.set(Calendar.MINUTE, 0);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+    }
+
     private ConditionTag getConditionTagAt(int index) {
         return (ConditionTag) mZenConditions.getChildAt(index).getTag();
     }
@@ -610,7 +574,8 @@
             mTimeCondition = ZenModeConfig.toTimeCondition(mContext,
                     MINUTE_BUCKETS[favoriteIndex], ActivityManager.getCurrentUser());
             mBucketIndex = favoriteIndex;
-            bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX));
+            bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX),
+                    COUNTDOWN_CONDITION_INDEX);
             getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
         }
     }
@@ -623,7 +588,7 @@
         return c != null && mForeverId.equals(c.id);
     }
 
-    private void bind(final Condition condition, final View row) {
+    private void bind(final Condition condition, final View row, final int rowId) {
         if (condition == null) throw new IllegalArgumentException("condition must not be null");
         final boolean enabled = condition.state == Condition.STATE_TRUE;
         final ConditionTag tag =
@@ -640,8 +605,7 @@
         tag.rb.setEnabled(enabled);
         final boolean checked = (mSessionExitCondition != null
                     || mAttachedZen != Global.ZEN_MODE_OFF)
-                && (sameConditionId(mSessionExitCondition, tag.condition)
-                    || isCountdown(mSessionExitCondition) && isCountdown(tag.condition));
+                && (sameConditionId(mSessionExitCondition, tag.condition));
         if (checked != tag.rb.isChecked()) {
             if (DEBUG) Log.d(mTag, "bind checked=" + checked + " condition=" + conditionId);
             tag.rb.setChecked(checked);
@@ -692,7 +656,7 @@
         button1.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
-                onClickTimeButton(row, tag, false /*down*/);
+                onClickTimeButton(row, tag, false /*down*/, rowId);
             }
         });
 
@@ -700,7 +664,7 @@
         button2.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
-                onClickTimeButton(row, tag, true /*up*/);
+                onClickTimeButton(row, tag, true /*up*/, rowId);
             }
         });
         tag.lines.setOnClickListener(new OnClickListener() {
@@ -711,7 +675,7 @@
         });
 
         final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
-        if (time > 0) {
+        if (rowId != COUNTDOWN_ALARM_CONDITION_INDEX && time > 0) {
             button1.setVisibility(VISIBLE);
             button2.setVisibility(VISIBLE);
             if (mBucketIndex > -1) {
@@ -761,7 +725,7 @@
                 tag.line1.getText()));
     }
 
-    private void onClickTimeButton(View row, ConditionTag tag, boolean up) {
+    private void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) {
         MetricsLogger.action(mContext, MetricsLogger.QS_DND_TIME, up);
         Condition newCondition = null;
         final int N = MINUTE_BUCKETS.length;
@@ -777,7 +741,7 @@
                 if (up && bucketTime > time || !up && bucketTime < time) {
                     mBucketIndex = j;
                     newCondition = ZenModeConfig.toTimeCondition(mContext,
-                            bucketTime, bucketMinutes, now, ActivityManager.getCurrentUser(),
+                            bucketTime, bucketMinutes, ActivityManager.getCurrentUser(),
                             false /*shortVersion*/);
                     break;
                 }
@@ -794,7 +758,7 @@
                     MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
         }
         mTimeCondition = newCondition;
-        bind(mTimeCondition, row);
+        bind(mTimeCondition, row, rowId);
         tag.rb.setChecked(true);
         select(mTimeCondition);
         announceConditionSelection(tag);
@@ -838,18 +802,12 @@
 
     private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
         @Override
-        public void onConditionsChanged(Condition[] conditions) {
-            mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
-        }
-
-        @Override
         public void onManualRuleChanged(ZenRule rule) {
             mHandler.obtainMessage(H.MANUAL_RULE_CHANGED, rule).sendToTarget();
         }
     };
 
     private final class H extends Handler {
-        private static final int UPDATE_CONDITIONS = 1;
         private static final int MANUAL_RULE_CHANGED = 2;
         private static final int UPDATE_WIDGETS = 3;
 
@@ -860,7 +818,6 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case UPDATE_CONDITIONS: handleUpdateConditions((Condition[]) msg.obj); break;
                 case MANUAL_RULE_CHANGED: handleUpdateManualRule((ZenRule) msg.obj); break;
                 case UPDATE_WIDGETS: updateWidgets(); break;
             }
@@ -1005,26 +962,20 @@
         private final ArraySet<View> mTransitioningViews = new ArraySet<View>();
 
         private boolean mTransitioning;
-        private boolean mPendingUpdateConditions;
         private boolean mPendingUpdateWidgets;
 
         public void clear() {
             mTransitioningViews.clear();
-            mPendingUpdateConditions = mPendingUpdateWidgets = false;
+            mPendingUpdateWidgets = false;
         }
 
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             pw.println("  TransitionHelper state:");
-            pw.print("    mPendingUpdateConditions="); pw.println(mPendingUpdateConditions);
             pw.print("    mPendingUpdateWidgets="); pw.println(mPendingUpdateWidgets);
             pw.print("    mTransitioning="); pw.println(mTransitioning);
             pw.print("    mTransitioningViews="); pw.println(mTransitioningViews);
         }
 
-        public void pendingUpdateConditions() {
-            mPendingUpdateConditions = true;
-        }
-
         public void pendingUpdateWidgets() {
             mPendingUpdateWidgets = true;
         }
@@ -1050,15 +1001,11 @@
         @Override
         public void run() {
             if (DEBUG) Log.d(mTag, "TransitionHelper run"
-                    + " mPendingUpdateWidgets=" + mPendingUpdateWidgets
-                    + " mPendingUpdateConditions=" + mPendingUpdateConditions);
+                    + " mPendingUpdateWidgets=" + mPendingUpdateWidgets);
             if (mPendingUpdateWidgets) {
                 updateWidgets();
             }
-            if (mPendingUpdateConditions) {
-                handleUpdateConditions();
-            }
-            mPendingUpdateWidgets = mPendingUpdateConditions = false;
+            mPendingUpdateWidgets = false;
         }
 
         private void updateTransitioning() {
@@ -1067,10 +1014,10 @@
             mTransitioning = transitioning;
             if (DEBUG) Log.d(mTag, "TransitionHelper mTransitioning=" + mTransitioning);
             if (!mTransitioning) {
-                if (mPendingUpdateConditions || mPendingUpdateWidgets) {
+                if (mPendingUpdateWidgets) {
                     mHandler.post(this);
                 } else {
-                    mPendingUpdateConditions = mPendingUpdateWidgets = false;
+                    mPendingUpdateWidgets = false;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 882899e..08f0952 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -971,8 +971,8 @@
             // This is a special alarm that will put the system into idle until it goes off.
             // The caller has given the time they want this to happen at, however we need
             // to pull that earlier if there are existing alarms that have requested to
-            // bring us out of idle.
-            if (mNextWakeFromIdle != null) {
+            // bring us out of idle at an earlier time.
+            if (mNextWakeFromIdle != null && a.whenElapsed > mNextWakeFromIdle.whenElapsed) {
                 a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed;
             }
             // Add fuzz to make the alarm go off some time before the actual desired time.
@@ -1256,7 +1256,7 @@
                 pw.print("      Idling until: ");
                 if (mPendingIdleUntil != null) {
                     pw.println(mPendingIdleUntil);
-                    mPendingIdleUntil.dump(pw, "    ", nowRTC, nowELAPSED, sdf);
+                    mPendingIdleUntil.dump(pw, "        ", nowRTC, nowELAPSED, sdf);
                 } else {
                     pw.println("null");
                 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6190a5a..327fb8a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -946,13 +946,13 @@
             uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
         }
 
-        if ((uidRules & RULE_REJECT_ALL) != 0
-                || (networkCostly && (uidRules & RULE_REJECT_METERED) != 0)) {
+        if (uidRules == RULE_REJECT_ALL) {
             return true;
+        } else if ((uidRules == RULE_REJECT_METERED) && networkCostly) {
+            return true;
+        } else {
+            return false;
         }
-
-        // no restrictive rules; network is visible
-        return false;
     }
 
     /**
@@ -1447,10 +1447,7 @@
     }
 
     private void enforceChangePermission() {
-        int uid = Binder.getCallingUid();
-        Settings.checkAndNoteChangeNetworkStateOperation(mContext, uid, Settings
-                .getPackageNameForUid(mContext, uid), true);
-
+        ConnectivityManager.enforceChangePermission(mContext);
     }
 
     private void enforceTetherAccessPermission() {
@@ -3212,6 +3209,11 @@
             final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN));
             final VpnProfile profile = VpnProfile.decode(
                     profileName, mKeyStore.get(Credentials.VPN + profileName));
+            if (profile == null) {
+                Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName);
+                setLockdownTracker(null);
+                return true;
+            }
             int user = UserHandle.getUserId(Binder.getCallingUid());
             synchronized(mVpns) {
                 setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpns.get(user),
@@ -3358,7 +3360,7 @@
                     .setPriority(highPriority ?
                             Notification.PRIORITY_HIGH :
                             Notification.PRIORITY_DEFAULT)
-                    .setDefaults(Notification.DEFAULT_ALL)
+                    .setDefaults(highPriority ? Notification.DEFAULT_ALL : 0)
                     .setOnlyAlertOnce(true)
                     .build();
 
@@ -3724,7 +3726,7 @@
             synchronized(mRulesLock) {
                 uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
             }
-            if ((uidRules & (RULE_REJECT_METERED | RULE_REJECT_ALL)) != 0) {
+            if (uidRules != RULE_ALLOW_ALL) {
                 // we could silently fail or we can filter the available nets to only give
                 // them those they have access to.  Chose the more useful
                 networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 5239ede..a057ac9 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -244,6 +244,14 @@
             if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
                 int plugged = intent.getIntExtra("plugged", 0);
                 updateChargingLocked(plugged != 0);
+            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                    Uri data = intent.getData();
+                    String ssp;
+                    if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
+                        removePowerSaveWhitelistAppInternal(ssp);
+                    }
+                }
             } else if (ACTION_STEP_IDLE_STATE.equals(intent.getAction())) {
                 synchronized (DeviceIdleController.this) {
                     stepIdleStateLocked();
@@ -979,6 +987,10 @@
                 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
                 filter.addAction(ACTION_STEP_IDLE_STATE);
                 getContext().registerReceiver(mReceiver, filter);
+                filter = new IntentFilter();
+                filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+                filter.addDataScheme("package");
+                getContext().registerReceiver(mReceiver, filter);
 
                 mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
 
@@ -991,7 +1003,10 @@
     public boolean addPowerSaveWhitelistAppInternal(String name) {
         synchronized (this) {
             try {
-                ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name, 0);
+                ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name,
+                        PackageManager.GET_UNINSTALLED_PACKAGES
+                                | PackageManager.GET_DISABLED_COMPONENTS
+                                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
                 if (mPowerSaveWhitelistUserApps.put(name, UserHandle.getAppId(ai.uid)) == null) {
                     reportPowerSaveWhitelistChangedLocked();
                     updateWhitelistAppIdsLocked();
@@ -1332,7 +1347,6 @@
                 if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
                 EventLogTags.writeDeviceIdle(mState, "step");
                 scheduleSensingAlarmLocked(mConstants.SENSING_TIMEOUT);
-                cancelSensingAlarmLocked();
                 cancelLocatingLocked();
                 mAnyMotionDetector.checkForAnyMotion();
                 mNotMoving = false;
@@ -1344,7 +1358,6 @@
                 mState = STATE_LOCATING;
                 if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING.");
                 EventLogTags.writeDeviceIdle(mState, "step");
-                cancelSensingAlarmLocked();
                 scheduleSensingAlarmLocked(mConstants.LOCATING_TIMEOUT);
                 if (mLocationManager != null
                         && mLocationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
@@ -1414,6 +1427,7 @@
             mState = STATE_ACTIVE;
             mInactiveTimeout = timeout;
             EventLogTags.writeDeviceIdle(mState, type);
+            cancelSensingAlarmLocked();
             becomeInactiveIfAppropriateLocked();
         }
     }
@@ -1595,7 +1609,6 @@
             } catch (IOException e) {
             }
         }
-
     }
 
     private void readConfigFileLocked(XmlPullParser parser) {
@@ -1624,7 +1637,10 @@
                     String name = parser.getAttributeValue(null, "n");
                     if (name != null) {
                         try {
-                            ApplicationInfo ai = pm.getApplicationInfo(name, 0);
+                            ApplicationInfo ai = pm.getApplicationInfo(name,
+                                    PackageManager.GET_UNINSTALLED_PACKAGES
+                                            | PackageManager.GET_DISABLED_COMPONENTS
+                                            | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
                             mPowerSaveWhitelistUserApps.put(ai.packageName,
                                     UserHandle.getAppId(ai.uid));
                         } catch (PackageManager.NameNotFoundException e) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 4e11070..6d07a57 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -40,6 +40,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import android.annotation.Nullable;
 import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
 import android.app.AlertDialog;
@@ -286,8 +287,19 @@
     boolean mSystemReady;
 
     /**
-     * Id of the currently selected input method.
+     * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method.
+     * method.  This is to be synchronized with the secure settings keyed with
+     * {@link Settings.Secure#DEFAULT_INPUT_METHOD}.
+     *
+     * <p>This can be transiently {@code null} when the system is re-initializing input method
+     * settings, e.g., the system locale is just changed.</p>
+     *
+     * <p>Note that {@link #mCurId} is used to track which IME is being connected to
+     * {@link InputMethodManagerService}.</p>
+     *
+     * @see #mCurId
      */
+    @Nullable
     String mCurMethodId;
 
     /**
@@ -317,9 +329,14 @@
     EditorInfo mCurAttribute;
 
     /**
-     * The input method ID of the input method service that we are currently
+     * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently
      * connected to or in the process of connecting to.
+     *
+     * <p>This can be {@code null} when no input method is connected.</p>
+     *
+     * @see #mCurMethodId
      */
+    @Nullable
     String mCurId;
 
     /**
@@ -967,7 +984,6 @@
                 || (newLocale != null && !newLocale.equals(mLastSystemLocale))) {
             if (!updateOnlyWhenLocaleChanged) {
                 hideCurrentInputLocked(0, null);
-                mCurMethodId = null;
                 unbindCurrentMethodLocked(true, false);
             }
             if (DEBUG) {
@@ -1523,7 +1539,11 @@
         channel.dispose();
     }
 
-    void unbindCurrentMethodLocked(boolean reportToClient, boolean savePosition) {
+    void unbindCurrentMethodLocked(boolean resetCurrentMethodAndClient, boolean savePosition) {
+        if (resetCurrentMethodAndClient) {
+            mCurMethodId = null;
+        }
+
         if (mVisibleBound) {
             mContext.unbindService(mVisibleConnection);
             mVisibleBound = false;
@@ -1550,9 +1570,8 @@
         mCurId = null;
         clearCurMethodLocked();
 
-        if (reportToClient && mCurClient != null) {
-            executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
-                    MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
+        if (resetCurrentMethodAndClient) {
+            unbindCurrentClientLocked();
         }
     }
 
@@ -1903,13 +1922,11 @@
                 setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
             } catch (IllegalArgumentException e) {
                 Slog.w(TAG, "Unknown input method from prefs: " + id, e);
-                mCurMethodId = null;
                 unbindCurrentMethodLocked(true, false);
             }
             mShortcutInputMethodsAndSubtypes.clear();
         } else {
             // There is no longer an input method set, so stop any current one.
-            mCurMethodId = null;
             unbindCurrentMethodLocked(true, false);
         }
         // Here is not the perfect place to reset the switching controller. Ideally
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 4e721da..5e67414 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2415,8 +2415,13 @@
         }
 
         try {
-            mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
-                               new SensitiveArg(password));
+            if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
+                mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
+                                CRYPTO_TYPES[type]);
+            } else {
+                mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
+                                CRYPTO_TYPES[type], new SensitiveArg(password));
+            }
         } catch (NativeDaemonConnectorException e) {
             // Encryption failed
             return e.getCode();
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 433f707..ba9279c 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2023,9 +2023,9 @@
     public void setFirewallChainEnabled(int chain, boolean enable) {
         enforceSystemUid();
         synchronized (mQuotaLock) {
-            if (mFirewallChainStates.indexOfKey(chain) >= 0 &&
-                    mFirewallChainStates.get(chain) == enable) {
-                // All is the same, nothing to do.
+            if (mFirewallChainStates.get(chain, false) == enable) {
+                // All is the same, nothing to do.  This relies on the fact that netd has child
+                // chains default detached.
                 return;
             }
             mFirewallChainStates.put(chain, enable);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b87e109..970f1b5 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1314,6 +1314,15 @@
         if (!mRestartingServices.contains(r)) {
             return;
         }
+        if (!isServiceNeeded(r, false, false)) {
+            // Paranoia: is this service actually needed?  In theory a service that is not
+            // needed should never remain on the restart list.  In practice...  well, there
+            // have been bugs where this happens, and bad things happen because the process
+            // ends up just being cached, so quickly killed, then restarted again and again.
+            // Let's not let that happen.
+            Slog.wtf(TAG, "Restarting service that is not needed: " + r);
+            return;
+        }
         try {
             bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true);
         } catch (TransactionTooLargeException e) {
@@ -2043,6 +2052,13 @@
                             mAm.mProcessStats);
                     realStartServiceLocked(sr, proc, sr.createdFromFg);
                     didSomething = true;
+                    if (!isServiceNeeded(sr, false, false)) {
+                        // We were waiting for this service to start, but it is actually no
+                        // longer needed.  This could happen because bringDownServiceIfNeeded
+                        // won't bring down a service that is pending...  so now the pending
+                        // is done, so let's drop it.
+                        bringDownServiceLocked(sr);
+                    }
                 }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Exception in new application when starting service "
@@ -2055,7 +2071,7 @@
         // be weird to bring up the process but arbitrarily not let the services
         // run at this point just because their restart time hasn't come up.
         if (mRestartingServices.size() > 0) {
-            ServiceRecord sr = null;
+            ServiceRecord sr;
             for (int i=0; i<mRestartingServices.size(); i++) {
                 sr = mRestartingServices.get(i);
                 if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 1195e83..70c4821 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2468,8 +2468,13 @@
     final void doPendingActivityLaunchesLocked(boolean doResume) {
         while (!mPendingActivityLaunches.isEmpty()) {
             PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
-            startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
-                    doResume && mPendingActivityLaunches.isEmpty(), null, null);
+
+            try {
+                startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
+                                             doResume && mPendingActivityLaunches.isEmpty(), null, null);
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception during pending activity launch pal=" + pal, e);
+            }
         }
     }
 
@@ -3920,7 +3925,7 @@
         mLockTaskModeTasks.add(task);
 
         if (task.mLockTaskUid == -1) {
-            task.mLockTaskUid = task.mCallingUid;
+            task.mLockTaskUid = task.effectiveUid;
         }
 
         if (andResume) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 960cbf1..6de8579 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -44,6 +44,7 @@
 import android.os.UserHandle;
 import android.util.EventLog;
 import android.util.Slog;
+import android.util.TimeUtils;
 import com.android.server.DeviceIdleController;
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -1284,6 +1285,7 @@
 
     final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
                 || mPendingBroadcast != null) {
             boolean printed = false;
@@ -1301,7 +1303,7 @@
                     pw.println("  Active broadcasts [" + mQueueName + "]:");
                 }
                 pw.println("  Active Broadcast " + mQueueName + " #" + i + ":");
-                br.dump(pw, "    ");
+                br.dump(pw, "    ", sdf);
             }
             printed = false;
             needSep = true;
@@ -1319,7 +1321,7 @@
                     pw.println("  Active ordered broadcasts [" + mQueueName + "]:");
                 }
                 pw.println("  Active Ordered Broadcast " + mQueueName + " #" + i + ":");
-                mOrderedBroadcasts.get(i).dump(pw, "    ");
+                mOrderedBroadcasts.get(i).dump(pw, "    ", sdf);
             }
             if (dumpPackage == null || (mPendingBroadcast != null
                     && dumpPackage.equals(mPendingBroadcast.callerPackage))) {
@@ -1328,7 +1330,7 @@
                 }
                 pw.println("  Pending broadcast [" + mQueueName + "]:");
                 if (mPendingBroadcast != null) {
-                    mPendingBroadcast.dump(pw, "    ");
+                    mPendingBroadcast.dump(pw, "    ", sdf);
                 } else {
                     pw.println("    (null)");
                 }
@@ -1366,7 +1368,7 @@
             if (dumpAll) {
                 pw.print("  Historical Broadcast " + mQueueName + " #");
                         pw.print(i); pw.println(":");
-                r.dump(pw, "    ");
+                r.dump(pw, "    ", sdf);
             } else {
                 pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
                 pw.print("    ");
@@ -1400,7 +1402,6 @@
             }
             // done skipping; dump the remainder of the ring. 'i' is still the ordinal within
             // the overall broadcast history.
-            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
             do {
                 ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
                 Intent intent = mBroadcastSummaryHistory[ringIndex];
@@ -1422,9 +1423,19 @@
                 i++;
                 pw.print("  #"); pw.print(i); pw.print(": ");
                 pw.println(intent.toShortString(false, true, true, false));
-                pw.print("    enq="); pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
-                pw.print(" disp="); pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
-                pw.print(" fin="); pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
+                pw.print("    ");
+                TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex]
+                        - mSummaryHistoryEnqueueTime[ringIndex], pw);
+                pw.print(" dispatch ");
+                TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex]
+                        - mSummaryHistoryDispatchTime[ringIndex], pw);
+                pw.println(" finish");
+                pw.print("    enq=");
+                pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
+                pw.print(" disp=");
+                pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
+                pw.print(" fin=");
+                pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
                 Bundle bundle = intent.getExtras();
                 if (bundle != null) {
                     pw.print("    extras: "); pw.println(bundle.toString());
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 1fbfd9f..b42bcff 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -32,6 +32,7 @@
 import android.util.TimeUtils;
 
 import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
@@ -88,7 +89,7 @@
     ComponentName curComponent; // the receiver class that is currently running.
     ActivityInfo curReceiver;   // info about the receiver that is currently running.
 
-    void dump(PrintWriter pw, String prefix) {
+    void dump(PrintWriter pw, String prefix, SimpleDateFormat sdf) {
         final long now = SystemClock.uptimeMillis();
 
         pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId);
@@ -114,13 +115,19 @@
             pw.print(prefix); pw.print("options="); pw.println(options.toBundle());
         }
         pw.print(prefix); pw.print("enqueueClockTime=");
-                pw.print(new Date(enqueueClockTime));
+                pw.print(sdf.format(new Date(enqueueClockTime)));
                 pw.print(" dispatchClockTime=");
-                pw.println(new Date(dispatchClockTime));
+                pw.println(sdf.format(new Date(dispatchClockTime)));
         pw.print(prefix); pw.print("dispatchTime=");
                 TimeUtils.formatDuration(dispatchTime, now, pw);
+                pw.print(" (");
+                TimeUtils.formatDuration(dispatchClockTime-enqueueClockTime, pw);
+                pw.print(" since enq)");
         if (finishTime != 0) {
             pw.print(" finishTime="); TimeUtils.formatDuration(finishTime, now, pw);
+            pw.print(" (");
+            TimeUtils.formatDuration(finishTime-dispatchTime, pw);
+            pw.print(" since disp)");
         } else {
             pw.print(" receiverTime="); TimeUtils.formatDuration(receiverTime, now, pw);
         }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 334bc18..b61f90e 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -104,6 +104,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Random;
 import java.util.Set;
 
@@ -3370,7 +3371,7 @@
             if (!smaller.containsKey(key)) {
                 return false;
             }
-            if (!bigger.get(key).equals(smaller.get(key))) {
+            if (!Objects.equals(bigger.get(key), smaller.get(key))) {
                 return false;
             }
         }
@@ -3378,7 +3379,6 @@
     }
 
     /**
-     * TODO: Get rid of this when we separate sync settings extras from dev specified extras.
      * @return true if the provided key is used by the SyncManager in scheduling the sync.
      */
     private static boolean isSyncSetting(String key) {
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index bb4dbc3..835ba17 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -74,6 +74,7 @@
 
     // Set to true when the animation context has been fully prepared.
     private boolean mPrepared;
+    private boolean mCreatedResources;
     private int mMode;
 
     private final DisplayManagerInternal mDisplayManagerInternal;
@@ -169,6 +170,7 @@
         }
 
         // Done.
+        mCreatedResources = true;
         mPrepared = true;
 
         // Dejanking optimization.
@@ -313,6 +315,34 @@
     }
 
     /**
+     * Dismisses the color fade animation resources.
+     *
+     * This function destroys the resources that are created for the color fade
+     * animation but does not clean up the surface.
+     */
+    public void dismissResources() {
+        if (DEBUG) {
+            Slog.d(TAG, "dismissResources");
+        }
+
+        if (mCreatedResources) {
+            attachEglContext();
+            try {
+                destroyScreenshotTexture();
+                destroyGLShaders();
+                destroyGLBuffers();
+                destroyEglSurface();
+            } finally {
+                detachEglContext();
+            }
+            // This is being called with no active context so shouldn't be
+            // needed but is safer to not change for now.
+            GLES20.glFlush();
+            mCreatedResources = false;
+        }
+    }
+
+    /**
      * Dismisses the color fade animation surface and cleans up.
      *
      * To prevent stray photons from leaking out after the color fade has been
@@ -325,17 +355,8 @@
         }
 
         if (mPrepared) {
-            attachEglContext();
-            try {
-                destroyScreenshotTexture();
-                destroyGLShaders();
-                destroyGLBuffers();
-                destroyEglSurface();
-            } finally {
-                detachEglContext();
-            }
+            dismissResources();
             destroySurface();
-            GLES20.glFlush();
             mPrepared = false;
         }
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 452378f..7b49530 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -837,6 +837,7 @@
         if (mPendingScreenOff && target != Display.STATE_OFF) {
             setScreenState(Display.STATE_OFF);
             mPendingScreenOff = false;
+            mPowerState.dismissColorFadeResources();
         }
 
         if (target == Display.STATE_ON) {
@@ -910,6 +911,7 @@
                 // A black surface is already hiding the contents of the screen.
                 setScreenState(Display.STATE_OFF);
                 mPendingScreenOff = false;
+                mPowerState.dismissColorFadeResources();
             } else if (performScreenOffTransition
                     && mPowerState.prepareColorFade(mContext,
                             mColorFadeFadesConfig ?
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 2eabd32..9862516 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -187,7 +187,7 @@
     }
 
     /**
-     * Dismisses the electron beam surface.
+     * Dismisses the color fade surface.
      */
     public void dismissColorFade() {
         mColorFade.dismiss();
@@ -195,6 +195,13 @@
         mColorFadeReady = true;
     }
 
+   /**
+     * Dismisses the color fade resources.
+     */
+    public void dismissColorFadeResources() {
+        mColorFade.dismissResources();
+    }
+
     /**
      * Sets the level of the electron beam steering current.
      *
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 5a13672..0205a20 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -792,8 +792,17 @@
     }
 
     @Override // Binder call
+    public int isInTabletMode() {
+        if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE,
+                "isInTabletMode()")) {
+            throw new SecurityException("Requires TABLET_MODE permission");
+        }
+        return getSwitchState(-1, InputDevice.SOURCE_ANY, SW_TABLET_MODE);
+    }
+
+    @Override // Binder call
     public void registerTabletModeChangedListener(ITabletModeChangedListener listener) {
-        if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE_LISTENER,
+        if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE,
                 "registerTabletModeChangedListener()")) {
             throw new SecurityException("Requires TABLET_MODE_LISTENER permission");
         }
@@ -1488,7 +1497,7 @@
                     switchMask);
         }
 
-        if ((switchMask & SW_TABLET_MODE) != 0) {
+        if ((switchMask & SW_TABLET_MODE_BIT) != 0) {
             SomeArgs args = SomeArgs.obtain();
             args.argi1 = (int) (whenNanos & 0xFFFFFFFF);
             args.argi2 = (int) (whenNanos >> 32);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index ecda36a..3c50102 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -73,7 +73,7 @@
  */
 public class JobSchedulerService extends com.android.server.SystemService
         implements StateChangedListener, JobCompletedListener {
-    static final boolean DEBUG = false;
+    public static final boolean DEBUG = false;
     /** The number of concurrent jobs we run at one time. */
     private static final int MAX_JOB_CONTEXTS_COUNT
             = ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
@@ -99,7 +99,7 @@
      * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
      * things early.
      */
-    static final int MIN_CONNECTIVITY_COUNT = 2;
+    static final int MIN_CONNECTIVITY_COUNT = 1;  // Run connectivity jobs as soon as ready.
     /**
      * Minimum # of jobs (with no particular constraints) for which the JMS will be happy running
      * some work early.
@@ -443,13 +443,16 @@
     }
 
     /**
-     * A job is rescheduled with exponential back-off if the client requests this from their
-     * execution logic.
-     * A caveat is for idle-mode jobs, for which the idle-mode constraint will usurp the
-     * timeliness of the reschedule. For an idle-mode job, no deadline is given.
+     * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
+     * specify an override deadline on a failed job (the failed job will run even though it's not
+     * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
+     * ready job with {@link JobStatus#numFailures} > 0 will be executed.
+     *
      * @param failureToReschedule Provided job status that we will reschedule.
      * @return A newly instantiated JobStatus with the same constraints as the last job except
      * with adjusted timing constraints.
+     *
+     * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH
      */
     private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
         final long elapsedNowMillis = SystemClock.elapsedRealtime();
@@ -479,8 +482,9 @@
     }
 
     /**
-     * Called after a periodic has executed so we can to re-add it. We take the last execution time
-     * of the job to be the time of completion (i.e. the time at which this function is called).
+     * Called after a periodic has executed so we can reschedule it. We take the last execution
+     * time of the job to be the time of completion (i.e. the time at which this function is
+     * called).
      * This could be inaccurate b/c the job can run for as long as
      * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
      * to underscheduling at least, rather than if we had taken the last execution time to be the
@@ -491,7 +495,12 @@
     private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
         final long elapsedNow = SystemClock.elapsedRealtime();
         // Compute how much of the period is remaining.
-        long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0);
+        long runEarly = 0L;
+
+        // If this periodic was rescheduled it won't have a deadline.
+        if (periodicToReschedule.hasDeadlineConstraint()) {
+            runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
+        }
         long newEarliestRunTimeElapsed = elapsedNow + runEarly;
         long period = periodicToReschedule.getJob().getIntervalMillis();
         long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index d26319b..5376043 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -62,13 +62,13 @@
  *
  */
 public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection {
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = JobSchedulerService.DEBUG;
     private static final String TAG = "JobServiceContext";
     /** Define the maximum # of jobs allowed to run on a service at once. */
     private static final int defaultMaxActiveJobsPerService =
             ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
     /** Amount of time a job is allowed to execute for before being considered timed-out. */
-    private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000;
+    private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000;  // 10mins.
     /** Amount of time the JobScheduler will wait for a response from an app for a message. */
     private static final long OP_TIMEOUT_MILLIS = 8 * 1000;
 
@@ -109,7 +109,13 @@
     int mVerb;
     private AtomicBoolean mCancelled = new AtomicBoolean();
 
-    /** All the information maintained about the job currently being executed. */
+    /**
+     * All the information maintained about the job currently being executed.
+     *
+     * Any reads (dereferences) not done from the handler thread must be synchronized on
+     * {@link #mLock}.
+     * Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}.
+     */
     private JobStatus mRunningJob;
     /** Binder to the client service. */
     IJobService service;
@@ -194,7 +200,8 @@
      */
     JobStatus getRunningJob() {
         synchronized (mLock) {
-            return mRunningJob;
+            return mRunningJob == null ?
+                    null : new JobStatus(mRunningJob);
         }
     }
 
@@ -255,15 +262,22 @@
      */
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
-        if (!name.equals(mRunningJob.getServiceComponent())) {
+        JobStatus runningJob;
+        synchronized (mLock) {
+            // This isn't strictly necessary b/c the JobServiceHandler is running on the main
+            // looper and at this point we can't get any binder callbacks from the client. Better
+            // safe than sorry.
+            runningJob = mRunningJob;
+        }
+        if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
             mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
             return;
         }
         this.service = IJobService.Stub.asInterface(service);
         final PowerManager pm =
                 (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mRunningJob.getTag());
-        mWakeLock.setWorkSource(new WorkSource(mRunningJob.getUid()));
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, runningJob.getTag());
+        mWakeLock.setWorkSource(new WorkSource(runningJob.getUid()));
         mWakeLock.setReferenceCounted(false);
         mWakeLock.acquire();
         mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
@@ -281,13 +295,15 @@
      * @return True if the binder calling is coming from the client we expect.
      */
     private boolean verifyCallingUid() {
-        if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) {
-            if (DEBUG) {
-                Slog.d(TAG, "Stale callback received, ignoring.");
+        synchronized (mLock) {
+            if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Stale callback received, ignoring.");
+                }
+                return false;
             }
-            return false;
+            return true;
         }
-        return true;
     }
 
     /**
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 11e7605..0004c42 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -24,6 +24,7 @@
 import android.os.PersistableBundle;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.text.format.DateUtils;
 import android.util.AtomicFile;
 import android.util.ArraySet;
 import android.util.Pair;
@@ -552,9 +553,10 @@
                 return null;
             }
 
-            Pair<Long, Long> runtimes;
+            // Tuple of (earliest runtime, latest runtime) in elapsed realtime after disk load.
+            Pair<Long, Long> elapsedRuntimes;
             try {
-                runtimes = buildExecutionTimesFromXml(parser);
+                elapsedRuntimes = buildExecutionTimesFromXml(parser);
             } catch (NumberFormatException e) {
                 if (DEBUG) {
                     Slog.d(TAG, "Error parsing execution time parameters, skipping.");
@@ -562,22 +564,45 @@
                 return null;
             }
 
+            final long elapsedNow = SystemClock.elapsedRealtime();
             if (XML_TAG_PERIODIC.equals(parser.getName())) {
                 try {
                     String val = parser.getAttributeValue(null, "period");
-                    jobBuilder.setPeriodic(Long.valueOf(val));
+                    final long periodMillis = Long.valueOf(val);
+                    jobBuilder.setPeriodic(periodMillis);
+                    // As a sanity check, cap the recreated run time to be no later than 2 periods
+                    // from now. This is the latest the periodic could be pushed out. This could
+                    // happen if the periodic ran early (at the start of its period), and then the
+                    // device rebooted.
+                    if (elapsedRuntimes.second > elapsedNow + 2 * periodMillis) {
+                        final long clampedEarlyRuntimeElapsed = elapsedNow + periodMillis;
+                        final long clampedLateRuntimeElapsed = elapsedNow + 2 * periodMillis;
+                        Slog.w(TAG,
+                                String.format("Periodic job for uid='%d' persisted run-time is" +
+                                                " too big [%s, %s]. Clamping to [%s,%s]",
+                                        uid,
+                                        DateUtils.formatElapsedTime(elapsedRuntimes.first / 1000),
+                                        DateUtils.formatElapsedTime(elapsedRuntimes.second / 1000),
+                                        DateUtils.formatElapsedTime(
+                                                clampedEarlyRuntimeElapsed / 1000),
+                                        DateUtils.formatElapsedTime(
+                                                clampedLateRuntimeElapsed / 1000))
+                        );
+                        elapsedRuntimes =
+                                Pair.create(clampedEarlyRuntimeElapsed, clampedLateRuntimeElapsed);
+                    }
                 } catch (NumberFormatException e) {
                     Slog.d(TAG, "Error reading periodic execution criteria, skipping.");
                     return null;
                 }
             } else if (XML_TAG_ONEOFF.equals(parser.getName())) {
                 try {
-                    if (runtimes.first != JobStatus.NO_EARLIEST_RUNTIME) {
-                        jobBuilder.setMinimumLatency(runtimes.first - SystemClock.elapsedRealtime());
+                    if (elapsedRuntimes.first != JobStatus.NO_EARLIEST_RUNTIME) {
+                        jobBuilder.setMinimumLatency(elapsedRuntimes.first - elapsedNow);
                     }
-                    if (runtimes.second != JobStatus.NO_LATEST_RUNTIME) {
+                    if (elapsedRuntimes.second != JobStatus.NO_LATEST_RUNTIME) {
                         jobBuilder.setOverrideDeadline(
-                                runtimes.second - SystemClock.elapsedRealtime());
+                                elapsedRuntimes.second - elapsedNow);
                     }
                 } catch (NumberFormatException e) {
                     Slog.d(TAG, "Error reading job execution criteria, skipping.");
@@ -598,7 +623,8 @@
             do {
                 eventType = parser.next();
             } while (eventType == XmlPullParser.TEXT);
-            if (!(eventType == XmlPullParser.START_TAG && XML_TAG_EXTRAS.equals(parser.getName()))) {
+            if (!(eventType == XmlPullParser.START_TAG
+                    && XML_TAG_EXTRAS.equals(parser.getName()))) {
                 if (DEBUG) {
                     Slog.d(TAG, "Error reading extras, skipping.");
                 }
@@ -609,7 +635,8 @@
             jobBuilder.setExtras(extras);
             parser.nextTag(); // Consume </extras>
 
-            return new JobStatus(jobBuilder.build(), uid, runtimes.first, runtimes.second);
+            return new JobStatus(
+                    jobBuilder.build(), uid, elapsedRuntimes.first, elapsedRuntimes.second);
         }
 
         private JobInfo.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException {
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 69c63f3..c02611f 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -82,6 +82,13 @@
         this.numFailures = numFailures;
     }
 
+    /** Copy constructor. */
+    public JobStatus(JobStatus jobStatus) {
+        this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.getNumFailures());
+        this.earliestRunTimeElapsedMillis = jobStatus.getEarliestRunTime();
+        this.latestRunTimeElapsedMillis = jobStatus.getLatestRunTimeElapsed();
+    }
+
     /** Create a newly scheduled job. */
     public JobStatus(JobInfo job, int uId) {
         this(job, uId, 0);
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index cda7c32..21c30c7 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 
+import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateChangedListener;
 
 import java.io.PrintWriter;
@@ -28,7 +29,7 @@
  * are ready to run, or whether they must be stopped.
  */
 public abstract class StateController {
-    protected static final boolean DEBUG = false;
+    protected static final boolean DEBUG = JobSchedulerService.DEBUG;
     protected Context mContext;
     protected StateChangedListener mStateChangedListener;
     protected boolean mDeviceIdleMode;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 8845d6e..7c9c018 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -39,17 +39,17 @@
 import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
+import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_BACKGROUND_BATTERY_SAVE;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import static android.net.NetworkPolicyManager.RULE_UNKNOWN;
 import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
-import static android.net.NetworkPolicyManager.dumpPolicy;
-import static android.net.NetworkPolicyManager.dumpRules;
 import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
 import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
 import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
@@ -139,6 +139,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.DebugUtils;
 import android.util.Log;
 import android.util.NtpTrustedTime;
 import android.util.Pair;
@@ -148,8 +149,6 @@
 import android.util.TrustedTime;
 import android.util.Xml;
 
-import com.android.server.DeviceIdleController;
-import com.android.server.EventLogTags;
 import libcore.io.IoUtils;
 
 import com.android.internal.R;
@@ -157,6 +156,8 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.DeviceIdleController;
+import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.google.android.collect.Lists;
 
@@ -285,6 +286,10 @@
     final SparseIntArray mUidPolicy = new SparseIntArray();
     /** Currently derived rules for each UID. */
     final SparseIntArray mUidRules = new SparseIntArray();
+
+    final SparseIntArray mUidFirewallStandbyRules = new SparseIntArray();
+    final SparseIntArray mUidFirewallDozableRules = new SparseIntArray();
+
     /** Set of states for the child firewall chains. True if the chain is active. */
     final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
 
@@ -457,14 +462,8 @@
             // read policy from disk
             readPolicyLocked();
 
-            if (mRestrictBackground || mRestrictPower || mDeviceIdleMode) {
-                updateRulesForGlobalChangeLocked(false);
-                updateNotificationsLocked();
-            } else {
-                // If we are not in any special mode, we just need to make sure the current
-                // app idle state is updated.
-                updateRulesForAppIdleLocked();
-            }
+            updateRulesForGlobalChangeLocked(false);
+            updateNotificationsLocked();
         }
 
         updateScreenOn();
@@ -1888,7 +1887,9 @@
             if (mDeviceIdleMode != enabled) {
                 mDeviceIdleMode = enabled;
                 if (mSystemReady) {
-                    updateRulesForDeviceIdleLocked();
+                    // Device idle change means we need to rebuild rules for all
+                    // known apps, so do a global refresh.
+                    updateRulesForGlobalChangeLocked(false);
                 }
                 if (enabled) {
                     EventLogTags.writeDeviceIdleOnPhase("net");
@@ -2026,7 +2027,7 @@
                 fout.print("UID=");
                 fout.print(uid);
                 fout.print(" policy=");
-                dumpPolicy(fout, policy);
+                fout.print(DebugUtils.flagsToString(NetworkPolicyManager.class, "POLICY_", policy));
                 fout.println();
             }
             fout.decreaseIndent();
@@ -2071,18 +2072,14 @@
                 fout.print("UID=");
                 fout.print(uid);
 
-                int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+                final int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
                 fout.print(" state=");
                 fout.print(state);
                 fout.print(state <= ActivityManager.PROCESS_STATE_TOP ? " (fg)" : " (bg)");
 
-                fout.print(" rules=");
-                final int rulesIndex = mUidRules.indexOfKey(uid);
-                if (rulesIndex < 0) {
-                    fout.print("UNKNOWN");
-                } else {
-                    dumpRules(fout, mUidRules.valueAt(rulesIndex));
-                }
+                final int rule = mUidRules.get(uid, RULE_UNKNOWN);
+                fout.print(" rule=");
+                fout.print(DebugUtils.valueToString(NetworkPolicyManager.class, "RULE_", rule));
 
                 fout.println();
             }
@@ -2117,7 +2114,7 @@
             updateRulesForUidStateChangeLocked(uid, oldUidState, uidState);
             if (mDeviceIdleMode && isProcStateAllowedWhileIdle(oldUidState)
                     != isProcStateAllowedWhileIdle(uidState)) {
-                updateRulesForDeviceIdleLocked();
+                updateRuleForDeviceIdleLocked(uid);
             }
         }
     }
@@ -2131,7 +2128,7 @@
                 updateRulesForUidStateChangeLocked(uid, oldUidState,
                         ActivityManager.PROCESS_STATE_CACHED_EMPTY);
                 if (mDeviceIdleMode) {
-                    updateRulesForDeviceIdleLocked();
+                    updateRuleForDeviceIdleLocked(uid);
                 }
             }
         }
@@ -2178,7 +2175,8 @@
         if (mDeviceIdleMode) {
             // sync the whitelists before enable dozable chain.  We don't care about the rules if
             // we are disabling the chain.
-            SparseIntArray uidRules = new SparseIntArray();
+            final SparseIntArray uidRules = mUidFirewallDozableRules;
+            uidRules.clear();
             final List<UserInfo> users = mUserManager.getUsers();
             for (int ui = users.size() - 1; ui >= 0; ui--) {
                 UserInfo user = users.get(ui);
@@ -2202,6 +2200,7 @@
             }
             setUidFirewallRules(FIREWALL_CHAIN_DOZABLE, uidRules);
         }
+
         enableFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, mDeviceIdleMode);
     }
 
@@ -2215,11 +2214,15 @@
                 setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_DEFAULT);
             }
         }
+
+        updateRulesForUidLocked(uid);
     }
 
     void updateRulesForAppIdleLocked() {
+        final SparseIntArray uidRules = mUidFirewallStandbyRules;
+        uidRules.clear();
+
         // Fully update the app idle firewall chain.
-        SparseIntArray uidRules = new SparseIntArray();
         final List<UserInfo> users = mUserManager.getUsers();
         for (int ui = users.size() - 1; ui >= 0; ui--) {
             UserInfo user = users.get(ui);
@@ -2230,6 +2233,7 @@
                 }
             }
         }
+
         setUidFirewallRules(FIREWALL_CHAIN_STANDBY, uidRules);
     }
 
@@ -2242,11 +2246,14 @@
         } else {
             setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
         }
+
+        updateRulesForUidLocked(uid);
     }
 
     void updateRulesForAppIdleParoleLocked() {
         boolean enableChain = !mUsageStats.isAppIdleParoleOn();
         enableFirewallChainLocked(FIREWALL_CHAIN_STANDBY, enableChain);
+        updateRulesForUidsLocked(mUidFirewallStandbyRules);
     }
 
     /**
@@ -2316,6 +2323,12 @@
         return true;
     }
 
+    void updateRulesForUidsLocked(SparseIntArray uids) {
+        for (int i = 0; i < uids.size(); i++) {
+            updateRulesForUidLocked(uids.keyAt(i));
+        }
+    }
+
     /**
      * Applies network rules to bandwidth and firewall controllers based on uid policy.
      * @param uid The uid for which to apply the latest policy
@@ -2337,8 +2350,7 @@
         final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
         final boolean uidForeground = isUidForegroundLocked(uid);
 
-        // derive active rules based on policy and active state
-
+        // Derive active rules based on policy and active state
         int appId = UserHandle.getAppId(uid);
         int uidRules = RULE_ALLOW_ALL;
         if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
@@ -2361,20 +2373,27 @@
             }
         }
 
-        final int oldRules = mUidRules.get(uid);
+        // Check dozable state, which is whitelist
+        if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE)
+                && mUidFirewallDozableRules.get(uid, FIREWALL_RULE_DEFAULT) != FIREWALL_RULE_ALLOW) {
+            uidRules = RULE_REJECT_ALL;
+        }
 
+        // Check standby state, which is blacklist
+        if (mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY)
+                && mUidFirewallStandbyRules.get(uid, FIREWALL_RULE_DEFAULT) == FIREWALL_RULE_DENY) {
+            uidRules = RULE_REJECT_ALL;
+        }
+
+        final int oldRules = mUidRules.get(uid);
         if (uidRules == RULE_ALLOW_ALL) {
             mUidRules.delete(uid);
         } else {
             mUidRules.put(uid, uidRules);
         }
 
-        // Update bandwidth rules if necessary
-        final boolean oldRejectMetered = (oldRules & RULE_REJECT_METERED) != 0;
-        final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0;
-        if (oldRejectMetered != rejectMetered) {
-            setUidNetworkRules(uid, rejectMetered);
-        }
+        final boolean rejectMetered = (uidRules == RULE_REJECT_METERED);
+        setUidNetworkRules(uid, rejectMetered);
 
         // dispatch changed rule to existing listeners
         if (oldRules != uidRules) {
@@ -2560,6 +2579,12 @@
      * Add or remove a uid to the firewall blacklist for all network ifaces.
      */
     private void setUidFirewallRule(int chain, int uid, int rule) {
+        if (chain == FIREWALL_CHAIN_DOZABLE) {
+            mUidFirewallDozableRules.put(uid, rule);
+        } else if (chain == FIREWALL_CHAIN_STANDBY) {
+            mUidFirewallStandbyRules.put(uid, rule);
+        }
+
         try {
             mNetworkManager.setFirewallUidRule(chain, uid, rule);
         } catch (IllegalStateException e) {
@@ -2573,9 +2598,9 @@
      * Add or remove a uid to the firewall blacklist for all network ifaces.
      */
     private void enableFirewallChainLocked(int chain, boolean enable) {
-        if (mFirewallChainStates.indexOfKey(chain) >= 0 &&
-                mFirewallChainStates.get(chain) == enable) {
-            // All is the same, nothing to do.
+        if (mFirewallChainStates.get(chain, false) == enable) {
+            // All is the same, nothing to do.  This relies on the fact that netd has child
+            // chains default detached.
             return;
         }
         mFirewallChainStates.put(chain, enable);
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index b504605..8abc8fc 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -571,6 +571,26 @@
                 grantRuntimePermissionsLPw(musicPackage, STORAGE_PERMISSIONS, userId);
             }
 
+            // Android Wear Home
+            if (mService.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+                Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+                homeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
+
+                PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackageLPr(
+                        homeIntent, userId);
+
+                if (wearHomePackage != null
+                        && doesPackageSupportRuntimePermissions(wearHomePackage)) {
+                    grantRuntimePermissionsLPw(wearHomePackage, CONTACTS_PERMISSIONS, false,
+                            userId);
+                    grantRuntimePermissionsLPw(wearHomePackage, PHONE_PERMISSIONS, true, userId);
+                    grantRuntimePermissionsLPw(wearHomePackage, MICROPHONE_PERMISSIONS, false,
+                            userId);
+                    grantRuntimePermissionsLPw(wearHomePackage, LOCATION_PERMISSIONS, false,
+                            userId);
+                }
+            }
+
             mService.mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
         }
     }
@@ -578,7 +598,10 @@
     private void grantDefaultPermissionsToDefaultSystemDialerAppLPr(
             PackageParser.Package dialerPackage, int userId) {
         if (doesPackageSupportRuntimePermissions(dialerPackage)) {
-            grantRuntimePermissionsLPw(dialerPackage, PHONE_PERMISSIONS, userId);
+            boolean isPhonePermFixed =
+                    mService.hasSystemFeature(PackageManager.FEATURE_WATCH);
+            grantRuntimePermissionsLPw(
+                    dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
             grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, userId);
             grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, userId);
             grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, userId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8f9b2c8..f95ccea 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2285,7 +2285,7 @@
                         + mSdkVersion + "; regranting permissions for internal storage");
                 updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
             }
-            updatePermissionsLPw(null, null, updateFlags);
+            updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
             ver.sdkVersion = mSdkVersion;
 
             // If this is the first boot or an update from pre-M, and it is a normal
@@ -4775,18 +4775,13 @@
             // First try to add the "always" resolution(s) for the current user, if any
             if (alwaysList.size() > 0) {
                 result.addAll(alwaysList);
-            // if there is an "always" for the parent user, add it.
-            } else if (xpDomainInfo != null && xpDomainInfo.bestDomainVerificationStatus
-                    == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
-                result.add(xpDomainInfo.resolveInfo);
             } else {
                 // Add all undefined apps as we want them to appear in the disambiguation dialog.
                 result.addAll(undefinedList);
+                // Maybe add one for the other profile.
                 if (xpDomainInfo != null && (
                         xpDomainInfo.bestDomainVerificationStatus
-                        == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
-                        || xpDomainInfo.bestDomainVerificationStatus
-                        == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK)) {
+                        != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)) {
                     result.add(xpDomainInfo.resolveInfo);
                 }
                 includeBrowser = true;
@@ -7554,8 +7549,8 @@
         // We would never need to extract libs for forward-locked and external packages,
         // since the container service will do it for us. We shouldn't attempt to
         // extract libs from system app when it was not updated.
-        if (pkg.isForwardLocked() || isExternal(pkg) ||
-            (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) ) {
+        if (pkg.isForwardLocked() || pkg.applicationInfo.isExternalAsec() ||
+                (isSystemApp(pkg) && !pkg.isUpdatedSystemApp())) {
             extractLibs = false;
         }
 
@@ -7832,7 +7827,7 @@
         final String codePath = pkg.codePath;
         final File codeFile = new File(codePath);
         final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
-        final boolean asecApp = info.isForwardLocked() || isExternal(info);
+        final boolean asecApp = info.isForwardLocked() || info.isExternalAsec();
 
         info.nativeLibraryRootDir = null;
         info.nativeLibraryRootRequiresIsa = false;
@@ -8230,8 +8225,14 @@
     static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
     static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
 
+    private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo,
+            int flags) {
+        final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null;
+        updatePermissionsLPw(changingPkg, pkgInfo, volumeUuid, flags);
+    }
+
     private void updatePermissionsLPw(String changingPkg,
-            PackageParser.Package pkgInfo, int flags) {
+            PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
         // Make sure there are no dangling permission trees.
         Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
         while (it.hasNext()) {
@@ -8300,14 +8301,21 @@
         if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
             for (PackageParser.Package pkg : mPackages.values()) {
                 if (pkg != pkgInfo) {
-                    grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
-                            changingPkg);
+                    // Only replace for packages on requested volume
+                    final String volumeUuid = getVolumeUuidForPackage(pkg);
+                    final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0)
+                            && Objects.equals(replaceVolumeUuid, volumeUuid);
+                    grantPermissionsLPw(pkg, replace, changingPkg);
                 }
             }
         }
 
         if (pkgInfo != null) {
-            grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
+            // Only replace for packages on requested volume
+            final String volumeUuid = getVolumeUuidForPackage(pkgInfo);
+            final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0)
+                    && Objects.equals(replaceVolumeUuid, volumeUuid);
+            grantPermissionsLPw(pkgInfo, replace, changingPkg);
         }
     }
 
@@ -8334,6 +8342,7 @@
 
         final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
 
+        boolean runtimePermissionsRevoked = false;
         int[] changedRuntimePermissionUserIds = EMPTY_INT_ARRAY;
 
         boolean changedInstallPermission = false;
@@ -8343,6 +8352,17 @@
             if (!ps.isSharedUser()) {
                 origPermissions = new PermissionsState(permissionsState);
                 permissionsState.reset();
+            } else {
+                // We need to know only about runtime permission changes since the
+                // calling code always writes the install permissions state but
+                // the runtime ones are written only if changed. The only cases of
+                // changed runtime permissions here are promotion of an install to
+                // runtime and revocation of a runtime from a shared user.
+                changedRuntimePermissionUserIds = revokeUnusedSharedUserPermissionsLPw(
+                        ps.sharedUser, UserManagerService.getInstance().getUserIds());
+                if (!ArrayUtils.isEmpty(changedRuntimePermissionUserIds)) {
+                    runtimePermissionsRevoked = true;
+                }
             }
         }
 
@@ -8558,9 +8578,11 @@
             ps.installPermissionsFixed = true;
         }
 
-        // Persist the runtime permissions state for users with changes.
+        // Persist the runtime permissions state for users with changes. If permissions
+        // were revoked because no app in the shared user declares them we have to
+        // write synchronously to avoid losing runtime permissions state.
         for (int userId : changedRuntimePermissionUserIds) {
-            mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+            mSettings.writeRuntimePermissionsForUserLPr(userId, runtimePermissionsRevoked);
         }
     }
 
@@ -12057,6 +12079,66 @@
         }
     }
 
+    private int[] revokeUnusedSharedUserPermissionsLPw(SharedUserSetting su, int[] allUserIds) {
+        // Collect all used permissions in the UID
+        ArraySet<String> usedPermissions = new ArraySet<>();
+        final int packageCount = su.packages.size();
+        for (int i = 0; i < packageCount; i++) {
+            PackageSetting ps = su.packages.valueAt(i);
+            if (ps.pkg == null) {
+                continue;
+            }
+            final int requestedPermCount = ps.pkg.requestedPermissions.size();
+            for (int j = 0; j < requestedPermCount; j++) {
+                String permission = ps.pkg.requestedPermissions.get(j);
+                BasePermission bp = mSettings.mPermissions.get(permission);
+                if (bp != null) {
+                    usedPermissions.add(permission);
+                }
+            }
+        }
+
+        PermissionsState permissionsState = su.getPermissionsState();
+        // Prune install permissions
+        List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates();
+        final int installPermCount = installPermStates.size();
+        for (int i = installPermCount - 1; i >= 0;  i--) {
+            PermissionState permissionState = installPermStates.get(i);
+            if (!usedPermissions.contains(permissionState.getName())) {
+                BasePermission bp = mSettings.mPermissions.get(permissionState.getName());
+                if (bp != null) {
+                    permissionsState.revokeInstallPermission(bp);
+                    permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+                            PackageManager.MASK_PERMISSION_FLAGS, 0);
+                }
+            }
+        }
+
+        int[] runtimePermissionChangedUserIds = EmptyArray.INT;
+
+        // Prune runtime permissions
+        for (int userId : allUserIds) {
+            List<PermissionState> runtimePermStates = permissionsState
+                    .getRuntimePermissionStates(userId);
+            final int runtimePermCount = runtimePermStates.size();
+            for (int i = runtimePermCount - 1; i >= 0; i--) {
+                PermissionState permissionState = runtimePermStates.get(i);
+                if (!usedPermissions.contains(permissionState.getName())) {
+                    BasePermission bp = mSettings.mPermissions.get(permissionState.getName());
+                    if (bp != null) {
+                        permissionsState.revokeRuntimePermission(bp, userId);
+                        permissionsState.updatePermissionFlags(bp, userId,
+                                PackageManager.MASK_PERMISSION_FLAGS, 0);
+                        runtimePermissionChangedUserIds = ArrayUtils.appendInt(
+                                runtimePermissionChangedUserIds, userId);
+                    }
+                }
+            }
+        }
+
+        return runtimePermissionChangedUserIds;
+    }
+
     private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
             String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res,
             UserHandle user) {
@@ -12554,6 +12636,18 @@
         return installFlags;
     }
 
+    private String getVolumeUuidForPackage(PackageParser.Package pkg) {
+        if (isExternal(pkg)) {
+            if (TextUtils.isEmpty(pkg.volumeUuid)) {
+                return StorageManager.UUID_PRIMARY_PHYSICAL;
+            } else {
+                return pkg.volumeUuid;
+            }
+        } else {
+            return StorageManager.UUID_PRIVATE_INTERNAL;
+        }
+    }
+
     private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) {
         if (isExternal(pkg)) {
             if (TextUtils.isEmpty(pkg.volumeUuid)) {
@@ -13576,7 +13670,7 @@
             if (ps != null) {
                 libDirRoot = ps.legacyNativeLibraryPathString;
             }
-            if (p != null && (isExternal(p) || p.isForwardLocked())) {
+            if (p != null && (p.isForwardLocked() || p.applicationInfo.isExternalAsec())) {
                 final long token = Binder.clearCallingIdentity();
                 try {
                     String secureContainerId = cidFromCodePath(p.applicationInfo.getBaseCodePath());
@@ -15430,7 +15524,7 @@
         if (isMounted) {
             if (DEBUG_SD_INSTALL)
                 Log.i(TAG, "Loading packages");
-            loadMediaPackages(processCids, uidArr);
+            loadMediaPackages(processCids, uidArr, externalStorage);
             startCleaningPackages();
             mInstallerService.onSecureContainersAvailable();
         } else {
@@ -15485,7 +15579,8 @@
      * the cid is added to list of removeCids. We currently don't delete stale
      * containers.
      */
-    private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr) {
+    private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr,
+            boolean externalStorage) {
         ArrayList<String> pkgList = new ArrayList<String>();
         Set<AsecInstallArgs> keys = processCids.keySet();
 
@@ -15557,7 +15652,10 @@
             // cases get permissions that the user didn't initially explicitly
             // allow... it would be nice to have some better way to handle
             // this situation.
-            final VersionInfo ver = mSettings.getExternalVersion();
+            final VersionInfo ver = externalStorage ? mSettings.getExternalVersion()
+                    : mSettings.getInternalVersion();
+            final String volumeUuid = externalStorage ? StorageManager.UUID_PRIMARY_PHYSICAL
+                    : StorageManager.UUID_PRIVATE_INTERNAL;
 
             int updateFlags = UPDATE_PERMISSIONS_ALL;
             if (ver.sdkVersion != mSdkVersion) {
@@ -15565,7 +15663,7 @@
                         + mSdkVersion + "; regranting permissions for external");
                 updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
             }
-            updatePermissionsLPw(null, null, updateFlags);
+            updatePermissionsLPw(null, null, volumeUuid, updateFlags);
 
             // Yay, everything is now upgraded
             ver.forceCurrent();
@@ -15698,7 +15796,7 @@
                         + mSdkVersion + "; regranting permissions for " + vol.fsUuid);
                 updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
             }
-            updatePermissionsLPw(null, null, updateFlags);
+            updatePermissionsLPw(null, null, vol.fsUuid, updateFlags);
 
             // Yay, everything is now upgraded
             ver.forceCurrent();
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 571be0e..4093e20 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -517,7 +517,18 @@
         ArrayList<String> removeStage = new ArrayList<String>();
         for (Map.Entry<String,SharedUserSetting> entry : mSharedUsers.entrySet()) {
             final SharedUserSetting sus = entry.getValue();
-            if (sus == null || sus.packages.size() == 0) {
+            if (sus == null) {
+                removeStage.add(entry.getKey());
+                continue;
+            }
+            // remove packages that are no longer installed
+            for (Iterator<PackageSetting> iter = sus.packages.iterator(); iter.hasNext();) {
+                PackageSetting ps = iter.next();
+                if (mPackages.get(ps.name) == null) {
+                    iter.remove();
+                }
+            }
+            if (sus.packages.size() == 0) {
                 removeStage.add(entry.getKey());
             }
         }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 8ad45c9..ace2505 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2995,7 +2995,7 @@
 
         // Display task switcher for ALT-TAB.
         if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) {
-            if (mRecentAppsHeldModifiers == 0 && !keyguardOn) {
+            if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) {
                 final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
                 if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) {
                     mRecentAppsHeldModifiers = shiftlessModifiers;
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 7c2da2d..be3e922 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -56,7 +56,7 @@
             int screenOffset, int maskThickness) {
         mScreenSize = new Point();
         display.getSize(mScreenSize);
-        if (mScreenSize.x != mScreenSize.y) {
+        if (mScreenSize.x != mScreenSize.y + screenOffset) {
             Slog.w(TAG, "Screen dimensions of displayId = " + display.getDisplayId() +
                     "are not equal, circularMask will not be drawn.");
             mDimensionsUnequal = true;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 726d29d..42042b94 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1064,6 +1064,8 @@
                 mAnimator.getScreenRotationAnimationLocked(displayId);
         final boolean screenAnimation =
                 screenRotationAnimation != null && screenRotationAnimation.isAnimating();
+
+        mHasClipRect = false;
         if (selfTransformation || attachedTransformation != null
                 || appTransformation != null || screenAnimation) {
             // cache often used attributes locally
@@ -1139,7 +1141,6 @@
             // transforming since it is more important to have that
             // animation be smooth.
             mShownAlpha = mAlpha;
-            mHasClipRect = false;
             if (!mService.mLimitedAlphaCompositing
                     || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
                     || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 3ecfd55..c6d5a7e 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -27,6 +27,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.XmlResourceParser;
+import android.media.midi.IBluetoothMidiService;
 import android.media.midi.IMidiDeviceListener;
 import android.media.midi.IMidiDeviceOpenCallback;
 import android.media.midi.IMidiDeviceServer;
@@ -394,7 +395,20 @@
                     mServiceConnection = new ServiceConnection() {
                         @Override
                         public void onServiceConnected(ComponentName name, IBinder service) {
-                            IMidiDeviceServer server = IMidiDeviceServer.Stub.asInterface(service);
+                            IMidiDeviceServer server = null;
+                            if (mBluetoothDevice != null) {
+                                IBluetoothMidiService mBluetoothMidiService = IBluetoothMidiService.Stub.asInterface(service);
+                                try {
+                                    // We need to explicitly add the device in a separate method
+                                    // because onBind() is only called once.
+                                    IBinder deviceBinder = mBluetoothMidiService.addBluetoothDevice(mBluetoothDevice);
+                                    server = IMidiDeviceServer.Stub.asInterface(deviceBinder);
+                                } catch(RemoteException e) {
+                                    Log.e(TAG, "Could not call addBluetoothDevice()", e);
+                                }
+                            } else {
+                                server = IMidiDeviceServer.Stub.asInterface(service);
+                            }
                             setDeviceServer(server);
                         }
 
@@ -411,7 +425,6 @@
                         intent.setComponent(new ComponentName(
                                 MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE,
                                 MidiManager.BLUETOOTH_MIDI_SERVICE_CLASS));
-                        intent.putExtra("device", mBluetoothDevice);
                     } else {
                         intent = new Intent(MidiDeviceService.SERVICE_INTERFACE);
                         intent.setComponent(
@@ -575,6 +588,8 @@
         Client client = getClient(token);
         if (client == null) return;
         client.addListener(listener);
+        // Let listener know whether any ports are already busy.
+        updateStickyDeviceStatus(client.mUid, listener);
     }
 
     @Override
@@ -584,6 +599,25 @@
         client.removeListener(listener);
     }
 
+    // Inform listener of the status of all known devices.
+    private void updateStickyDeviceStatus(int uid, IMidiDeviceListener listener) {
+        synchronized (mDevicesByInfo) {
+            for (Device device : mDevicesByInfo.values()) {
+                // ignore private devices that our client cannot access
+                if (device.isUidAllowed(uid)) {
+                    try {
+                        MidiDeviceStatus status = device.getDeviceStatus();
+                        if (status != null) {
+                            listener.onDeviceStatusChanged(status);
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "remote exception", e);
+                    }
+                }
+            }
+        }
+    }
+
     private static final MidiDeviceInfo[] EMPTY_DEVICE_INFO_ARRAY = new MidiDeviceInfo[0];
 
     public MidiDeviceInfo[] getDevices() {
diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java
index 334f708..df44b11 100644
--- a/services/net/java/android/net/dhcp/DhcpAckPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java
@@ -46,7 +46,7 @@
 
         return s + " ACK: your new IP " + mYourIp +
                 ", netmask " + mSubnetMask +
-                ", gateway " + mGateway + dnsServers +
+                ", gateways " + mGateways + dnsServers +
                 ", lease time " + mLeaseTime;
     }
 
@@ -79,7 +79,7 @@
         }
 
         addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask);
-        addTlv(buffer, DHCP_ROUTER, mGateway);
+        addTlv(buffer, DHCP_ROUTER, mGateways);
         addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName);
         addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress);
         addTlv(buffer, DHCP_DNS_SERVER, mDnsServers);
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index e0d2ac1..28cb114 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -299,6 +299,7 @@
             Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
             Os.setsockoptIfreq(mUdpSock, SOL_SOCKET, SO_BINDTODEVICE, mIfaceName);
             Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
+            Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
             Os.bind(mUdpSock, Inet4Address.ANY, DhcpPacket.DHCP_CLIENT);
             NetworkUtils.protectFromVpn(mUdpSock);
         } catch(SocketException|ErrnoException e) {
@@ -308,6 +309,16 @@
         return true;
     }
 
+    private boolean connectUdpSock(Inet4Address to) {
+        try {
+            Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER);
+            return true;
+        } catch (SocketException|ErrnoException e) {
+            Log.e(TAG, "Error connecting UDP socket", e);
+            return false;
+        }
+    }
+
     private static void closeQuietly(FileDescriptor fd) {
         try {
             IoBridge.closeAndSignalBlockedThreads(fd);
@@ -325,7 +336,7 @@
         try {
             mNMService.setInterfaceConfig(mIfaceName, ifcg);
         } catch (RemoteException|IllegalStateException e) {
-            Log.e(TAG, "Error configuring IP address : " + e);
+            Log.e(TAG, "Error configuring IP address " + address + ": ", e);
             return false;
         }
         return true;
@@ -345,21 +356,22 @@
         public void run() {
             maybeLog("Receive thread started");
             while (!stopped) {
+                int length = 0;  // Or compiler can't tell it's initialized if a parse error occurs.
                 try {
-                    int length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
+                    length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
                     DhcpPacket packet = null;
                     packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
-                    if (packet != null) {
-                        maybeLog("Received packet: " + packet);
-                        sendMessage(CMD_RECEIVED_PACKET, packet);
-                    } else if (PACKET_DBG) {
-                        Log.d(TAG,
-                                "Can't parse packet" + HexDump.dumpHexString(mPacket, 0, length));
-                    }
+                    maybeLog("Received packet: " + packet);
+                    sendMessage(CMD_RECEIVED_PACKET, packet);
                 } catch (IOException|ErrnoException e) {
                     if (!stopped) {
                         Log.e(TAG, "Read error", e);
                     }
+                } catch (DhcpPacket.ParseException e) {
+                    Log.e(TAG, "Can't parse packet: " + e.getMessage());
+                    if (PACKET_DBG) {
+                        Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
+                    }
                 }
             }
             maybeLog("Receive thread stopped");
@@ -376,8 +388,10 @@
                 maybeLog("Broadcasting " + description);
                 Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
             } else {
-                maybeLog("Unicasting " + description + " to " + to.getHostAddress());
-                Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
+                // It's safe to call getpeername here, because we only send unicast packets if we
+                // have an IP address, and we connect the UDP socket in DhcpHaveAddressState#enter.
+                maybeLog("Unicasting " + description + " to " + Os.getpeername(mUdpSock));
+                Os.write(mUdpSock, buf);
             }
         } catch(ErrnoException|IOException e) {
             Log.e(TAG, "Can't send packet: ", e);
@@ -789,6 +803,7 @@
                     transitionTo(mDhcpBoundState);
                 }
             } else if (packet instanceof DhcpNakPacket) {
+                // TODO: Wait a while before returning into INIT state.
                 Log.d(TAG, "Received NAK, returning to INIT");
                 mOffer = null;
                 transitionTo(mDhcpInitState);
@@ -806,10 +821,8 @@
         @Override
         public void enter() {
             super.enter();
-            if (setIpAddress(mDhcpLease.ipAddress)) {
-                maybeLog("Configured IP address " + mDhcpLease.ipAddress);
-            } else {
-                Log.e(TAG, "Failed to configure IP address " + mDhcpLease.ipAddress);
+            if (!setIpAddress(mDhcpLease.ipAddress) ||
+                    !connectUdpSock((mDhcpLease.serverAddress))) {
                 notifyFailure();
                 // There's likely no point in going into DhcpInitState here, we'll probably just
                 // repeat the transaction, get the same IP address as before, and fail.
diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
index 7ca7100..99154ef 100644
--- a/services/net/java/android/net/dhcp/DhcpOfferPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
@@ -48,7 +48,7 @@
         }
 
         return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask +
-                dnsServers + ", gateway " + mGateway +
+                dnsServers + ", gateways " + mGateways +
                 " lease time " + mLeaseTime + ", domain " + mDomainName;
     }
 
@@ -81,7 +81,7 @@
         }
 
         addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask);
-        addTlv(buffer, DHCP_ROUTER, mGateway);
+        addTlv(buffer, DHCP_ROUTER, mGateways);
         addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName);
         addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress);
         addTlv(buffer, DHCP_DNS_SERVER, mDnsServers);
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index cbf8fc2..8927bfa 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -114,6 +114,11 @@
     protected static final int MAX_LENGTH = 1500;
 
     /**
+     * The magic cookie that identifies this as a DHCP packet instead of BOOTP.
+     */
+    private static final int DHCP_MAGIC_COOKIE = 0x63825363;
+
+    /**
      * DHCP Optional Type: DHCP Subnet Mask
      */
     protected static final byte DHCP_SUBNET_MASK = 1;
@@ -123,7 +128,7 @@
      * DHCP Optional Type: DHCP Router
      */
     protected static final byte DHCP_ROUTER = 3;
-    protected Inet4Address mGateway;
+    protected List <Inet4Address> mGateways;
 
     /**
      * DHCP Optional Type: DHCP DNS Server
@@ -403,7 +408,7 @@
                      (HWADDR_LEN - mClientMac.length) // pad addr to 16 bytes
                      + 64     // empty server host name (64 bytes)
                      + 128);  // empty boot file name (128 bytes)
-        buf.putInt(0x63825363); // magic number
+        buf.putInt(DHCP_MAGIC_COOKIE); // magic number
         finishPacket(buf);
 
         // round up to an even number of octets
@@ -668,6 +673,20 @@
         return new String(bytes, 0, length, StandardCharsets.US_ASCII);
     }
 
+    private static boolean isPacketToOrFromClient(short udpSrcPort, short udpDstPort) {
+        return (udpSrcPort == DHCP_CLIENT) || (udpDstPort == DHCP_CLIENT);
+    }
+
+    private static boolean isPacketServerToServer(short udpSrcPort, short udpDstPort) {
+        return (udpSrcPort == DHCP_SERVER) && (udpDstPort == DHCP_SERVER);
+    }
+
+    public static class ParseException extends Exception {
+        public ParseException(String msg, Object... args) {
+            super(String.format(msg, args));
+        }
+    }
+
     /**
      * Creates a concrete DhcpPacket from the supplied ByteBuffer.  The
      * buffer may have an L2 encapsulation (which is the full EthernetII
@@ -677,7 +696,7 @@
      * A subset of the optional parameters are parsed and are stored
      * in object fields.
      */
-    public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType)
+    public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException
     {
         // bootp parameters
         int transactionId;
@@ -687,8 +706,8 @@
         Inet4Address nextIp;
         Inet4Address relayIp;
         byte[] clientMac;
-        List<Inet4Address> dnsServers = new ArrayList<Inet4Address>();
-        Inet4Address gateway = null; // aka router
+        List<Inet4Address> dnsServers = new ArrayList<>();
+        List<Inet4Address> gateways = new ArrayList<>();  // aka router
         Inet4Address serverIdentifier = null;
         Inet4Address netMask = null;
         String message = null;
@@ -720,7 +739,8 @@
         // check to see if we need to parse L2, IP, and UDP encaps
         if (pktType == ENCAP_L2) {
             if (packet.remaining() < MIN_PACKET_LENGTH_L2) {
-                return null;
+                throw new ParseException("L2 packet too short, %d < %d",
+                        packet.remaining(), MIN_PACKET_LENGTH_L2);
             }
 
             byte[] l2dst = new byte[6];
@@ -732,18 +752,20 @@
             short l2type = packet.getShort();
 
             if (l2type != OsConstants.ETH_P_IP)
-                return null;
+                throw new ParseException("Unexpected L2 type 0x%04x, expected 0x%04x",
+                        l2type, OsConstants.ETH_P_IP);
         }
 
         if (pktType <= ENCAP_L3) {
             if (packet.remaining() < MIN_PACKET_LENGTH_L3) {
-                return null;
+                throw new ParseException("L3 packet too short, %d < %d",
+                        packet.remaining(), MIN_PACKET_LENGTH_L3);
             }
 
             byte ipTypeAndLength = packet.get();
             int ipVersion = (ipTypeAndLength & 0xf0) >> 4;
             if (ipVersion != 4) {
-                return null;
+                throw new ParseException("Invalid IP version %d", ipVersion);
             }
 
             // System.out.println("ipType is " + ipType);
@@ -759,8 +781,9 @@
             ipSrc = readIpAddress(packet);
             ipDst = readIpAddress(packet);
 
-            if (ipProto != IP_TYPE_UDP) // UDP
-                return null;
+            if (ipProto != IP_TYPE_UDP) {
+                throw new ParseException("Protocol not UDP: %d", ipProto);
+            }
 
             // Skip options. This cannot cause us to read beyond the end of the buffer because the
             // IPv4 header cannot be more than (0x0f * 4) = 60 bytes long, and that is less than
@@ -776,13 +799,19 @@
             short udpLen = packet.getShort();
             short udpChkSum = packet.getShort();
 
-            if ((udpSrcPort != DHCP_SERVER) && (udpSrcPort != DHCP_CLIENT))
+            // Only accept packets to or from the well-known client port (expressly permitting
+            // packets from ports other than the well-known server port; http://b/24687559), and
+            // server-to-server packets, e.g. for relays.
+            if (!isPacketToOrFromClient(udpSrcPort, udpDstPort) &&
+                !isPacketServerToServer(udpSrcPort, udpDstPort)) {
                 return null;
+            }
         }
 
         // We need to check the length even for ENCAP_L3 because the IPv4 header is variable-length.
         if (pktType > ENCAP_BOOTP || packet.remaining() < MIN_PACKET_LENGTH_BOOTP) {
-            return null;
+            throw new ParseException("Invalid type or BOOTP packet too short, %d < %d",
+                        packet.remaining(), MIN_PACKET_LENGTH_BOOTP);
         }
 
         byte type = packet.get();
@@ -805,7 +834,7 @@
             packet.get(ipv4addr);
             relayIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
         } catch (UnknownHostException ex) {
-            return null;
+            throw new ParseException("Invalid IPv4 address: %s", Arrays.toString(ipv4addr));
         }
 
         // Some DHCP servers have been known to announce invalid client hardware address values such
@@ -828,8 +857,10 @@
 
         int dhcpMagicCookie = packet.getInt();
 
-        if (dhcpMagicCookie !=  0x63825363)
-            return null;
+        if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) {
+            throw new ParseException("Bad magic cookie 0x%08x, should be 0x%08x", dhcpMagicCookie,
+                    DHCP_MAGIC_COOKIE);
+        }
 
         // parse options
         boolean notFinishedOptions = true;
@@ -852,8 +883,9 @@
                             expectedLen = 4;
                             break;
                         case DHCP_ROUTER:
-                            gateway = readIpAddress(packet);
-                            expectedLen = 4;
+                            for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) {
+                                gateways.add(readIpAddress(packet));
+                            }
                             break;
                         case DHCP_DNS_SERVER:
                             for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) {
@@ -937,18 +969,20 @@
                     }
 
                     if (expectedLen != optionLen) {
-                        return null;
+                        throw new ParseException("Invalid length %d for option %d, expected %d",
+                                optionLen, optionType, expectedLen);
                     }
                 }
             } catch (BufferUnderflowException e) {
-                return null;
+                throw new ParseException("BufferUnderflowException");
             }
         }
 
         DhcpPacket newPacket;
 
         switch(dhcpType) {
-            case -1: return null;
+            case (byte) 0xFF:
+                throw new ParseException("No DHCP message type option");
             case DHCP_MESSAGE_TYPE_DISCOVER:
                 newPacket = new DhcpDiscoverPacket(
                     transactionId, secs, clientMac, broadcast);
@@ -981,14 +1015,13 @@
                     clientMac);
                 break;
             default:
-                System.out.println("Unimplemented type: " + dhcpType);
-                return null;
+                throw new ParseException("Unimplemented DHCP type %d", dhcpType);
         }
 
         newPacket.mBroadcastAddress = bcAddr;
         newPacket.mDnsServers = dnsServers;
         newPacket.mDomainName = domainName;
-        newPacket.mGateway = gateway;
+        newPacket.mGateways = gateways;
         newPacket.mHostName = hostName;
         newPacket.mLeaseTime = leaseTime;
         newPacket.mMessage = message;
@@ -1009,7 +1042,7 @@
      * Parse a packet from an array of bytes, stopping at the given length.
      */
     public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType)
-    {
+            throws ParseException {
         ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN);
         return decodeFullPacket(buffer, pktType);
     }
@@ -1044,7 +1077,11 @@
         } catch (IllegalArgumentException e) {
             return null;
         }
-        results.gateway = mGateway;
+
+        if (mGateways.size() > 0) {
+            results.gateway = mGateways.get(0);
+        }
+
         results.dnsServers.addAll(mDnsServers);
         results.domains = mDomainName;
         results.serverAddress = mServerIdentifier;
@@ -1086,11 +1123,11 @@
     public static ByteBuffer buildOfferPacket(int encap, int transactionId,
         boolean broadcast, Inet4Address serverIpAddr, Inet4Address clientIpAddr,
         byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr,
-        Inet4Address gateway, List<Inet4Address> dnsServers,
+        List<Inet4Address> gateways, List<Inet4Address> dnsServers,
         Inet4Address dhcpServerIdentifier, String domainName) {
         DhcpPacket pkt = new DhcpOfferPacket(
             transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac);
-        pkt.mGateway = gateway;
+        pkt.mGateways = gateways;
         pkt.mDnsServers = dnsServers;
         pkt.mLeaseTime = timeout;
         pkt.mDomainName = domainName;
@@ -1106,11 +1143,11 @@
     public static ByteBuffer buildAckPacket(int encap, int transactionId,
         boolean broadcast, Inet4Address serverIpAddr, Inet4Address clientIpAddr,
         byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr,
-        Inet4Address gateway, List<Inet4Address> dnsServers,
+        List<Inet4Address> gateways, List<Inet4Address> dnsServers,
         Inet4Address dhcpServerIdentifier, String domainName) {
         DhcpPacket pkt = new DhcpAckPacket(
             transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac);
-        pkt.mGateway = gateway;
+        pkt.mGateways = gateways;
         pkt.mDnsServers = dnsServers;
         pkt.mLeaseTime = timeout;
         pkt.mDomainName = domainName;
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index cd3b8bb..7e60bf1 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -117,7 +117,7 @@
 
     private void assertDomainAndVendorInfoParses(
             String expectedDomain, byte[] domainBytes,
-            String expectedVendorInfo, byte[] vendorInfoBytes) {
+            String expectedVendorInfo, byte[] vendorInfoBytes) throws Exception {
         ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER)
                 .setDomainBytes(domainBytes)
                 .setVendorInfoBytes(vendorInfoBytes)
@@ -158,17 +158,25 @@
     }
 
     private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime,
-                                       long leaseTimeMillis, byte[] leaseTimeBytes) {
+            long leaseTimeMillis, byte[] leaseTimeBytes) throws Exception {
         TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER);
         if (leaseTimeBytes != null) {
             testPacket.setLeaseTimeBytes(leaseTimeBytes);
         }
         ByteBuffer packet = testPacket.build();
-        DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+        DhcpPacket offerPacket = null;
+
         if (!expectValid) {
-            assertNull(offerPacket);
+            try {
+                offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+                fail("Invalid packet parsed successfully: " + offerPacket);
+            } catch (ParseException expected) {
+            }
             return;
         }
+
+        offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+        assertNotNull(offerPacket);
         assertEquals(rawLeaseTime, offerPacket.mLeaseTime);
         DhcpResults dhcpResults = offerPacket.toDhcpResults();  // Just check this doesn't crash.
         assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis());
@@ -200,14 +208,14 @@
     }
 
     private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
-                                byte[] netmaskBytes) {
+                                byte[] netmaskBytes) throws Exception {
         checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
         checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
     }
 
     private void checkIpAddress(String expected, byte type,
                                 Inet4Address clientIp, Inet4Address yourIp,
-                                byte[] netmaskBytes) {
+                                byte[] netmaskBytes) throws Exception {
         ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
                 .setNetmaskBytes(netmaskBytes)
                 .build();
@@ -506,4 +514,74 @@
         assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53",
                 "lancs.ac.uk", "10.32.255.128", null, 7200, false, dhcpResults);
     }
+
+    @SmallTest
+    public void testUdpServerAnySourcePort() throws Exception {
+        final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+            // Ethernet header.
+            "9cd917000000001c2e0000000800" +
+            // IP header.
+            "45a00148000040003d115087d18194fb0a0f7af2" +
+            // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
+            // NOTE: The server source port is not the canonical port 67.
+            "C29F004401341268" +
+            // BOOTP header.
+            "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
+            // MAC address.
+            "9cd91700000000000000000000000000" +
+            // Server name.
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            // File.
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            // Options.
+            "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
+            "d18180060f0777766d2e6564751c040a0fffffff000000"
+        ).toCharArray(), false));
+
+        DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
+        assertTrue(offerPacket instanceof DhcpOfferPacket);
+        assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac()));
+        DhcpResults dhcpResults = offerPacket.toDhcpResults();
+        assertDhcpResults("10.15.122.242/16", "10.15.200.23",
+                "209.129.128.3,209.129.148.3,209.129.128.6",
+                "wvm.edu", "10.1.105.252", null, 86400, false, dhcpResults);
+    }
+
+    @SmallTest
+    public void testMultipleRouters() throws Exception {
+        final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+            // Ethernet header.
+            "fc3d93000000" + "081735000000" + "0800" +
+            // IP header.
+            "45000148c2370000ff117ac2c0a8bd02ffffffff" +
+            // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
+            "0043004401343beb" +
+            // BOOTP header.
+            "0201060027f518e20000800000000000c0a8bd310000000000000000" +
+            // MAC address.
+            "fc3d9300000000000000000000000000" +
+            // Server name.
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            // File.
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            "0000000000000000000000000000000000000000000000000000000000000000" +
+            // Options.
+            "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" +
+            "0308c0a8bd01ffffff0006080808080808080404ff000000000000"
+        ).toCharArray(), false));
+
+        DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
+        assertTrue(offerPacket instanceof DhcpOfferPacket);
+        assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac()));
+        DhcpResults dhcpResults = offerPacket.toDhcpResults();
+        assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4",
+                null, "192.171.189.2", null, 28800, false, dhcpResults);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
new file mode 100644
index 0000000..be6861c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
@@ -0,0 +1,64 @@
+package com.android.server.content;
+
+import android.os.Bundle;
+
+import junit.framework.TestCase;
+
+public class SyncManagerTest extends TestCase {
+
+    final String KEY_1 = "key_1";
+    final String KEY_2 = "key_2";
+
+    public void testSyncExtrasEquals_WithNull() throws Exception {
+        Bundle b1 = new Bundle();
+        Bundle b2 = new Bundle();
+
+        b1.putString(KEY_1, null);
+        b2.putString(KEY_1, null);
+
+        assertTrue("Null extra not properly compared between bundles.",
+                SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */));
+    }
+
+    public void testSyncExtrasEqualsBigger_WithNull() throws Exception {
+        Bundle b1 = new Bundle();
+        Bundle b2 = new Bundle();
+
+        b1.putString(KEY_1, null);
+        b2.putString(KEY_1, null);
+
+        b1.putString(KEY_2, "bla");
+        b2.putString(KEY_2, "bla");
+
+        assertTrue("Extras not properly compared between bundles.",
+                SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */));
+    }
+
+    public void testSyncExtrasEqualsFails_differentValues() throws Exception {
+        Bundle b1 = new Bundle();
+        Bundle b2 = new Bundle();
+
+        b1.putString(KEY_1, null);
+        b2.putString(KEY_1, null);
+
+        b1.putString(KEY_2, "bla");
+        b2.putString(KEY_2, "ble");  // different key
+
+        assertFalse("Extras considered equal when they are different.",
+                SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */));
+    }
+
+    public void testSyncExtrasEqualsFails_differentNulls() throws Exception {
+        Bundle b1 = new Bundle();
+        Bundle b2 = new Bundle();
+
+        b1.putString(KEY_1, null);
+        b2.putString(KEY_1, "bla");  // different key
+
+        b1.putString(KEY_2, "ble");
+        b2.putString(KEY_2, "ble");
+
+        assertFalse("Extras considered equal when they are different.",
+                SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index bd64392..0b73beb 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -6,6 +6,7 @@
 import android.app.job.JobInfo;
 import android.app.job.JobInfo.Builder;
 import android.os.PersistableBundle;
+import android.os.SystemClock;
 import android.test.AndroidTestCase;
 import android.test.RenamingDelegatingContext;
 import android.util.Log;
@@ -102,6 +103,14 @@
         Iterator<JobStatus> it = jobStatusSet.iterator();
         JobStatus loaded1 = it.next();
         JobStatus loaded2 = it.next();
+
+        // Reverse them so we know which comparison to make.
+        if (loaded1.getJobId() != 8) {
+            JobStatus tmp = loaded1;
+            loaded1 = loaded2;
+            loaded2 = tmp;
+        }
+
         assertTasksEqual(task1, loaded1.getJob());
         assertTasksEqual(task2, loaded2.getJob());
         assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(taskStatus1));
@@ -143,6 +152,36 @@
         assertTasksEqual(task, loaded.getJob());
     }
 
+    public void testMassivePeriodClampedOnRead() throws Exception {
+        final long TEN_SECONDS = 10000L;
+        JobInfo.Builder b = new Builder(8, mComponent)
+                .setPeriodic(TEN_SECONDS)
+                .setPersisted(true);
+        final long invalidLateRuntimeElapsedMillis =
+                SystemClock.elapsedRealtime() + (TEN_SECONDS * 2) + 5000;  // >2P from now.
+        final long invalidEarlyRuntimeElapsedMillis =
+                invalidLateRuntimeElapsedMillis - TEN_SECONDS;  // Early is (late - period).
+        final JobStatus js = new JobStatus(b.build(), SOME_UID,
+                invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis);
+
+        mTaskStoreUnderTest.add(js);
+        Thread.sleep(IO_WAIT);
+
+        final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
+        mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
+        assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size());
+        JobStatus loaded = jobStatusSet.iterator().next();
+
+        // Assert early runtime was clamped to be under now + period. We can do <= here b/c we'll
+        // call SystemClock.elapsedRealtime after doing the disk i/o.
+        final long newNowElapsed = SystemClock.elapsedRealtime();
+        assertTrue("Early runtime wasn't correctly clamped.",
+                loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS);
+        // Assert late runtime was clamped to be now + period*2.
+        assertTrue("Early runtime wasn't correctly clamped.",
+                loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS*2);
+    }
+
     /**
      * Helper function to throw an error if the provided task and TaskStatus objects are not equal.
      */
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 4146c1c0..5ad796f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -314,6 +314,8 @@
                 mAppIdleParoled = paroled;
                 if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleParoled);
                 if (paroled) {
+                    postParoleEndTimeout();
+                } else {
                     mLastAppIdleParoledTime = checkAndGetTimeLocked();
                     postNextParoleTimeout();
                 }
@@ -404,8 +406,6 @@
                 if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
                     if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
                     setAppIdleParoled(true);
-                    // Make sure it ends at some point
-                    postParoleEndTimeout();
                 } else {
                     if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
                     postNextParoleTimeout();
@@ -492,7 +492,6 @@
             if (!deviceIdle
                     && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
                 if (DEBUG) Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
-                postNextParoleTimeout();
                 setAppIdleParoled(true);
             } else if (deviceIdle) {
                 if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
index 3ca0c84..31d859f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
@@ -87,6 +87,7 @@
     // This is an indirect indication of the microphone being open in some other application.
     private boolean mServiceDisabled = false;
     private boolean mStarted = false;
+    private boolean mRecognitionAborted = false;
     private PowerSaveModeListener mPowerSaveModeListener;
 
     SoundTriggerHelper(Context context) {
@@ -386,8 +387,9 @@
 
     private void onRecognitionAbortLocked() {
         Slog.w(TAG, "Recognition aborted");
-        // No-op
-        // This is handled via service state changes instead.
+        // If abort has been called, the hardware has already stopped recognition, so we shouldn't
+        // call it again when we process the state change.
+        mRecognitionAborted = true;
     }
 
     private void onRecognitionFailureLocked() {
@@ -490,8 +492,13 @@
             }
             return status;
         } else {
-            // Stop recognition.
-            int status = mModule.stopRecognition(mCurrentSoundModelHandle);
+            // Stop recognition (only if we haven't been aborted).
+            int status = STATUS_OK;
+            if (!mRecognitionAborted) {
+                status = mModule.stopRecognition(mCurrentSoundModelHandle);
+            } else {
+                mRecognitionAborted = false;
+            }
             if (status != SoundTrigger.STATUS_OK) {
                 Slog.w(TAG, "stopRecognition call failed with " + status);
                 if (notify) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index a8874d0..a96c164 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -925,7 +925,7 @@
                 return;
             }
             synchronized (this) {
-                pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)\n");
+                pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)");
                 pw.println("  mEnableService: " + mEnableService);
                 if (mImpl == null) {
                     pw.println("  (No active implementation)");
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 28520be..30296e1 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -36,6 +36,7 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionService;
 import android.service.voice.VoiceInteractionServiceInfo;
+import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.view.IWindowManager;
 
@@ -114,9 +115,9 @@
         mAm = ActivityManagerNative.getDefault();
         VoiceInteractionServiceInfo info;
         try {
-            info = new VoiceInteractionServiceInfo(context.getPackageManager(), service);
-        } catch (PackageManager.NameNotFoundException e) {
-            Slog.w(TAG, "Voice interaction service not found: " + service);
+            info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser);
+        } catch (RemoteException|PackageManager.NameNotFoundException e) {
+            Slog.w(TAG, "Voice interaction service not found: " + service, e);
             mInfo = null;
             mSessionComponentName = null;
             mIWindowManager = null;
@@ -260,9 +261,18 @@
             }
             return;
         }
+        pw.print("  mUser="); pw.println(mUser);
         pw.print("  mComponent="); pw.println(mComponent.flattenToShortString());
         pw.print("  Session service="); pw.println(mInfo.getSessionService());
+        pw.println("  Service info:");
+        mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), "    ");
+        pw.println("  Application info:");
+        mInfo.getServiceInfo().applicationInfo.dump(new PrintWriterPrinter(pw), "    ");
+        pw.print("  Recognition service="); pw.println(mInfo.getRecognitionService());
         pw.print("  Settings activity="); pw.println(mInfo.getSettingsActivity());
+        pw.print("  Supports assist="); pw.println(mInfo.getSupportsAssist());
+        pw.print("  Supports launch from keyguard=");
+        pw.println(mInfo.getSupportsLaunchFromKeyguard());
         if (mDisabledShowContext != 0) {
             pw.print("  mDisabledShowContext=");
             pw.println(Integer.toHexString(mDisabledShowContext));
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index cdb0bf2..b64043c 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -121,6 +121,14 @@
     public static final int CAPABILITY_CALL_SUBJECT = 0x40;
 
     /**
+     * Flag indicating that this {@code PhoneAccount} should only be used for emergency calls.
+     * <p>
+     * See {@link #getCapabilities}
+     * @hide
+     */
+    public static final int CAPABILITY_EMERGENCY_CALLS_ONLY = 0x80;
+
+    /**
      * URI scheme for telephone number URIs.
      */
     public static final String SCHEME_TEL = "tel";
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index aa4da4b..29e54a3 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -191,7 +191,7 @@
     public static final String
             KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
 
-   /**
+    /**
      * Override the platform's notion of a network operator being considered roaming.
      * Value is string array of MCCMNCs to be considered roaming for 3GPP RATs.
      */
@@ -268,6 +268,13 @@
             = "carrier_allow_turnoff_ims_bool";
 
     /**
+     * Flag specifying whether Generic Bootstrapping Architecture capable SIM is required for IMS.
+     * @hide
+     */
+    public static final String KEY_CARRIER_IMS_GBA_REQUIRED_BOOL
+            = "carrier_ims_gba_required_bool";
+
+    /**
      * Flag specifying whether IMS instant lettering is available for the carrier.  {@code True} if
      * instant lettering is available for the carrier, {@code false} otherwise.
      * @hide
@@ -380,9 +387,9 @@
             "ci_action_on_sys_update_extra_val_string";
 
     /**
-     * Specifies the amount of gap to be added in millis between DTMF tones. When a non-zero value
-     * is specified, the UE shall wait for the specified amount of time before it sends out
-     * successive DTMF tones on the network.
+     * Specifies the amount of gap to be added in millis between postdial DTMF tones. When a
+     * non-zero value is specified, the UE shall wait for the specified amount of time before it
+     * sends out successive DTMF tones on the network.
      * @hide
      */
     public static final String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
@@ -421,6 +428,14 @@
     public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
 
     /**
+     * Specifies the amount of gap to be added in millis between postdial DTMF tones. When a
+     * non-zero value is specified, the UE shall wait for the specified amount of time before it
+     * sends out successive DTMF tones on the network.
+     * @hide
+     */
+    public static final String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
+
+    /**
      * If this is true, the SIM card (through Customer Service Profile EF file) will be able to
      * prevent manual operator selection. If false, this SIM setting will be ignored and manual
      * operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more
@@ -428,6 +443,12 @@
      */
     public static final String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
 
+    /**
+     * Allow user to add APNs
+     * @hide
+     */
+    public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
+
     // These variables are used by the MMS service and exposed through another API, {@link
     // SmsManager}. The variable names and string values are copied from there.
     public static final String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
@@ -489,6 +510,7 @@
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
+        sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
         sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, "");
         sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, "");
@@ -524,6 +546,7 @@
         sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, "");
         sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING, "");
         sDefaults.putBoolean(KEY_CSP_ENABLED_BOOL, false);
+        sDefaults.putBoolean(KEY_ALLOW_ADDING_APNS_BOOL, true);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
 
         sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
@@ -537,6 +560,7 @@
         sDefaults.putBoolean(KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true);
         sDefaults.putBoolean(KEY_HIDE_IMS_APN_BOOL, false);
         sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
+        sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
 
         // MMS defaults
         sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index f535e5d..fced667 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -911,7 +911,7 @@
         else if (tdScdmaDbm >= -49) level = SIGNAL_STRENGTH_GREAT;
         else if (tdScdmaDbm >= -73) level = SIGNAL_STRENGTH_GOOD;
         else if (tdScdmaDbm >= -97) level = SIGNAL_STRENGTH_MODERATE;
-        else if (tdScdmaDbm >= -120) level = SIGNAL_STRENGTH_POOR;
+        else if (tdScdmaDbm >= -110) level = SIGNAL_STRENGTH_POOR;
         else level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
 
         if (DBG) log("getTdScdmaLevel = " + level);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 37ffa06..e11c8d3 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -334,7 +334,8 @@
 
     @Override
     public String toString() {
-        return "{id=" + mId + ", iccId=" + mIccId + " simSlotIndex=" + mSimSlotIndex
+        String iccIdToPrint = mIccId != null ? mIccId.substring(0, 9) + "XXXXXXXXXXX" : null;
+        return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
                 + " displayName=" + mDisplayName + " carrierName=" + mCarrierName
                 + " nameSource=" + mNameSource + " iconTint=" + mIconTint
                 + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d22727d..6b1b6296 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2874,7 +2874,7 @@
     /**
      * Returns all observed cell information from all radios on the
      * device including the primary and neighboring cells. This does
-     * not cause or change the rate of PhoneStateListner#onCellInfoChanged.
+     * not cause or change the rate of PhoneStateListener#onCellInfoChanged.
      *<p>
      * The list can include one or more of {@link android.telephony.CellInfoGsm CellInfoGsm},
      * {@link android.telephony.CellInfoCdma CellInfoCdma},
@@ -2888,6 +2888,9 @@
      * devices this may return null in which case getCellLocation should
      * be called.
      *<p>
+     * This API will return valid data for registered cells on devices with
+     * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}
+     *<p>
      * @return List of CellInfo or null if info unavailable.
      *
      * <p>Requires Permission: {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
diff --git a/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java b/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java
index 49ee642..2e44a77 100644
--- a/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java
@@ -29,9 +29,6 @@
 import android.view.ViewGroup;
 import android.widget.ListView;
 
-import java.util.HashMap;
-import java.util.Map;
-
 /**
  * Delegate that provides implementation for native methods in {@link Preference}
  * <p/>
@@ -59,9 +56,9 @@
      */
     public static View inflatePreference(Context context, XmlPullParser parser, ViewGroup root) {
         PreferenceManager pm = new PreferenceManager(context);
-        PreferenceScreen ps = pm.getPreferenceScreen();
         PreferenceInflater inflater = new BridgePreferenceInflater(context, pm);
-        ps = (PreferenceScreen) inflater.inflate(parser, ps, true);
+        PreferenceScreen ps = (PreferenceScreen) inflater.inflate(parser, null, true);
+        pm.setPreferences(ps);
         ListView preferenceView = createContainerView(context, root);
         ps.bind(preferenceView);
         return preferenceView;
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index e611ea4..59b22bd 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -101,9 +101,18 @@
     public static final String REALM_KEY           = "realm";
     /** @hide */
     public static final String PLMN_KEY            = "plmn";
+    /** @hide */
+    public static final String PHASE1_KEY          = "phase1";
 
+    /** {@hide} */
+    public static final String ENABLE_TLS_1_2 = "\"tls_disable_tlsv1_2=0\"";
+    /** {@hide} */
+    public static final String DISABLE_TLS_1_2 = "\"tls_disable_tlsv1_2=1\"";
 
     private HashMap<String, String> mFields = new HashMap<String, String>();
+    //By default, we enable TLS1.2. However, due to a known bug on some radius, we may disable it to
+    // fall back to TLS 1.1.
+    private boolean mTls12Enable =  true;
     private X509Certificate mCaCert;
     private PrivateKey mClientPrivateKey;
     private X509Certificate mClientCertificate;
@@ -149,6 +158,7 @@
         }
 
         writeCertificate(dest, mClientCertificate);
+        dest.writeInt(mTls12Enable ? 1: 0);
     }
 
     private void writeCertificate(Parcel dest, X509Certificate cert) {
@@ -196,6 +206,7 @@
 
                     enterpriseConfig.mClientPrivateKey = userKey;
                     enterpriseConfig.mClientCertificate = readCertificate(in);
+                    enterpriseConfig.mTls12Enable = (in.readInt() == 1);
                     return enterpriseConfig;
                 }
 
@@ -300,6 +311,26 @@
     }
 
     /**
+     * Set the TLS version
+     * @param enable: true -- enable TLS1.2  false -- disable TLS1.2
+     * @hide
+     */
+    public void setTls12Enable(boolean enable) {
+        mTls12Enable = enable;
+        mFields.put(PHASE1_KEY,
+                enable ? ENABLE_TLS_1_2 : DISABLE_TLS_1_2);
+    }
+
+    /**
+     * Get the TLS1.2 enabled or not
+     * @return eap method configured
+     * @hide
+     */
+    public boolean getTls12Enable() {
+        return mTls12Enable;
+    }
+
+    /**
      * Get the eap method.
      * @return eap method configured
      */