Merge "Import translations. DO NOT MERGE"
diff --git a/api/current.txt b/api/current.txt
index 96c47ed..4e7d2dc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -989,6 +989,7 @@
     field public static final int preferenceStyle = 16842894; // 0x101008e
     field public static final int presentationTheme = 16843712; // 0x10103c0
     field public static final int previewImage = 16843482; // 0x10102da
+    field public static final int primaryContentAlpha = 16843367; // 0x1010267
     field public static final int priority = 16842780; // 0x101001c
     field public static final int privateImeOptions = 16843299; // 0x1010223
     field public static final int process = 16842769; // 0x1010011
@@ -4591,6 +4592,7 @@
 
   public abstract class FragmentContainer {
     ctor public FragmentContainer();
+    method public android.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
     method public abstract android.view.View onFindViewById(int);
     method public abstract boolean onHasView();
   }
@@ -5025,6 +5027,7 @@
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
     field public static final int DEFAULT_SOUND = 1; // 0x1
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
     field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
     field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
     field public static final java.lang.String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
@@ -5108,6 +5111,7 @@
     method public android.app.Notification.Action clone();
     method public int describeContents();
     method public boolean getAllowGeneratedReplies();
+    method public android.app.RemoteInput[] getDataOnlyRemoteInputs();
     method public android.os.Bundle getExtras();
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput[] getRemoteInputs();
@@ -5573,14 +5577,18 @@
   }
 
   public final class RemoteInput implements android.os.Parcelable {
+    method public static void addDataResultToIntent(android.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
     method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
     method public int describeContents();
     method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String> getAllowedDataTypes();
     method public java.lang.CharSequence[] getChoices();
+    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
     method public android.os.Bundle getExtras();
     method public java.lang.CharSequence getLabel();
     method public java.lang.String getResultKey();
     method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
+    method public boolean isDataOnly();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR;
     field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
@@ -5592,6 +5600,7 @@
     method public android.app.RemoteInput.Builder addExtras(android.os.Bundle);
     method public android.app.RemoteInput build();
     method public android.os.Bundle getExtras();
+    method public android.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
     method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
     method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
     method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
@@ -8999,10 +9008,10 @@
     field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list";
     field public static final java.lang.String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER";
     field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
-    field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
-    field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
-    field public static final java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
-    field public static final java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
     field public static final java.lang.String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
     field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM";
     field public static final java.lang.String EXTRA_SUBJECT = "android.intent.extra.SUBJECT";
@@ -9787,6 +9796,8 @@
     method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
     method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
     method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
+    method public android.content.IntentSender getShortcutConfigActivityIntent(android.content.pm.LauncherActivityInfo);
+    method public java.util.List<android.content.pm.LauncherActivityInfo> getShortcutConfigActivityList(java.lang.String, android.os.UserHandle);
     method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean hasShortcutHostPermission();
@@ -10390,6 +10401,7 @@
 
   public class ShortcutManager {
     method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+    method public android.content.Intent createShortcutResultIntent(android.content.pm.ShortcutInfo);
     method public void disableShortcuts(java.util.List<java.lang.String>);
     method public void disableShortcuts(java.util.List<java.lang.String>, java.lang.CharSequence);
     method public void enableShortcuts(java.util.List<java.lang.String>);
@@ -29888,6 +29900,15 @@
     field public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; // 0xfffffff8
   }
 
+  public abstract class ProxyFileDescriptorCallback {
+    ctor public ProxyFileDescriptorCallback();
+    method public void onFsync() throws android.system.ErrnoException;
+    method public long onGetSize() throws android.system.ErrnoException;
+    method public int onRead(long, int, byte[]) throws android.system.ErrnoException;
+    method public abstract void onRelease();
+    method public int onWrite(long, int, byte[]) throws android.system.ErrnoException;
+  }
+
   public class RecoverySystem {
     method public static void installPackage(android.content.Context, java.io.File) throws java.io.IOException;
     method public static void rebootWipeCache(android.content.Context) throws java.io.IOException;
@@ -30305,6 +30326,7 @@
     method public boolean isEncrypted(java.io.File);
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
+    method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException;
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
     field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
   }
@@ -35249,6 +35271,7 @@
     field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
     field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
+    field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
   public final class FillCallback {
@@ -35454,7 +35477,6 @@
     field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
     field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
     field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
-    field public static final java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
   }
 
   public class MediaBrowserService.Result<T> {
diff --git a/api/system-current.txt b/api/system-current.txt
index 497b963..0acfea2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1098,6 +1098,7 @@
     field public static final int preferenceStyle = 16842894; // 0x101008e
     field public static final int presentationTheme = 16843712; // 0x10103c0
     field public static final int previewImage = 16843482; // 0x10102da
+    field public static final int primaryContentAlpha = 16843367; // 0x1010267
     field public static final int priority = 16842780; // 0x101001c
     field public static final int privateImeOptions = 16843299; // 0x1010223
     field public static final int process = 16842769; // 0x1010011
@@ -4748,6 +4749,7 @@
 
   public abstract class FragmentContainer {
     ctor public FragmentContainer();
+    method public android.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
     method public abstract android.view.View onFindViewById(int);
     method public abstract boolean onHasView();
   }
@@ -5183,6 +5185,7 @@
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
     field public static final int DEFAULT_SOUND = 1; // 0x1
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
     field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
     field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
     field public static final java.lang.String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
@@ -5268,6 +5271,7 @@
     method public android.app.Notification.Action clone();
     method public int describeContents();
     method public boolean getAllowGeneratedReplies();
+    method public android.app.RemoteInput[] getDataOnlyRemoteInputs();
     method public android.os.Bundle getExtras();
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput[] getRemoteInputs();
@@ -5758,14 +5762,18 @@
   }
 
   public final class RemoteInput implements android.os.Parcelable {
+    method public static void addDataResultToIntent(android.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
     method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
     method public int describeContents();
     method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String> getAllowedDataTypes();
     method public java.lang.CharSequence[] getChoices();
+    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
     method public android.os.Bundle getExtras();
     method public java.lang.CharSequence getLabel();
     method public java.lang.String getResultKey();
     method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
+    method public boolean isDataOnly();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR;
     field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
@@ -5777,6 +5785,7 @@
     method public android.app.RemoteInput.Builder addExtras(android.os.Bundle);
     method public android.app.RemoteInput build();
     method public android.os.Bundle getExtras();
+    method public android.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
     method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
     method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
     method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
@@ -9392,10 +9401,10 @@
     field public static final java.lang.String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
     field public static final java.lang.String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER";
     field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
-    field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
-    field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
-    field public static final java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
-    field public static final java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
     field public static final java.lang.String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
     field public static final java.lang.String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME";
     field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM";
@@ -10215,6 +10224,8 @@
     method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
     method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
     method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
+    method public android.content.IntentSender getShortcutConfigActivityIntent(android.content.pm.LauncherActivityInfo);
+    method public java.util.List<android.content.pm.LauncherActivityInfo> getShortcutConfigActivityList(java.lang.String, android.os.UserHandle);
     method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean hasShortcutHostPermission();
@@ -10891,6 +10902,7 @@
 
   public class ShortcutManager {
     method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+    method public android.content.Intent createShortcutResultIntent(android.content.pm.ShortcutInfo);
     method public void disableShortcuts(java.util.List<java.lang.String>);
     method public void disableShortcuts(java.util.List<java.lang.String>, java.lang.CharSequence);
     method public void enableShortcuts(java.util.List<java.lang.String>);
@@ -21907,6 +21919,7 @@
     method public int getClientPid();
     method public int getClientUid();
     method public int getPlayerInterfaceId();
+    method public android.media.PlayerProxy getPlayerProxy();
     method public int getPlayerState();
     method public int getPlayerType();
     method public void writeToParcel(android.os.Parcel, int);
@@ -23735,6 +23748,13 @@
     field public static final android.os.Parcelable.Creator<android.media.PlaybackParams> CREATOR;
   }
 
+  public class PlayerProxy {
+    method public void pause() throws java.lang.IllegalStateException;
+    method public void setVolume(float) throws java.lang.IllegalStateException;
+    method public void start() throws java.lang.IllegalStateException;
+    method public void stop() throws java.lang.IllegalStateException;
+  }
+
   public final class Rating implements android.os.Parcelable {
     method public int describeContents();
     method public float getPercentRating();
@@ -32501,6 +32521,15 @@
     field public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; // 0xfffffff8
   }
 
+  public abstract class ProxyFileDescriptorCallback {
+    ctor public ProxyFileDescriptorCallback();
+    method public void onFsync() throws android.system.ErrnoException;
+    method public long onGetSize() throws android.system.ErrnoException;
+    method public int onRead(long, int, byte[]) throws android.system.ErrnoException;
+    method public abstract void onRelease();
+    method public int onWrite(long, int, byte[]) throws android.system.ErrnoException;
+  }
+
   public class RecoverySystem {
     method public static void cancelScheduledUpdate(android.content.Context) throws java.io.IOException;
     method public static void installPackage(android.content.Context, java.io.File) throws java.io.IOException;
@@ -33013,6 +33042,7 @@
     method public boolean isEncrypted(java.io.File);
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
+    method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException;
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
     field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
   }
@@ -38140,6 +38170,7 @@
     field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
     field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
+    field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
   public final class FillCallback {
@@ -38345,7 +38376,6 @@
     field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
     field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
     field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
-    field public static final java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
   }
 
   public class MediaBrowserService.Result<T> {
diff --git a/api/test-current.txt b/api/test-current.txt
index 96ddfc7..8abaf84 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -989,6 +989,7 @@
     field public static final int preferenceStyle = 16842894; // 0x101008e
     field public static final int presentationTheme = 16843712; // 0x10103c0
     field public static final int previewImage = 16843482; // 0x10102da
+    field public static final int primaryContentAlpha = 16843367; // 0x1010267
     field public static final int priority = 16842780; // 0x101001c
     field public static final int privateImeOptions = 16843299; // 0x1010223
     field public static final int process = 16842769; // 0x1010011
@@ -4601,6 +4602,7 @@
 
   public abstract class FragmentContainer {
     ctor public FragmentContainer();
+    method public android.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
     method public abstract android.view.View onFindViewById(int);
     method public abstract boolean onHasView();
   }
@@ -5035,6 +5037,7 @@
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
     field public static final int DEFAULT_SOUND = 1; // 0x1
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
     field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
     field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
     field public static final java.lang.String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
@@ -5118,6 +5121,7 @@
     method public android.app.Notification.Action clone();
     method public int describeContents();
     method public boolean getAllowGeneratedReplies();
+    method public android.app.RemoteInput[] getDataOnlyRemoteInputs();
     method public android.os.Bundle getExtras();
     method public android.graphics.drawable.Icon getIcon();
     method public android.app.RemoteInput[] getRemoteInputs();
@@ -5584,14 +5588,18 @@
   }
 
   public final class RemoteInput implements android.os.Parcelable {
+    method public static void addDataResultToIntent(android.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
     method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
     method public int describeContents();
     method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String> getAllowedDataTypes();
     method public java.lang.CharSequence[] getChoices();
+    method public static java.util.Map<java.lang.String, android.net.Uri> getDataResultsFromIntent(android.content.Intent, java.lang.String);
     method public android.os.Bundle getExtras();
     method public java.lang.CharSequence getLabel();
     method public java.lang.String getResultKey();
     method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
+    method public boolean isDataOnly();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.RemoteInput> CREATOR;
     field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
@@ -5603,6 +5611,7 @@
     method public android.app.RemoteInput.Builder addExtras(android.os.Bundle);
     method public android.app.RemoteInput build();
     method public android.os.Bundle getExtras();
+    method public android.app.RemoteInput.Builder setAllowDataType(java.lang.String, boolean);
     method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
     method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
     method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
@@ -9024,10 +9033,10 @@
     field public static final java.lang.String EXTRA_RESTRICTIONS_LIST = "android.intent.extra.restrictions_list";
     field public static final java.lang.String EXTRA_RESULT_RECEIVER = "android.intent.extra.RESULT_RECEIVER";
     field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
-    field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
-    field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
-    field public static final java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
-    field public static final java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
+    field public static final deprecated java.lang.String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
     field public static final java.lang.String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
     field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM";
     field public static final java.lang.String EXTRA_SUBJECT = "android.intent.extra.SUBJECT";
@@ -9815,6 +9824,8 @@
     method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
     method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
     method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
+    method public android.content.IntentSender getShortcutConfigActivityIntent(android.content.pm.LauncherActivityInfo);
+    method public java.util.List<android.content.pm.LauncherActivityInfo> getShortcutConfigActivityList(java.lang.String, android.os.UserHandle);
     method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean hasShortcutHostPermission();
@@ -10422,6 +10433,7 @@
   public class ShortcutManager {
     ctor public ShortcutManager(android.content.Context);
     method public boolean addDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+    method public android.content.Intent createShortcutResultIntent(android.content.pm.ShortcutInfo);
     method public void disableShortcuts(java.util.List<java.lang.String>);
     method public void disableShortcuts(java.util.List<java.lang.String>, java.lang.CharSequence);
     method public void enableShortcuts(java.util.List<java.lang.String>);
@@ -29998,6 +30010,15 @@
     field public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; // 0xfffffff8
   }
 
+  public abstract class ProxyFileDescriptorCallback {
+    ctor public ProxyFileDescriptorCallback();
+    method public void onFsync() throws android.system.ErrnoException;
+    method public long onGetSize() throws android.system.ErrnoException;
+    method public int onRead(long, int, byte[]) throws android.system.ErrnoException;
+    method public abstract void onRelease();
+    method public int onWrite(long, int, byte[]) throws android.system.ErrnoException;
+  }
+
   public class RecoverySystem {
     method public static void installPackage(android.content.Context, java.io.File) throws java.io.IOException;
     method public static void rebootWipeCache(android.content.Context) throws java.io.IOException;
@@ -30416,6 +30437,7 @@
     method public boolean isEncrypted(java.io.File);
     method public boolean isObbMounted(java.lang.String);
     method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
+    method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException;
     method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
     field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
   }
@@ -35368,6 +35390,7 @@
     field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
     field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
+    field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
   public final class FillCallback {
@@ -35573,7 +35596,6 @@
     field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
     field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
     field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
-    field public static final java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
   }
 
   public class MediaBrowserService.Result<T> {
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index d5580ac..0ea141c 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -184,10 +184,6 @@
 
 int main(int argc, char* const argv[])
 {
-    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
-        LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
-    }
-
     if (!LOG_NDEBUG) {
       String8 argv_String;
       for (int i = 0; i < argc; ++i) {
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 07a8253..b76aeb7 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -414,9 +414,9 @@
     public int flags;
 
     /**
-     * The unique string Id to identify the accessibility service.
+     * The component name the accessibility service.
      */
-    private String mId;
+    private ComponentName mComponentName;
 
     /**
      * The Service that implements this accessibility service component.
@@ -464,7 +464,7 @@
     public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context)
             throws XmlPullParserException, IOException {
         ServiceInfo serviceInfo = resolveInfo.serviceInfo;
-        mId = new ComponentName(serviceInfo.packageName, serviceInfo.name).flattenToShortString();
+        mComponentName = new ComponentName(serviceInfo.packageName, serviceInfo.name);
         mResolveInfo = resolveInfo;
 
         XmlResourceParser parser = null;
@@ -574,7 +574,14 @@
      * @hide
      */
     public void setComponentName(ComponentName component) {
-        mId = component.flattenToShortString();
+        mComponentName = component;
+    }
+
+    /**
+     * @hide
+     */
+    public ComponentName getComponentName() {
+        return mComponentName;
     }
 
     /**
@@ -585,7 +592,7 @@
      * @return The id.
      */
     public String getId() {
-        return mId;
+        return mComponentName.flattenToShortString();
     }
 
     /**
@@ -715,7 +722,7 @@
         parcel.writeInt(feedbackType);
         parcel.writeLong(notificationTimeout);
         parcel.writeInt(flags);
-        parcel.writeString(mId);
+        parcel.writeParcelable(mComponentName, flagz);
         parcel.writeParcelable(mResolveInfo, 0);
         parcel.writeString(mSettingsActivityName);
         parcel.writeInt(mCapabilities);
@@ -729,7 +736,7 @@
         feedbackType = parcel.readInt();
         notificationTimeout = parcel.readLong();
         flags = parcel.readInt();
-        mId = parcel.readString();
+        mComponentName = parcel.readParcelable(this.getClass().getClassLoader());
         mResolveInfo = parcel.readParcelable(null);
         mSettingsActivityName = parcel.readString();
         mCapabilities = parcel.readInt();
@@ -739,7 +746,7 @@
 
     @Override
     public int hashCode() {
-        return 31 * 1 + ((mId == null) ? 0 : mId.hashCode());
+        return 31 * 1 + ((mComponentName == null) ? 0 : mComponentName.hashCode());
     }
 
     @Override
@@ -754,11 +761,11 @@
             return false;
         }
         AccessibilityServiceInfo other = (AccessibilityServiceInfo) obj;
-        if (mId == null) {
-            if (other.mId != null) {
+        if (mComponentName == null) {
+            if (other.mComponentName != null) {
                 return false;
             }
-        } else if (!mId.equals(other.mId)) {
+        } else if (!mComponentName.equals(other.mComponentName)) {
             return false;
         }
         return true;
@@ -777,7 +784,7 @@
         stringBuilder.append(", ");
         appendFlags(stringBuilder, flags);
         stringBuilder.append(", ");
-        stringBuilder.append("id: ").append(mId);
+        stringBuilder.append("id: ").append(getId());
         stringBuilder.append(", ");
         stringBuilder.append("resolveInfo: ").append(mResolveInfo);
         stringBuilder.append(", ");
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 10ab2bc..62d6898 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -102,15 +102,19 @@
         mSavedFragmentState = in.readBundle();
     }
 
-    public Fragment instantiate(FragmentHostCallback host, Fragment parent,
-            FragmentManagerNonConfig childNonConfig) {
+    public Fragment instantiate(FragmentHostCallback host, FragmentContainer container,
+            Fragment parent, FragmentManagerNonConfig childNonConfig) {
         if (mInstance == null) {
             final Context context = host.getContext();
             if (mArguments != null) {
                 mArguments.setClassLoader(context.getClassLoader());
             }
 
-            mInstance = Fragment.instantiate(context, mClassName, mArguments);
+            if (container != null) {
+                mInstance = container.instantiate(context, mClassName, mArguments);
+            } else {
+                mInstance = Fragment.instantiate(context, mClassName, mArguments);
+            }
 
             if (mSavedFragmentState != null) {
                 mSavedFragmentState.setClassLoader(context.getClassLoader());
diff --git a/core/java/android/app/FragmentContainer.java b/core/java/android/app/FragmentContainer.java
index b2e0300..6ed54dc 100644
--- a/core/java/android/app/FragmentContainer.java
+++ b/core/java/android/app/FragmentContainer.java
@@ -18,6 +18,8 @@
 
 import android.annotation.IdRes;
 import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Bundle;
 import android.view.View;
 
 /**
@@ -35,4 +37,13 @@
      * Return {@code true} if the container holds any view.
      */
     public abstract boolean onHasView();
+
+    /**
+     * Creates an instance of the specified fragment, can be overridden to construct fragments
+     * with dependencies, or change the fragment being constructed. By default just calls
+     * {@link Fragment#instantiate(Context, String, Bundle)}.
+     */
+    public Fragment instantiate(Context context, String className, Bundle arguments) {
+        return Fragment.instantiate(context, className, arguments);
+    }
 }
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index efd2b69..44f1322 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1984,11 +1984,13 @@
                 if (startIndex != recordNum) {
                     executeOpsTogether(records, isRecordPop, startIndex, recordNum);
                 }
-                // execute all unoptimized together
-                int optimizeEnd;
-                for (optimizeEnd = recordNum + 1; optimizeEnd < numRecords; optimizeEnd++) {
-                    if (records.get(optimizeEnd).mAllowOptimization) {
-                        break;
+                // execute all unoptimized pop operations together or one add operation
+                int optimizeEnd = recordNum + 1;
+                if (isRecordPop.get(recordNum)) {
+                    while (optimizeEnd < numRecords
+                            && isRecordPop.get(optimizeEnd)
+                            && !records.get(optimizeEnd).mAllowOptimization) {
+                        optimizeEnd++;
                     }
                 }
                 executeOpsTogether(records, isRecordPop, recordNum, optimizeEnd);
@@ -2651,7 +2653,7 @@
                 if (childNonConfigs != null && i < childNonConfigs.size()) {
                     childNonConfig = childNonConfigs.get(i);
                 }
-                Fragment f = fs.instantiate(mHost, mParent, childNonConfig);
+                Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig);
                 if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
                 mActive.add(f);
                 // Now that the fragment is instantiated (or came from being
@@ -3270,7 +3272,7 @@
                 + Integer.toHexString(id) + " fname=" + fname
                 + " existing=" + fragment);
         if (fragment == null) {
-            fragment = Fragment.instantiate(context, fname);
+            fragment = mContainer.instantiate(context, fname, null);
             fragment.mFromLayout = true;
             fragment.mFragmentId = id != 0 ? id : containerId;
             fragment.mContainerId = containerId;
diff --git a/core/java/android/app/FragmentTransition.java b/core/java/android/app/FragmentTransition.java
index 6d57cd4..80a5aac 100644
--- a/core/java/android/app/FragmentTransition.java
+++ b/core/java/android/app/FragmentTransition.java
@@ -188,7 +188,10 @@
     private static void configureTransitionsOptimized(FragmentManagerImpl fragmentManager,
             int containerId, FragmentContainerTransition fragments,
             View nonExistentView, ArrayMap<String, String> nameOverrides) {
-        ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        ViewGroup sceneRoot = null;
+        if (fragmentManager.mContainer.onHasView()) {
+            sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        }
         if (sceneRoot == null) {
             return;
         }
@@ -257,7 +260,10 @@
     private static void configureTransitionsUnoptimized(FragmentManagerImpl fragmentManager,
             int containerId, FragmentContainerTransition fragments,
             View nonExistentView, ArrayMap<String, String> nameOverrides) {
-        ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        ViewGroup sceneRoot = null;
+        if (fragmentManager.mContainer.onHasView()) {
+            sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+        }
         if (sceneRoot == null) {
             return;
         }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index c6f0b66..5b74e23 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -988,6 +988,32 @@
      */
     public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView";
 
+    /**
+     * {@link #extras} key: the audio contents of this notification.
+     *
+     * This is for use when rendering the notification on an audio-focused interface;
+     * the audio contents are a complete sound sample that contains the contents/body of the
+     * notification. This may be used in substitute of a Text-to-Speech reading of the
+     * notification. For example if the notification represents a voice message this should point
+     * to the audio of that message.
+     *
+     * The data stored under this key should be a String representation of a Uri that contains the
+     * audio contents in one of the following formats: WAV, PCM 16-bit, AMR-WB.
+     *
+     * This extra is unnecessary if you are using {@code MessagingStyle} since each {@code Message}
+     * has a field for holding data URI. That field can be used for audio.
+     * See {@code Message#setData}.
+     *
+     * Example usage:
+     * <pre>
+     * {@code
+     * Notification.Builder myBuilder = (build your Notification as normal);
+     * myBuilder.getExtras().putString(EXTRA_AUDIO_CONTENTS_URI, myAudioUri.toString());
+     * }
+     * </pre>
+     */
+    public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+
     /** @hide */
     @SystemApi
     public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
@@ -1007,6 +1033,21 @@
      * to attach actions.
      */
     public static class Action implements Parcelable {
+        /**
+         * {@link #extras} key: Keys to a {@link Parcelable} {@link ArrayList} of
+         * {@link RemoteInput}s.
+         *
+         * This is intended for {@link RemoteInput}s that only accept data, meaning
+         * {@link RemoteInput#getAllowFreeFormInput} is false, {@link RemoteInput#getChoices}
+         * is null or empty, and {@link RemoteInput#getAllowedDataTypes is non-null and not
+         * empty. These {@link RemoteInput}s will be ignored by devices that do not
+         * support non-text-based {@link RemoteInput}s. See {@link Builder#build}.
+         *
+         * You can test if a RemoteInput matches these constraints using
+         * {@link RemoteInput#isDataOnly}.
+         */
+        private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS";
+
         private final Bundle mExtras;
         private Icon mIcon;
         private final RemoteInput[] mRemoteInputs;
@@ -1097,13 +1138,28 @@
 
         /**
          * Get the list of inputs to be collected from the user when this action is sent.
-         * May return null if no remote inputs were added.
+         * May return null if no remote inputs were added. Only returns inputs which accept
+         * a text input. For inputs which only accept data use {@link #getDataOnlyRemoteInputs}.
          */
         public RemoteInput[] getRemoteInputs() {
             return mRemoteInputs;
         }
 
         /**
+         * Get the list of inputs to be collected from the user that ONLY accept data when this
+         * action is sent. These remote inputs are guaranteed to return true on a call to
+         * {@link RemoteInput#isDataOnly}.
+         *
+         * May return null if no data-only remote inputs were added.
+         *
+         * This method exists so that legacy RemoteInput collectors that pre-date the addition
+         * of non-textual RemoteInputs do not access these remote inputs.
+         */
+        public RemoteInput[] getDataOnlyRemoteInputs() {
+            return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
+        }
+
+        /**
          * Builder class for {@link Action} objects.
          */
         public static final class Builder {
@@ -1226,9 +1282,32 @@
              * @return the built action
              */
             public Action build() {
-                RemoteInput[] remoteInputs = mRemoteInputs != null
-                        ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
-                return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs,
+                ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
+                RemoteInput[] previousDataInputs =
+                    (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
+                if (previousDataInputs != null) {
+                    for (RemoteInput input : previousDataInputs) {
+                        dataOnlyInputs.add(input);
+                    }
+                }
+                List<RemoteInput> textInputs = new ArrayList<>();
+                if (mRemoteInputs != null) {
+                    for (RemoteInput input : mRemoteInputs) {
+                        if (input.isDataOnly()) {
+                            dataOnlyInputs.add(input);
+                        } else {
+                            textInputs.add(input);
+                        }
+                    }
+                }
+                if (!dataOnlyInputs.isEmpty()) {
+                    RemoteInput[] dataInputsArr =
+                            dataOnlyInputs.toArray(new RemoteInput[dataOnlyInputs.size()]);
+                    mExtras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, dataInputsArr);
+                }
+                RemoteInput[] textInputsArr = textInputs.isEmpty()
+                        ? null : textInputs.toArray(new RemoteInput[textInputs.size()]);
+                return new Action(mIcon, mTitle, mIntent, mExtras, textInputsArr,
                         mAllowGeneratedReplies);
             }
         }
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
index 11420c5..d1dc859 100644
--- a/core/java/android/app/RemoteInput.java
+++ b/core/java/android/app/RemoteInput.java
@@ -19,9 +19,14 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArraySet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * A {@code RemoteInput} object specifies input to be collected from a user to be passed along with
@@ -61,9 +66,13 @@
     /** Label used to denote the clip data type used for remote input transport */
     public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
 
-    /** Extra added to a clip data intent object to hold the results bundle. */
+    /** Extra added to a clip data intent object to hold the text results bundle. */
     public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
 
+    /** Extra added to a clip data intent object to hold the data results bundle. */
+    private static final String EXTRA_DATA_TYPE_RESULTS_DATA =
+            "android.remoteinput.dataTypeResultsData";
+
     // Flags bitwise-ored to mFlags
     private static final int FLAG_ALLOW_FREE_FORM_INPUT = 0x1;
 
@@ -75,14 +84,16 @@
     private final CharSequence[] mChoices;
     private final int mFlags;
     private final Bundle mExtras;
+    private final ArraySet<String> mAllowedDataTypes;
 
     private RemoteInput(String resultKey, CharSequence label, CharSequence[] choices,
-            int flags, Bundle extras) {
+            int flags, Bundle extras, ArraySet<String> allowedDataTypes) {
         this.mResultKey = resultKey;
         this.mLabel = label;
         this.mChoices = choices;
         this.mFlags = flags;
         this.mExtras = extras;
+        this.mAllowedDataTypes = allowedDataTypes;
     }
 
     /**
@@ -107,6 +118,21 @@
         return mChoices;
     }
 
+    public Set<String> getAllowedDataTypes() {
+        return mAllowedDataTypes;
+    }
+
+    /**
+     * Returns true if the input only accepts data, meaning {@link #getAllowFreeFormInput}
+     * is false, {@link #getChoices} is null or empty, and {@link #getAllowedDataTypes is
+     * non-null and not empty.
+     */
+    public boolean isDataOnly() {
+        return !getAllowFreeFormInput()
+                && (getChoices() == null || getChoices().length == 0)
+                && !getAllowedDataTypes().isEmpty();
+    }
+
     /**
      * Get whether or not users can provide an arbitrary value for
      * input. If you set this to {@code false}, users must select one of the
@@ -133,6 +159,7 @@
         private CharSequence[] mChoices;
         private int mFlags = DEFAULT_FLAGS;
         private Bundle mExtras = new Bundle();
+        private final ArraySet<String> mAllowedDataTypes = new ArraySet<>();
 
         /**
          * Create a builder object for {@link RemoteInput} objects.
@@ -177,14 +204,34 @@
         /**
          * Specifies whether the user can provide arbitrary values.
          *
-         * @param allowFreeFormInput The default is {@code true}.
-         *         If you specify {@code false}, you must provide a non-null
-         *         and non-empty array to {@link #setChoices} or an
+         * @param mimeType A mime type that results are allowed to come in.
+         *         Be aware that text results (see {@link #setAllowFreeFormInput}
+         *         are allowed by default. If you do not want text results you will have to
+         *         pass false to {@code setAllowFreeFormInput}.
+         * @param doAllow Whether the mime type should be allowed or not.
+         * @return this object for method chaining
+         */
+        public Builder setAllowDataType(String mimeType, boolean doAllow) {
+            if (doAllow) {
+                mAllowedDataTypes.add(mimeType);
+            } else {
+                mAllowedDataTypes.remove(mimeType);
+            }
+            return this;
+        }
+
+        /**
+         * Specifies whether the user can provide arbitrary text values.
+         *
+         * @param allowFreeFormTextInput The default is {@code true}.
+         *         If you specify {@code false}, you must either provide a non-null
+         *         and non-empty array to {@link #setChoices}, or enable a data result
+         *         in {@code setAllowDataType}. Otherwise an
          *         {@link IllegalArgumentException} is thrown.
          * @return this object for method chaining
          */
-        public Builder setAllowFreeFormInput(boolean allowFreeFormInput) {
-            setFlag(mFlags, allowFreeFormInput);
+        public Builder setAllowFreeFormInput(boolean allowFreeFormTextInput) {
+            setFlag(mFlags, allowFreeFormTextInput);
             return this;
         }
 
@@ -224,7 +271,8 @@
          * object.
          */
         public RemoteInput build() {
-            return new RemoteInput(mResultKey, mLabel, mChoices, mFlags, mExtras);
+            return new RemoteInput(
+                    mResultKey, mLabel, mChoices, mFlags, mExtras, mAllowedDataTypes);
         }
     }
 
@@ -234,32 +282,68 @@
         mChoices = in.readCharSequenceArray();
         mFlags = in.readInt();
         mExtras = in.readBundle();
+        mAllowedDataTypes = (ArraySet<String>) in.readArraySet(null);
     }
 
     /**
-     * Get the remote input results bundle from an intent. The returned Bundle will
-     * contain a key/value for every result key populated by remote input collector.
-     * Use the {@link Bundle#getCharSequence(String)} method to retrieve a value.
+     * Similar as {@link #getResultsFromIntent} but retrieves data results for a
+     * specific RemoteInput result. To retrieve a value use:
+     * <pre>
+     * {@code
+     * Map<String, Uri> results =
+     *     RemoteInput.getDataResultsFromIntent(intent, REMOTE_INPUT_KEY);
+     * if (results != null) {
+     *   Uri data = results.get(MIME_TYPE_OF_INTEREST);
+     * }
+     * }
+     * </pre>
+     * @param intent The intent object that fired in response to an action or content intent
+     *               which also had one or more remote input requested.
+     * @param remoteInputResultKey The result key for the RemoteInput you want results for.
+     */
+    public static Map<String, Uri> getDataResultsFromIntent(
+            Intent intent, String remoteInputResultKey) {
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
+            return null;
+        }
+        Map<String, Uri> results = new HashMap<>();
+        Bundle extras = clipDataIntent.getExtras();
+        for (String key : extras.keySet()) {
+          if (key.startsWith(EXTRA_DATA_TYPE_RESULTS_DATA)) {
+              String mimeType = key.substring(EXTRA_DATA_TYPE_RESULTS_DATA.length());
+              if (mimeType == null || mimeType.isEmpty()) {
+                  continue;
+              }
+              Bundle bundle = clipDataIntent.getBundleExtra(key);
+              String uriStr = bundle.getString(remoteInputResultKey);
+              if (uriStr == null || uriStr.isEmpty()) {
+                  continue;
+              }
+              results.put(mimeType, Uri.parse(uriStr));
+          }
+        }
+        return results.isEmpty() ? null : results;
+    }
+
+    /**
+     * Get the remote input text results bundle from an intent. The returned Bundle will
+     * contain a key/value for every result key populated with text by remote input collector.
+     * Use the {@link Bundle#getCharSequence(String)} method to retrieve a value. For non-text
+     * results use {@link #getDataResultsFromIntent}.
      * @param intent The intent object that fired in response to an action or content intent
      *               which also had one or more remote input requested.
      */
     public static Bundle getResultsFromIntent(Intent intent) {
-        ClipData clipData = intent.getClipData();
-        if (clipData == null) {
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
             return null;
         }
-        ClipDescription clipDescription = clipData.getDescription();
-        if (!clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
-            return null;
-        }
-        if (clipDescription.getLabel().equals(RESULTS_CLIP_LABEL)) {
-            return clipData.getItemAt(0).getIntent().getExtras().getParcelable(EXTRA_RESULTS_DATA);
-        }
-        return null;
+        return clipDataIntent.getExtras().getParcelable(EXTRA_RESULTS_DATA);
     }
 
     /**
-     * Populate an intent object with the results gathered from remote input. This method
+     * Populate an intent object with the text results gathered from remote input. This method
      * should only be called by remote input collection services when sending results to a
      * pending intent.
      * @param remoteInputs The remote inputs for which results are being provided
@@ -267,20 +351,61 @@
      *               field of the intent will be modified to contain the results.
      * @param results A bundle holding the remote input results. This bundle should
      *                be populated with keys matching the result keys specified in
-     *                {@code remoteInputs} with values being the result per key.
+     *                {@code remoteInputs} with values being the CharSequence results per key.
      */
     public static void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent,
             Bundle results) {
-        Bundle resultsBundle = new Bundle();
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
+            clipDataIntent = new Intent();  // First time we've added a result.
+        }
+        Bundle resultsBundle = clipDataIntent.getBundleExtra(EXTRA_RESULTS_DATA);
+        if (resultsBundle == null) {
+            resultsBundle = new Bundle();
+        }
         for (RemoteInput remoteInput : remoteInputs) {
             Object result = results.get(remoteInput.getResultKey());
             if (result instanceof CharSequence) {
                 resultsBundle.putCharSequence(remoteInput.getResultKey(), (CharSequence) result);
             }
         }
-        Intent clipIntent = new Intent();
-        clipIntent.putExtra(EXTRA_RESULTS_DATA, resultsBundle);
-        intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipIntent));
+        clipDataIntent.putExtra(EXTRA_RESULTS_DATA, resultsBundle);
+        intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent));
+    }
+
+    /**
+     * Same as {@link #addResultsToIntent} but for setting data results.
+     * @param remoteInput The remote input for which results are being provided
+     * @param intent The intent to add remote input results to. The {@link ClipData}
+     *               field of the intent will be modified to contain the results.
+     * @param results A map of mime type to the Uri result for that mime type.
+     */
+    public static void addDataResultToIntent(RemoteInput remoteInput, Intent intent,
+            Map<String, Uri> results) {
+        Intent clipDataIntent = getClipDataIntentFromIntent(intent);
+        if (clipDataIntent == null) {
+            clipDataIntent = new Intent();  // First time we've added a result.
+        }
+        for (Map.Entry<String, Uri> entry : results.entrySet()) {
+            String mimeType = entry.getKey();
+            Uri uri = entry.getValue();
+            if (mimeType == null) {
+                continue;
+            }
+            Bundle resultsBundle =
+                    clipDataIntent.getBundleExtra(getExtraResultsKeyForData(mimeType));
+            if (resultsBundle == null) {
+                resultsBundle = new Bundle();
+            }
+            resultsBundle.putString(remoteInput.getResultKey(), uri.toString());
+
+            clipDataIntent.putExtra(getExtraResultsKeyForData(mimeType), resultsBundle);
+        }
+        intent.setClipData(ClipData.newIntent(RESULTS_CLIP_LABEL, clipDataIntent));
+    }
+
+    private static String getExtraResultsKeyForData(String mimeType) {
+        return EXTRA_DATA_TYPE_RESULTS_DATA + mimeType;
     }
 
     @Override
@@ -295,6 +420,7 @@
         out.writeCharSequenceArray(mChoices);
         out.writeInt(mFlags);
         out.writeBundle(mExtras);
+        out.writeArraySet(mAllowedDataTypes);
     }
 
     public static final Creator<RemoteInput> CREATOR = new Creator<RemoteInput>() {
@@ -308,4 +434,19 @@
             return new RemoteInput[size];
         }
     };
+
+    private static Intent getClipDataIntentFromIntent(Intent intent) {
+        ClipData clipData = intent.getClipData();
+        if (clipData == null) {
+            return null;
+        }
+        ClipDescription clipDescription = clipData.getDescription();
+        if (!clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
+            return null;
+        }
+        if (!clipDescription.getLabel().equals(RESULTS_CLIP_LABEL)) {
+            return null;
+        }
+        return clipData.getItemAt(0).getIntent();
+    }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c95e011..b8b9793 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6317,7 +6317,7 @@
      * @see #setPermissionGrantState(ComponentName, String, String, int)
      * @see PackageManager#checkPermission(String, String)
      */
-    public int getPermissionGrantState(@NonNull ComponentName admin, String packageName,
+    public int getPermissionGrantState(@Nullable ComponentName admin, String packageName,
             String permission) {
         throwIfParentInstance("getPermissionGrantState");
         try {
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 4b6076b..e437de0 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -653,6 +653,13 @@
         return mBase.bindServiceAsUser(service, conn, flags, user);
     }
 
+    /** @hide */
+    @Override
+    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
+            Handler handler, UserHandle user) {
+        return mBase.bindServiceAsUser(service, conn, flags, handler, user);
+    }
+
     @Override
     public void unbindService(ServiceConnection conn) {
         mBase.unbindService(conn);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d8358f9..8cc9a3a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -715,11 +715,13 @@
     /**
      * Activity Action: Creates a shortcut.
      * <p>Input: Nothing.</p>
-     * <p>Output: An Intent representing the shortcut. The intent must contain three
+     * <p>Output: An Intent representing the {@link android.content.pm.ShortcutInfo} result.</p>
+     * <p>For compatibility with older versions of android the intent may also contain three
      * extras: SHORTCUT_INTENT (value: Intent), SHORTCUT_NAME (value: String),
      * and SHORTCUT_ICON (value: Bitmap) or SHORTCUT_ICON_RESOURCE
      * (value: ShortcutIconResource).</p>
      *
+     * @see android.content.pm.ShortcutManager#createShortcutResultIntent
      * @see #EXTRA_SHORTCUT_INTENT
      * @see #EXTRA_SHORTCUT_NAME
      * @see #EXTRA_SHORTCUT_ICON
@@ -733,26 +735,34 @@
      * The name of the extra used to define the Intent of a shortcut.
      *
      * @see #ACTION_CREATE_SHORTCUT
+     * @deprecated Replaced with {@link android.content.pm.ShortcutManager#createShortcutResultIntent}
      */
+    @Deprecated
     public static final String EXTRA_SHORTCUT_INTENT = "android.intent.extra.shortcut.INTENT";
     /**
      * The name of the extra used to define the name of a shortcut.
      *
      * @see #ACTION_CREATE_SHORTCUT
+     * @deprecated Replaced with {@link android.content.pm.ShortcutManager#createShortcutResultIntent}
      */
+    @Deprecated
     public static final String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
     /**
      * The name of the extra used to define the icon, as a Bitmap, of a shortcut.
      *
      * @see #ACTION_CREATE_SHORTCUT
+     * @deprecated Replaced with {@link android.content.pm.ShortcutManager#createShortcutResultIntent}
      */
+    @Deprecated
     public static final String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
     /**
      * The name of the extra used to define the icon, as a ShortcutIconResource, of a shortcut.
      *
      * @see #ACTION_CREATE_SHORTCUT
      * @see android.content.Intent.ShortcutIconResource
+     * @deprecated Replaced with {@link android.content.pm.ShortcutManager#createShortcutResultIntent}
      */
+    @Deprecated
     public static final String EXTRA_SHORTCUT_ICON_RESOURCE =
             "android.intent.extra.shortcut.ICON_RESOURCE";
 
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 430c7e7..5152416 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IOnAppsChangedListener;
@@ -60,4 +61,8 @@
             int userId);
 
     boolean hasShortcutHostPermission(String callingPackage);
+
+    ParceledListSlice getShortcutConfigActivities(String packageName, in UserHandle user);
+    IntentSender getShortcutConfigActivityIntent(String callingPackage, in ComponentName component,
+            in UserHandle user);
 }
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 91df8e8..c90134a 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -15,6 +15,7 @@
  */
 package android.content.pm;
 
+import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
@@ -45,6 +46,8 @@
     boolean requestPinShortcut(String packageName, in ShortcutInfo shortcut,
             in IntentSender resultIntent, int userId);
 
+    Intent createShortcutResultIntent(String packageName, in ShortcutInfo shortcut, int userId);
+
     void disableShortcuts(String packageName, in List shortcutIds, CharSequence disabledMessage,
             int disabledMessageResId, int userId);
 
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 4cdd653..cf873b0 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -27,6 +27,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.PackageManager.ApplicationInfoFlags;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
@@ -384,25 +385,11 @@
      * @return List of launchable activities. Can be an empty list but will not be null.
      */
     public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
-        ParceledListSlice<ResolveInfo> activities = null;
         try {
-            activities = mService.getLauncherActivities(packageName, user);
+            return convertToActivityList(mService.getLauncherActivities(packageName, user), user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
-        if (activities == null) {
-            return Collections.EMPTY_LIST;
-        }
-        ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
-        for (ResolveInfo ri : activities.getList()) {
-            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
-            if (DEBUG) {
-                Log.v(TAG, "Returning activity for profile " + user + " : "
-                        + lai.getComponentName());
-            }
-            lais.add(lai);
-        }
-        return lais;
     }
 
     /**
@@ -465,6 +452,73 @@
     }
 
     /**
+     * Retrieves a list of config activities for creating {@link ShortcutInfo}.
+     *
+     * @param packageName The specific package to query. If null, it checks all installed packages
+     *            in the profile.
+     * @param user The UserHandle of the profile.
+     * @return List of config activities. Can be an empty list but will not be null.
+     *
+     * @see Intent#ACTION_CREATE_SHORTCUT
+     * @see #getShortcutConfigActivityIntent(LauncherActivityInfo)
+     */
+    public List<LauncherActivityInfo> getShortcutConfigActivityList(@Nullable String packageName,
+            @NonNull UserHandle user) {
+        try {
+            return convertToActivityList(mService.getShortcutConfigActivities(packageName, user),
+                    user);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    private List<LauncherActivityInfo> convertToActivityList(
+            @Nullable ParceledListSlice<ResolveInfo> activities, UserHandle user) {
+        if (activities == null) {
+            return Collections.EMPTY_LIST;
+        }
+        ArrayList<LauncherActivityInfo> lais = new ArrayList<>();
+        for (ResolveInfo ri : activities.getList()) {
+            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
+            if (DEBUG) {
+                Log.v(TAG, "Returning activity for profile " + user + " : "
+                        + lai.getComponentName());
+            }
+            lais.add(lai);
+        }
+        return lais;
+    }
+
+    /**
+     * Returns an intent sender which can be used to start the configure activity for creating
+     * custom shortcuts. Use this method if the provider is in another profile as you are not
+     * allowed to start an activity in another profile.
+     *
+     * <p>The caller should receive {@link PinItemRequest} in onActivityResult on
+     * {@link android.app.Activity#RESULT_OK}.
+     *
+     * <p>Callers must be allowed to access the shortcut information, as defined in {@link
+     * #hasShortcutHostPermission()}.
+     *
+     * @param info a configuration activity returned by {@link #getShortcutConfigActivityList}
+     *
+     * @throws IllegalStateException when the user is locked or not running.
+     * @throws SecurityException if {@link #hasShortcutHostPermission()} is false.
+     *
+     * @see #getPinItemRequest(Intent)
+     * @see Intent#ACTION_CREATE_SHORTCUT
+     * @see android.app.Activity#startIntentSenderForResult
+     */
+    public IntentSender getShortcutConfigActivityIntent(@NonNull LauncherActivityInfo info) {
+        try {
+            return mService.getShortcutConfigActivityIntent(
+                    mContext.getPackageName(), info.getComponentName(), info.getUser());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Checks if the package is installed and enabled for a profile.
      *
      * @param packageName The package to check.
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 3853400..805054f 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -881,6 +881,31 @@
     }
 
     /**
+     * Returns an Intent which can be used by the default launcher to pin {@param shortcut}.
+     * This should be used by an Activity to set result in response to
+     * {@link Intent#ACTION_CREATE_SHORTCUT}.
+     *
+     * @param shortcut New shortcut to pin.  If an app wants to pin an existing (either dynamic
+     *     or manifest) shortcut, then it only needs to have an ID, and other fields don't have to
+     *     be set, in which case, the target shortcut must be enabled.
+     *     If it's a new shortcut, all the mandatory fields, such as a short label, must be
+     *     set.
+     * @return The intent that should be set as the result for the calling activity or null.
+     *
+     * @see Intent#ACTION_CREATE_SHORTCUT
+     *
+     * @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled.
+     */
+    public Intent createShortcutResultIntent(@NonNull ShortcutInfo shortcut) {
+        try {
+            return mService.createShortcutResultIntent(mContext.getPackageName(), shortcut,
+                    injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Called internally when an app is considered to have come to the foreground
      * even when technically it's not.  This method resets the throttling for this package.
      * For example, when the user sends an "inline reply" on a notification, the system UI will
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 04ee1e6..3ccac69 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -492,7 +492,7 @@
         if (type == Sensor.TYPE_PROXIMITY || type == Sensor.TYPE_SIGNIFICANT_MOTION ||
                 type == Sensor.TYPE_TILT_DETECTOR || type == Sensor.TYPE_WAKE_GESTURE ||
                 type == Sensor.TYPE_GLANCE_GESTURE || type == Sensor.TYPE_PICK_UP_GESTURE ||
-                type == Sensor.TYPE_WRIST_TILT_GESTURE) {
+                type == Sensor.TYPE_WRIST_TILT_GESTURE || type == Sensor.TYPE_DYNAMIC_SENSOR_META) {
             wakeUpSensor = true;
         }
 
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index c704ef0..0775bda 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -24,8 +24,10 @@
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Build;
+import android.service.NetworkIdentityProto;
 import android.telephony.TelephonyManager;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import java.util.Objects;
 
@@ -110,6 +112,23 @@
         return builder.append("}").toString();
     }
 
+    public void writeToProto(ProtoOutputStream proto, long tag) {
+        final long start = proto.start(tag);
+
+        proto.write(NetworkIdentityProto.TYPE, mType);
+
+        // Not dumping mSubType, subtypes are no longer supported.
+
+        if (mSubscriberId != null) {
+            proto.write(NetworkIdentityProto.SUBSCRIBER_ID, scrubSubscriberId(mSubscriberId));
+        }
+        proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
+        proto.write(NetworkIdentityProto.ROAMING, mRoaming);
+        proto.write(NetworkIdentityProto.METERED, mMetered);
+
+        proto.end(start);
+    }
+
     public int getType() {
         return mType;
     }
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 7a832d1..57cf1a5 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -21,6 +21,7 @@
 import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
@@ -36,6 +37,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.CompletableFuture;
 
 /**
  * Class that manages communication between network subsystems and a network scorer.
@@ -370,25 +372,26 @@
      *
      * @param request a {@link RecommendationRequest} instance containing additional
      *                request details
-     * @param callback a {@link RecommendationCallback} instance that will be invoked when
-     *                 the {@link RecommendationResult} is available
-     * @param handler a {@link Handler} instance representing the thread to run the callback on.
+     * @param handler a {@link Handler} instance representing the thread to complete the future on.
+     *                If null the responding binder thread will be used.
+     * @return a {@link CompletableFuture} instance that will eventually receive the
+     *         {@link RecommendationResult}.
      * @throws SecurityException
      * @hide
      */
-    public void requestRecommendation(
+    public CompletableFuture<RecommendationResult> requestRecommendation(
             final @NonNull RecommendationRequest request,
-            final @NonNull RecommendationCallback callback,
-            final @NonNull Handler handler) {
+            final @Nullable Handler handler) {
         Preconditions.checkNotNull(request, "RecommendationRequest cannot be null.");
-        Preconditions.checkNotNull(callback, "RecommendationCallback cannot be null.");
-        Preconditions.checkNotNull(handler, "Handler cannot be null.");
+
+        final CompletableFuture<RecommendationResult> futureResult =
+                new CompletableFuture<>();
 
         RemoteCallback remoteCallback = new RemoteCallback(new RemoteCallback.OnResultListener() {
             @Override
             public void onResult(Bundle data) {
                 RecommendationResult result = data.getParcelable(EXTRA_RECOMMENDATION_RESULT);
-                callback.onRecommendationAvailable(result);
+                futureResult.complete(result);
             }
         }, handler);
 
@@ -397,18 +400,7 @@
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-    }
 
-    /**
-     * Callers of {@link #requestRecommendation(RecommendationRequest, RecommendationCallback, Handler)}
-     * must pass in an implementation of this class.
-     * @hide
-     */
-    public abstract static class RecommendationCallback {
-        /**
-         * Invoked when a {@link RecommendationResult} is available.
-         * @param result a {@link RecommendationResult} instance.
-         */
-        public abstract void onRecommendationAvailable(RecommendationResult result);
+        return futureResult;
     }
 }
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index 4a4accb..5f521de 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -31,7 +31,10 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.service.NetworkStatsHistoryBucketProto;
+import android.service.NetworkStatsHistoryProto;
 import android.util.MathUtils;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.IndentingPrintWriter;
 
@@ -628,6 +631,33 @@
         }
     }
 
+    public void writeToProto(ProtoOutputStream proto, long tag) {
+        final long start = proto.start(tag);
+
+        proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS, bucketDuration);
+
+        for (int i = 0; i < bucketCount; i++) {
+            final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS);
+
+            proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS, bucketStart[i]);
+            writeToProto(proto, NetworkStatsHistoryBucketProto.RX_BYTES, rxBytes, i);
+            writeToProto(proto, NetworkStatsHistoryBucketProto.RX_PACKETS, rxPackets, i);
+            writeToProto(proto, NetworkStatsHistoryBucketProto.TX_BYTES, txBytes, i);
+            writeToProto(proto, NetworkStatsHistoryBucketProto.TX_PACKETS, txPackets, i);
+            writeToProto(proto, NetworkStatsHistoryBucketProto.OPERATIONS, operations, i);
+
+            proto.end(startBucket);
+        }
+
+        proto.end(start);
+    }
+
+    private static void writeToProto(ProtoOutputStream proto, long tag, long[] array, int index) {
+        if (array != null) {
+            proto.write(tag, array[index]);
+        }
+    }
+
     @Override
     public String toString() {
         final CharArrayWriter writer = new CharArrayWriter();
diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index 1ee83ae..c5ceecd 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -25,5 +25,5 @@
     boolean uncrypt(in String packageFile, IRecoverySystemProgressListener listener);
     boolean setupBcb(in String command);
     boolean clearBcb();
-    void rebootRecoveryWithCommand(in String command, in boolean update);
+    void rebootRecoveryWithCommand(in String command);
 }
diff --git a/core/java/android/os/IProxyFileDescriptorCallback.java b/core/java/android/os/ProxyFileDescriptorCallback.java
similarity index 69%
rename from core/java/android/os/IProxyFileDescriptorCallback.java
rename to core/java/android/os/ProxyFileDescriptorCallback.java
index e41e194..2e9f8d9 100644
--- a/core/java/android/os/IProxyFileDescriptorCallback.java
+++ b/core/java/android/os/ProxyFileDescriptorCallback.java
@@ -17,18 +17,20 @@
 package android.os;
 
 import android.system.ErrnoException;
+import android.system.OsConstants;
 
 /**
  * Callback that handles file system requests from ProxyFileDescriptor.
- * @hide
  */
-public interface IProxyFileDescriptorCallback {
+public abstract class ProxyFileDescriptorCallback {
     /**
      * Returns size of bytes provided by the file descriptor.
      * @return Size of bytes
      * @throws ErrnoException
      */
-    long onGetSize() throws ErrnoException;
+    public long onGetSize() throws ErrnoException {
+        throw new ErrnoException("onGetSize", OsConstants.EBADF);
+    }
 
     /**
      * Provides bytes read from file descriptor.
@@ -39,7 +41,9 @@
      * @return Size of bytes returned by the function.
      * @throws ErrnoException
      */
-    int onRead(long offset, int size, byte[] data) throws ErrnoException;
+    public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+        throw new ErrnoException("onRead", OsConstants.EBADF);
+    }
 
     /**
      * Handles bytes written to file descriptor.
@@ -49,11 +53,20 @@
      * @return Size of bytes processed by the function.
      * @throws ErrnoException
      */
-    int onWrite(long offset, int size, byte[] data) throws ErrnoException;
+    public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+        throw new ErrnoException("onWrite", OsConstants.EBADF);
+    }
 
     /**
      * Processes fsync request.
      * @throws ErrnoException
      */
-    void onFsync() throws ErrnoException;
+    public void onFsync() throws ErrnoException {
+        throw new ErrnoException("onFsync", OsConstants.EINVAL);
+    }
+
+    /**
+     * Invoked after the file is closed.
+     */
+    abstract public void onRelease();
 }
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 7f9ea438..d48431a 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -491,10 +491,15 @@
                 command += securityArg;
             }
 
-            // RECOVERY_SERVICE writes to BCB (bootloader control block) and triggers the reboot.
             RecoverySystem rs = (RecoverySystem) context.getSystemService(
                     Context.RECOVERY_SERVICE);
-            rs.rebootRecoveryWithCommand(command, true /* update */);
+            if (!rs.setupBcb(command)) {
+                throw new IOException("Setup BCB failed");
+            }
+
+            // Having set up the BCB (bootloader control block), go ahead and reboot
+            PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+            pm.reboot(PowerManager.REBOOT_RECOVERY_UPDATE);
 
             throw new IOException("Reboot failed (no permissions?)");
         }
@@ -708,7 +713,7 @@
         // Write the command into BCB (bootloader control block) and boot from
         // there. Will not return unless failed.
         RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
-        rs.rebootRecoveryWithCommand(command.toString(), false);
+        rs.rebootRecoveryWithCommand(command.toString());
 
         throw new IOException("Reboot failed (no permissions?)");
     }
@@ -908,9 +913,9 @@
      * Talks to RecoverySystemService via Binder to set up the BCB command and
      * reboot into recovery accordingly.
      */
-    private void rebootRecoveryWithCommand(String command, boolean update) {
+    private void rebootRecoveryWithCommand(String command) {
         try {
-            mService.rebootRecoveryWithCommand(command, update);
+            mService.rebootRecoveryWithCommand(command);
         } catch (RemoteException ignored) {
         }
     }
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 27c0526..59394b2 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -25,6 +25,7 @@
 import android.os.storage.StorageVolume;
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
+import com.android.internal.os.AppFuseMount;
 
 /**
  * WARNING! Update IMountService.h and IMountService.cpp if you change this
@@ -289,4 +290,6 @@
     void addUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 70;
     void fixateNewestUserKeyAuth(int userId) = 71;
     void fstrim(int flags) = 72;
+    AppFuseMount mountProxyFileDescriptorBridge() = 73;
+    ParcelFileDescriptor openProxyFileDescriptor(int mountPointId, int fileId, int mode) = 74;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 03fd8d3..85df48f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -30,6 +30,7 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
+import android.os.ProxyFileDescriptorCallback;
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
@@ -44,7 +45,10 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
-
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.AppFuseMount;
+import com.android.internal.os.FuseAppLoop;
 import com.android.internal.os.RoSystemProperties;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
@@ -62,6 +66,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -1322,6 +1327,90 @@
         }
     }
 
+
+    /** {@hide} */
+    @VisibleForTesting
+    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
+            int mode, ProxyFileDescriptorCallback callback, ThreadFactory factory)
+                    throws IOException {
+        // Retry is needed because the mount point mFuseAppLoop is using may be unmounted before
+        // invoking StorageManagerService#openProxyFileDescriptor. In this case, we need to re-mount
+        // the bridge by calling mountProxyFileDescriptorBridge.
+        int retry = 3;
+        while (retry-- > 0) {
+            try {
+                synchronized (mFuseAppLoopLock) {
+                    if (mFuseAppLoop == null) {
+                        final AppFuseMount mount = mStorageManager.mountProxyFileDescriptorBridge();
+                        if (mount == null) {
+                            Log.e(TAG, "Failed to open proxy file bridge.");
+                            throw new IOException("Failed to open proxy file bridge.");
+                        }
+                        mFuseAppLoop = FuseAppLoop.open(mount.mountPointId, mount.fd, factory);
+                    }
+
+                    try {
+                        final int fileId = mFuseAppLoop.registerCallback(callback);
+                        final ParcelFileDescriptor pfd =
+                                mStorageManager.openProxyFileDescriptor(
+                                        mFuseAppLoop.getMountPointId(), fileId, mode);
+                        if (pfd != null) {
+                            return pfd;
+                        }
+                        // Probably the bridge is being unmounted but mFuseAppLoop has not been
+                        // noticed it yet.
+                        mFuseAppLoop.unregisterCallback(fileId);
+                    } catch (FuseAppLoop.UnmountedException error) {
+                        Log.d(TAG, "mFuseAppLoop has been already unmounted.");
+                        mFuseAppLoop = null;
+                        continue;
+                    }
+                }
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                    break;
+                }
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
+
+        throw new IOException("Failed to mount bridge.");
+    }
+
+    /**
+     * Opens seekable ParcelFileDescriptor that routes file operation requests to
+     * ProxyFileDescriptorCallback.
+     *
+     * @param mode The desired access mode, must be one of
+     *     {@link ParcelFileDescriptor#MODE_READ_ONLY},
+     *     {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or
+     *     {@link ParcelFileDescriptor#MODE_READ_WRITE}
+     * @param callback Callback to process file operation requests issued on returned file
+     *     descriptor. The callback is invoked on a thread managed by the framework.
+     * @return Seekable ParcelFileDescriptor.
+     * @throws IOException
+     */
+    public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
+            int mode, ProxyFileDescriptorCallback callback)
+                    throws IOException {
+        return openProxyFileDescriptor(mode, callback, null);
+    }
+
+    /** {@hide} */
+    @VisibleForTesting
+    public int getProxyFileDescriptorMountPointId() {
+        synchronized (mFuseAppLoopLock) {
+            return mFuseAppLoop != null ? mFuseAppLoop.getMountPointId() : -1;
+        }
+    }
+
+    private final Object mFuseAppLoopLock = new Object();
+
+    @GuardedBy("mFuseAppLoopLock")
+    private @Nullable FuseAppLoop mFuseAppLoop = null;
+
     /// Consts to match the password types in cryptfs.h
     /** @hide */
     public static final int CRYPT_TYPE_PASSWORD = 0;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 893e53c..371c0f3 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5313,6 +5313,21 @@
         public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled";
 
         /**
+         * Setting specifying if the accessibility shortcut dialog has been shown to this user.
+         * @hide
+         */
+        public static final String ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN =
+                "accessibility_shortcut_dialog_shown";
+
+        /**
+         * Setting specifying the the accessibility service to be toggled via the accessibility
+         * shortcut. Must be its flattened {@link ComponentName}.
+         * @hide
+         */
+        public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE =
+                "accessibility_shortcut_target_service";
+
+        /**
          * If touch exploration is enabled.
          */
         public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
@@ -6782,6 +6797,8 @@
             TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
             TOUCH_EXPLORATION_ENABLED,
             ACCESSIBILITY_ENABLED,
+            ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+            ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
             ACCESSIBILITY_SPEAK_PASSWORD,
             ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
             ACCESSIBILITY_CAPTIONING_PRESET,
@@ -7071,7 +7088,9 @@
          * Setting whether the global gesture for enabling accessibility is enabled.
          * If this gesture is enabled the user will be able to perfrom it to enable
          * the accessibility state without visiting the settings app.
+         *
          * @hide
+         * No longer used. Should be removed once all dependencies have been updated.
          */
         public static final String ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED =
                 "enable_accessibility_global_gesture_enabled";
@@ -9372,7 +9391,6 @@
             DOCK_SOUNDS_ENABLED,
             CHARGING_SOUNDS_ENABLED,
             USB_MASS_STORAGE_ENABLED,
-            ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED,
             WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
             WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
             WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java
index c2e980c..805d8e5 100644
--- a/core/java/android/service/autofill/AutoFillService.java
+++ b/core/java/android/service/autofill/AutoFillService.java
@@ -61,6 +61,19 @@
     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     public static final String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
 
+    /**
+     * Name under which a AutoFillService component publishes information about itself.
+     * This meta-data should reference an XML resource containing a
+     * <code>&lt;{@link
+     * android.R.styleable#AutoFillService autofill-service}&gt;</code> tag.
+     * This is a a sample XML file configuring an AutoFillService:
+     * <pre> &lt;autofill-service
+     *     android:settingsActivity="foo.bar.SettingsActivity"
+     *     . . .
+     * /&gt;</pre>
+     */
+    public static final String SERVICE_META_DATA = "android.autofill";
+
     // Internal bundle keys.
     /** @hide */ public static final String KEY_CALLBACK = "callback";
     /** @hide */ public static final String KEY_SAVABLE_IDS = "savable_ids";
diff --git a/core/java/android/service/autofill/AutoFillServiceInfo.java b/core/java/android/service/autofill/AutoFillServiceInfo.java
index fe21615..ab86580 100644
--- a/core/java/android/service/autofill/AutoFillServiceInfo.java
+++ b/core/java/android/service/autofill/AutoFillServiceInfo.java
@@ -16,20 +16,34 @@
 package android.service.autofill;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
 
 /** @hide */
 public final class AutoFillServiceInfo {
+    static final String TAG = "AutoFillServiceInfo";
 
     private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle)
             throws PackageManager.NameNotFoundException {
         try {
-            final ServiceInfo si =
-                    AppGlobals.getPackageManager().getServiceInfo(comp, 0, userHandle);
+            ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(
+                    comp,
+                    PackageManager.GET_META_DATA,
+                    userHandle);
             if (si != null) {
                 return si;
             }
@@ -38,11 +52,21 @@
         throw new PackageManager.NameNotFoundException(comp.toString());
     }
 
+    @Nullable
     private String mParseError;
 
+    @Nullable
     private ServiceInfo mServiceInfo;
+    @Nullable
+    private String mSettingsActivity;
 
-    private  AutoFillServiceInfo(ServiceInfo si) {
+    public AutoFillServiceInfo(PackageManager pm, ComponentName comp, int userHandle)
+            throws PackageManager.NameNotFoundException {
+        this(pm, getServiceInfoOrThrow(comp, userHandle));
+    }
+
+    public AutoFillServiceInfo(PackageManager pm, ServiceInfo si)
+            throws PackageManager.NameNotFoundException{
         if (si == null) {
             mParseError = "Service not available";
             return;
@@ -53,19 +77,57 @@
             return;
         }
 
+        XmlResourceParser parser = null;
+        try {
+            parser = si.loadXmlMetaData(pm, AutoFillService.SERVICE_META_DATA);
+            if (parser == null) {
+                mParseError = "No " + AutoFillService.SERVICE_META_DATA
+                        + " meta-data for " + si.packageName;
+                return;
+            }
+
+            Resources res = pm.getResourcesForApplication(si.applicationInfo);
+
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+            }
+
+            String nodeName = parser.getName();
+            if (!"autofill-service".equals(nodeName)) {
+                mParseError = "Meta-data does not start with autofill-service tag";
+                return;
+            }
+
+            TypedArray array = res.obtainAttributes(attrs,
+                    com.android.internal.R.styleable.AutoFillService);
+            mSettingsActivity = array.getString(
+                    com.android.internal.R.styleable.AutoFillService_settingsActivity);
+            array.recycle();
+        } catch (XmlPullParserException | IOException | PackageManager.NameNotFoundException e) {
+            mParseError = "Error parsing auto fill service meta-data: " + e;
+            Log.w(TAG, "error parsing auto fill service meta-data", e);
+            return;
+        } finally {
+            if (parser != null) parser.close();
+        }
         mServiceInfo = si;
     }
 
-    public AutoFillServiceInfo(ComponentName comp, int userHandle)
-            throws PackageManager.NameNotFoundException {
-        this(getServiceInfoOrThrow(comp, userHandle));
-    }
-
+    @Nullable
     public String getParseError() {
         return mParseError;
     }
 
+    @Nullable
     public ServiceInfo getServiceInfo() {
         return mServiceInfo;
     }
+
+    @Nullable
+    public String getSettingsActivity() {
+        return mSettingsActivity;
+    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9d1af50..37dfdb9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6934,8 +6934,7 @@
                 & (View.AUTO_FILL_FLAG_TYPE_FILL
                         | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0;
         final int id = mID;
-        if (id > 0 && (id&0xff000000) != 0 && (id&0x00ff0000) != 0
-                && (id&0x0000ffff) != 0) {
+        if (id != NO_ID && !isViewIdGenerated(id)) {
             String pkg, type, entry;
             try {
                 final Resources res = getResources();
@@ -22640,6 +22639,10 @@
         }
     }
 
+    private static boolean isViewIdGenerated(int id) {
+        return (id & 0xFF000000) == 0 && (id & 0x00FFFFFF) != 0;
+    }
+
     /**
      * Gets the Views in the hierarchy affected by entering and exiting Activity Scene transitions.
      * @param transitioningViews This View will be added to transitioningViews if it is VISIBLE and
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 6d2f850..0e753f3 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -83,6 +83,13 @@
     private static final int GLOBAL_ACTIONS_KEY_TIMEOUT = 500;
 
     /**
+     * Defines the duration in milliseconds a user needs to hold down the
+     * appropriate button to bring up the accessibility shortcut (first time) or enable it
+     * (once shortcut is configured).
+     */
+    private static final int A11Y_SHORTCUT_KEY_TIMEOUT = 3000;
+
+    /**
      * Defines the duration in milliseconds we will wait to see if a touch event
      * is a tap or a scroll. If the user does not move within this interval, it is
      * considered to be a tap.
@@ -785,6 +792,18 @@
     }
 
     /**
+     * The amount of time a user needs to press the relevant keys to activate the accessibility
+     * shortcut.
+     *
+     * @return how long a user needs to press the relevant keys to activate the accessibility
+     *   shortcut.
+     * @hide
+     */
+    public long getAccessibilityShortcutKeyTimeout() {
+        return A11Y_SHORTCUT_KEY_TIMEOUT;
+    }
+
+    /**
      * The amount of friction applied to scrolls and flings.
      *
      * @return A scalar dimensionless value representing the coefficient of
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1b42a90..a479bb3 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1788,15 +1788,15 @@
             for (int i = childrenCount - 1; i >= 0; i--) {
                 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
                 final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
-                final PointF point = getLocalPoint();
-                if (isTransformedTouchPointInView(x, y, child, point)) {
-                    final PointerIcon pointerIcon =
-                            dispatchResolvePointerIcon(event, pointerIndex, child);
-                    if (pointerIcon != null) {
-                        if (preorderedList != null) preorderedList.clear();
-                        return pointerIcon;
-                    }
-                    break;
+                if (!canViewReceivePointerEvents(child)
+                        || !isTransformedTouchPointInView(x, y, child, null)) {
+                    continue;
+                }
+                final PointerIcon pointerIcon =
+                        dispatchResolvePointerIcon(event, pointerIndex, child);
+                if (pointerIcon != null) {
+                    if (preorderedList != null) preorderedList.clear();
+                    return pointerIcon;
                 }
             }
             if (preorderedList != null) preorderedList.clear();
@@ -2091,11 +2091,12 @@
                                 getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
                         final View child =
                                 getAndVerifyPreorderedView(preorderedList, children, childIndex);
-                        final PointF point = getLocalPoint();
-                        if (isTransformedTouchPointInView(x, y, child, point)) {
-                            if (dispatchTooltipHoverEvent(event, child)) {
-                                newTarget = child;
-                            }
+                        if (!canViewReceivePointerEvents(child)
+                                || !isTransformedTouchPointInView(x, y, child, null)) {
+                            continue;
+                        }
+                        if (dispatchTooltipHoverEvent(event, child)) {
+                            newTarget = child;
                             break;
                         }
                     }
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 7f940f1..bfb8d83 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -21,6 +21,7 @@
 import android.Manifest;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.NonNull;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
@@ -241,6 +242,8 @@
      * @hide
      */
     public AccessibilityManager(Context context, IAccessibilityManager service, int userId) {
+        // Constructor can't be chained because we can't create an instance of an inner class
+        // before calling another constructor.
         mHandler = new MyHandler(context.getMainLooper());
         mUserId = userId;
         synchronized (mLock) {
@@ -249,6 +252,23 @@
     }
 
     /**
+     * Create an instance.
+     *
+     * @param handler The handler to use
+     * @param service An interface to the backing service.
+     * @param userId User id under which to run.
+     *
+     * @hide
+     */
+    public AccessibilityManager(Handler handler, IAccessibilityManager service, int userId) {
+        mHandler = handler;
+        mUserId = userId;
+        synchronized (mLock) {
+            tryConnectToServiceLocked(service);
+        }
+    }
+
+    /**
      * @hide
      */
     public IAccessibilityManagerClient getClient() {
@@ -647,6 +667,30 @@
     }
 
     /**
+     * Find an installed service with the specified {@link ComponentName}.
+     *
+     * @param componentName The name to match to the service.
+     *
+     * @return The info corresponding to the installed service, or {@code null} if no such service
+     * is installed.
+     * @hide
+     */
+    public AccessibilityServiceInfo getInstalledServiceInfoWithComponentName(
+            ComponentName componentName) {
+        final List<AccessibilityServiceInfo> installedServiceInfos =
+                getInstalledAccessibilityServiceList();
+        if ((installedServiceInfos == null) || (componentName == null)) {
+            return null;
+        }
+        for (int i = 0; i < installedServiceInfos.size(); i++) {
+            if (componentName.equals(installedServiceInfos.get(i).getComponentName())) {
+                return installedServiceInfos.get(i);
+            }
+        }
+        return null;
+    }
+
+    /**
      * Adds an accessibility interaction connection interface for a given window.
      * @param windowToken The window token to which a connection is added.
      * @param connection The connection.
@@ -693,6 +737,26 @@
         }
     }
 
+    /**
+     * Perform the accessibility shortcut if the caller has permission.
+     *
+     * @hide
+     */
+    public void performAccessibilityShortcut() {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.performAccessibilityShortcut();
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re);
+        }
+    }
+
     private IAccessibilityManager getServiceLocked() {
         if (mService == null) {
             tryConnectToServiceLocked(null);
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 2829744..ed77f68 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -60,7 +60,6 @@
 
     IBinder getWindowToken(int windowId, int userId);
 
-    void enableAccessibilityService(in ComponentName service, int userId);
-
-    void disableAccessibilityService(in ComponentName service, int userId);
+    // Requires WRITE_SECURE_SETTINGS
+    void performAccessibilityShortcut();
 }
diff --git a/core/java/com/android/internal/os/AppFuseMount.aidl b/core/java/com/android/internal/os/AppFuseMount.aidl
new file mode 100644
index 0000000..66cf95b
--- /dev/null
+++ b/core/java/com/android/internal/os/AppFuseMount.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+parcelable AppFuseMount;
diff --git a/core/java/com/android/internal/os/AppFuseMount.java b/core/java/com/android/internal/os/AppFuseMount.java
index b392186..04d7211 100644
--- a/core/java/com/android/internal/os/AppFuseMount.java
+++ b/core/java/com/android/internal/os/AppFuseMount.java
@@ -19,14 +19,26 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
-import java.io.File;
+import android.os.storage.IStorageManager;
+import com.android.internal.util.Preconditions;
 
+/**
+ * Parcelable class representing AppFuse mount.
+ * This conveys the result for IStorageManager#openProxyFileDescriptor.
+ * @see IStorageManager#openProxyFileDescriptor
+ */
 public class AppFuseMount implements Parcelable {
-    final public File mountPoint;
+    final public int mountPointId;
     final public ParcelFileDescriptor fd;
 
-    public AppFuseMount(File mountPoint, ParcelFileDescriptor fd) {
-        this.mountPoint = mountPoint;
+    /**
+     * @param mountPointId Integer number for mount point that is unique in the lifetime of
+     *     StorageManagerService.
+     * @param fd File descriptor pointing /dev/fuse and tagged with the mount point.
+     */
+    public AppFuseMount(int mountPointId, ParcelFileDescriptor fd) {
+        Preconditions.checkNotNull(fd);
+        this.mountPointId = mountPointId;
         this.fd = fd;
     }
 
@@ -37,7 +49,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(this.mountPoint.getPath());
+        dest.writeInt(this.mountPointId);
         dest.writeParcelable(fd, flags);
     }
 
@@ -45,7 +57,7 @@
             new Parcelable.Creator<AppFuseMount>() {
         @Override
         public AppFuseMount createFromParcel(Parcel in) {
-            return new AppFuseMount(new File(in.readString()), in.readParcelable(null));
+            return new AppFuseMount(in.readInt(), in.readParcelable(null));
         }
 
         @Override
diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java
index 34253ce..3603b6d 100644
--- a/core/java/com/android/internal/os/FuseAppLoop.java
+++ b/core/java/com/android/internal/os/FuseAppLoop.java
@@ -18,34 +18,38 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.IProxyFileDescriptorCallback;
+import android.os.ProxyFileDescriptorCallback;
 import android.os.ParcelFileDescriptor;
 import android.system.ErrnoException;
 import android.system.OsConstants;
 import android.util.Log;
 import android.util.SparseArray;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
-import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.concurrent.ThreadFactory;
 
 public class FuseAppLoop {
     private static final String TAG = "FuseAppLoop";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     public static final int ROOT_INODE = 1;
     private static final int MIN_INODE = 2;
+    private static final ThreadFactory sDefaultThreadFactory = new ThreadFactory() {
+        @Override
+        public Thread newThread(Runnable r) {
+            return new Thread(r, TAG);
+        }
+    };
 
     private final Object mLock = new Object();
-    private final File mParent;
+    private final int mMountPointId;
+    private final Thread mThread;
 
     @GuardedBy("mLock")
     private final SparseArray<CallbackEntry> mCallbackMap = new SparseArray<>();
 
-    @GuardedBy("mLock")
-    private boolean mActive = true;
-
     /**
      * Sequential number can be used as file name and inode in AppFuse.
      * 0 is regarded as an error, 1 is mount point. So we start the number from 2.
@@ -53,35 +57,40 @@
     @GuardedBy("mLock")
     private int mNextInode = MIN_INODE;
 
-    private FuseAppLoop(@NonNull File parent) {
-        mParent = parent;
-    }
-
-    public static @NonNull FuseAppLoop open(
-            @NonNull File parent, @NonNull ParcelFileDescriptor fd) {
-        Preconditions.checkNotNull(parent);
-        Preconditions.checkNotNull(fd);
-        final FuseAppLoop bridge = new FuseAppLoop(parent);
+    private FuseAppLoop(
+            int mountPointId, @NonNull ParcelFileDescriptor fd, @Nullable ThreadFactory factory) {
+        mMountPointId = mountPointId;
         final int rawFd = fd.detachFd();
-        new Thread(new Runnable() {
+        if (factory == null) {
+            factory = sDefaultThreadFactory;
+        }
+        mThread = factory.newThread(new Runnable() {
             @Override
             public void run() {
-                bridge.native_start_loop(rawFd);
+                // rawFd is closed by native_start_loop. Java code does not need to close it.
+                native_start_loop(rawFd);
             }
-        }, TAG).start();
-        return bridge;
+        });
     }
 
-    public @NonNull ParcelFileDescriptor openFile(int mode, IProxyFileDescriptorCallback callback)
+    public static @NonNull FuseAppLoop open(int mountPointId, @NonNull ParcelFileDescriptor fd,
+            @Nullable ThreadFactory factory) {
+        Preconditions.checkNotNull(fd);
+        final FuseAppLoop loop = new FuseAppLoop(mountPointId, fd, factory);
+        loop.mThread.start();
+        return loop;
+    }
+
+    public int registerCallback(@NonNull ProxyFileDescriptorCallback callback)
             throws UnmountedException, IOException {
-        int id;
+        if (mThread.getState() == Thread.State.TERMINATED) {
+            throw new UnmountedException();
+        }
         synchronized (mLock) {
-            if (!mActive) {
-                throw new UnmountedException();
-            }
             if (mCallbackMap.size() >= Integer.MAX_VALUE - MIN_INODE) {
                 throw new IOException("Too many opened files.");
             }
+            int id;
             while (true) {
                 id = mNextInode;
                 mNextInode++;
@@ -92,24 +101,17 @@
                     break;
                 }
             }
-
-            // Register callback after we succeed to create pfd.
             mCallbackMap.put(id, new CallbackEntry(callback));
-        }
-        try {
-            return ParcelFileDescriptor.open(new File(mParent, String.valueOf(id)), mode);
-        } catch (FileNotFoundException error) {
-            synchronized (mLock) {
-                mCallbackMap.remove(id);
-            }
-            throw error;
+            return id;
         }
     }
 
-    public @Nullable File getMountPoint() {
-        synchronized (mLock) {
-            return mActive ? mParent : null;
-        }
+    public void unregisterCallback(int id) {
+        mCallbackMap.remove(id);
+    }
+
+    public int getMountPointId() {
+        return mMountPointId;
     }
 
     private CallbackEntry getCallbackEntryOrThrowLocked(long inode) throws ErrnoException {
@@ -128,7 +130,7 @@
             try {
                 return getCallbackEntryOrThrowLocked(inode).callback.onGetSize();
             } catch (ErrnoException exp) {
-                return -exp.errno;
+                return getError(exp);
             }
         }
     }
@@ -147,7 +149,7 @@
                 // file twice.
                 return (int) inode;
             } catch (ErrnoException exp) {
-                return -exp.errno;
+                return getError(exp);
             }
         }
     }
@@ -160,7 +162,7 @@
                 getCallbackEntryOrThrowLocked(inode).callback.onFsync();
                 return 0;
             } catch (ErrnoException exp) {
-                return -exp.errno;
+                return getError(exp);
             }
         }
     }
@@ -169,12 +171,14 @@
     @SuppressWarnings("unused")
     private int onRelease(long inode) {
         synchronized(mLock) {
-            mCallbackMap.remove(checkInode(inode));
-            if (mCallbackMap.size() == 0) {
-                mActive = false;
-                return -1;
+            try {
+                getCallbackEntryOrThrowLocked(inode).callback.onRelease();
+                return 0;
+            } catch (ErrnoException exp) {
+                return getError(exp);
+            } finally {
+                mCallbackMap.remove(checkInode(inode));
             }
-            return 0;
         }
     }
 
@@ -185,7 +189,7 @@
             try {
                 return getCallbackEntryOrThrowLocked(inode).callback.onRead(offset, size, bytes);
             } catch (ErrnoException exp) {
-                return -exp.errno;
+                return getError(exp);
             }
         }
     }
@@ -197,11 +201,17 @@
             try {
                 return getCallbackEntryOrThrowLocked(inode).callback.onWrite(offset, size, bytes);
             } catch (ErrnoException exp) {
-                return -exp.errno;
+                return getError(exp);
             }
         }
     }
 
+    private static int getError(@NonNull ErrnoException exp) {
+        // Should not return ENOSYS because the kernel stops
+        // dispatching the FUSE action once FUSE implementation returns ENOSYS for the action.
+        return exp.errno != OsConstants.ENOSYS ? -exp.errno : -OsConstants.EIO;
+    }
+
     native boolean native_start_loop(int fd);
 
     private static int checkInode(long inode) {
@@ -212,9 +222,9 @@
     public static class UnmountedException extends Exception {}
 
     private static class CallbackEntry {
-        final IProxyFileDescriptorCallback callback;
+        final ProxyFileDescriptorCallback callback;
         boolean opened;
-        CallbackEntry(IProxyFileDescriptorCallback callback) {
+        CallbackEntry(ProxyFileDescriptorCallback callback) {
             Preconditions.checkNotNull(callback);
             this.callback = callback;
         }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 68034f6..6a9ed8e 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -238,6 +238,7 @@
     libnativehelper \
     liblog \
     libcutils \
+    libdebuggerd_client \
     libutils \
     libbinder \
     libui \
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 14d7e81..be3a87b 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -33,7 +33,7 @@
 #include <string>
 
 #include <android-base/stringprintf.h>
-#include <cutils/debugger.h>
+#include <debuggerd/client.h>
 #include <log/log.h>
 #include <utils/misc.h>
 #include <utils/String8.h>
diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp
index 6b634df..4150636 100644
--- a/core/jni/android_view_PointerIcon.cpp
+++ b/core/jni/android_view_PointerIcon.cpp
@@ -78,6 +78,9 @@
 
 status_t android_view_PointerIcon_getLoadedIcon(JNIEnv* env, jobject pointerIconObj,
         PointerIcon* outPointerIcon) {
+    if (!pointerIconObj) {
+        return BAD_VALUE;
+    }
     outPointerIcon->style = env->GetIntField(pointerIconObj, gPointerIconClassInfo.mType);
     outPointerIcon->hotSpotX = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotX);
     outPointerIcon->hotSpotY = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotY);
diff --git a/core/jni/com_android_internal_os_FuseAppLoop.cpp b/core/jni/com_android_internal_os_FuseAppLoop.cpp
index 92a6934..dd003eb 100644
--- a/core/jni/com_android_internal_os_FuseAppLoop.cpp
+++ b/core/jni/com_android_internal_os_FuseAppLoop.cpp
@@ -51,7 +51,6 @@
     JNIEnv* const mEnv;
     jobject const mSelf;
     ScopedLocalRef<jbyteArray> mJniBuffer;
-    bool mActive;
 
     template <typename T>
     T checkException(T result) const {
@@ -67,8 +66,7 @@
     Callback(JNIEnv* env, jobject self) :
         mEnv(env),
         mSelf(self),
-        mJniBuffer(env, nullptr),
-        mActive(true) {}
+        mJniBuffer(env, nullptr) {}
 
     bool Init() {
         mJniBuffer.reset(mEnv->NewByteArray(kBufferSize));
@@ -76,7 +74,7 @@
     }
 
     bool IsActive() override {
-        return mActive;
+        return true;
     }
 
     int64_t OnGetSize(uint64_t inode) override {
@@ -92,10 +90,7 @@
     }
 
     int32_t OnRelease(uint64_t inode) override {
-        if (checkException(mEnv->CallIntMethod(mSelf, gOnReleaseMethod, inode)) == -1) {
-            mActive = false;
-        }
-        return fuse::kFuseSuccess;
+        return checkException(mEnv->CallIntMethod(mSelf, gOnReleaseMethod, inode));
     }
 
     int32_t OnRead(uint64_t inode, uint64_t offset, uint32_t size, void* buffer) override {
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index ec2f32b..c3b0ff1 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -21,6 +21,7 @@
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 import "frameworks/base/core/proto/android/service/fingerprint.proto";
+import "frameworks/base/core/proto/android/service/netstats.proto";
 
 package android.os;
 
@@ -49,4 +50,5 @@
 
     // System Services
     android.service.fingerprint.FingerprintServiceDumpProto fingerprint = 3000;
+    android.service.NetworkStatsServiceDumpProto netstats = 3001;
 }
diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto
new file mode 100644
index 0000000..5cca6ab
--- /dev/null
+++ b/core/proto/android/service/netstats.proto
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+package android.service;
+
+option java_multiple_files = true;
+option java_outer_classname = "NetworkStatsServiceProto";
+
+// Represents dumpsys from NetworkStatsService (netstats).
+message NetworkStatsServiceDumpProto {
+    repeated NetworkInterfaceProto active_interfaces = 1;
+
+    repeated NetworkInterfaceProto active_uid_interfaces = 2;
+
+    NetworkStatsRecorderProto dev_stats = 3;
+
+    NetworkStatsRecorderProto xt_stats = 4;
+
+    NetworkStatsRecorderProto uid_stats = 5;
+
+    NetworkStatsRecorderProto uid_tag_stats = 6;
+}
+
+// Corresponds to NetworkStatsService.mActiveIfaces/mActiveUidIfaces.
+message NetworkInterfaceProto {
+    string interface = 1;
+
+    NetworkIdentitySetProto identities = 2;
+}
+
+// Corresponds to NetworkIdentitySet.
+message NetworkIdentitySetProto {
+    repeated NetworkIdentityProto identities = 1;
+}
+
+// Corresponds to NetworkIdentity.
+message NetworkIdentityProto {
+    // Constats from ConnectivityManager.TYPE_*.
+    int32 type = 1;
+
+    string subscriber_id = 2;
+
+    string network_id = 3;
+
+    bool roaming = 4;
+
+    bool metered = 5;
+}
+
+// Corresponds to NetworkStatsRecorder.
+message NetworkStatsRecorderProto {
+    int64 pending_total_bytes = 1;
+
+    NetworkStatsCollectionProto complete_history = 2;
+}
+
+// Corresponds to NetworkStatsCollection.
+message NetworkStatsCollectionProto {
+    repeated NetworkStatsCollectionStatsProto stats = 1;
+}
+
+// Corresponds to NetworkStatsCollection.mStats.
+message NetworkStatsCollectionStatsProto {
+    NetworkStatsCollectionKeyProto key = 1;
+
+    NetworkStatsHistoryProto history = 2;
+}
+
+// Corresponds to NetworkStatsCollection.Key.
+message NetworkStatsCollectionKeyProto {
+    NetworkIdentitySetProto identity = 1;
+
+    int32 uid = 2;
+
+    int32 set = 3;
+
+    int32 tag = 4;
+}
+
+// Corresponds to NetworkStatsHistory.
+message NetworkStatsHistoryProto {
+    // Duration for this bucket in milliseconds.
+    int64 bucket_duration_ms = 1;
+
+    repeated NetworkStatsHistoryBucketProto buckets = 2;
+}
+
+// Corresponds to each bucket in NetworkStatsHistory.
+message NetworkStatsHistoryBucketProto {
+    // Bucket start time in milliseconds since epoch.
+    int64 bucket_start_ms = 1;
+
+    int64 rx_bytes = 2;
+
+    int64 rx_packets = 3;
+
+    int64 tx_bytes = 4;
+
+    int64 tx_packets = 5;
+
+    int64 operations = 6;
+}
diff --git a/core/res/res/color/text_color_primary.xml b/core/res/res/color/text_color_primary.xml
new file mode 100644
index 0000000..831a9c4
--- /dev/null
+++ b/core/res/res/color/text_color_primary.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+        android:alpha="?attr/disabledAlpha"
+        android:color="?attr/colorForeground"/>
+    <item android:alpha="?attr/primaryContentAlpha"
+        android:color="?attr/colorForeground"/>
+</selector>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index dd33718..a5c1a94 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -62,6 +62,8 @@
 
         <!-- Default disabled alpha for widgets that set enabled/disabled alpha programmatically. -->
         <attr name="disabledAlpha" format="float" />
+        <!-- The alpha applied to the foreground color to create the primary text color. -->
+        <attr name="primaryContentAlpha" format="float" />
         <!-- Default background dim amount when a menu, dialog, or something similar pops up. -->
         <attr name="backgroundDimAmount" format="float" />
         <!-- Control whether dimming behind the window is enabled.  The default
@@ -7615,6 +7617,21 @@
     </declare-styleable>
 
     <!-- =============================== -->
+    <!-- AutoFill attributes -->
+    <!-- =============================== -->
+    <eat-comment />
+
+    <!-- Use <code>autofill-service</code> as the root tag of the XML resource that describes a
+         {@link android.service.autofill.AutoFillService}, which is referenced from its
+         {@link android.service.autofill#SERVICE_META_DATA} meta-data entry.
+    -->
+    <declare-styleable name="AutoFillService">
+        <!-- Fully qualified class name of an activity that allows the user to modify
+             the settings for this service. -->
+        <attr name="settingsActivity" />
+    </declare-styleable>
+
+    <!-- =============================== -->
     <!-- Contacts meta-data attributes -->
     <!-- =============================== -->
     <eat-comment />
@@ -8428,7 +8445,7 @@
     <!-- @hide Attributes which will be read by the Activity to intialize the
                base activity TaskDescription. -->
     <declare-styleable name="ActivityTaskDescription">
-        <!-- @hide From Theme.colorPrimary, used for the TaskDescription primary 
+        <!-- @hide From Theme.colorPrimary, used for the TaskDescription primary
                    color. -->
         <attr name="colorPrimary" />
         <!-- @hide From Theme.colorBackground, used for the TaskDescription background
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 40e7341..835b8b60 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -73,6 +73,8 @@
 
     <item name="disabled_alpha_material_light" format="float" type="dimen">0.26</item>
     <item name="disabled_alpha_material_dark" format="float" type="dimen">0.30</item>
+    <item name="primary_content_alpha_material_light" format="float" type="dimen">1</item>
+    <item name="primary_content_alpha_material_dark" format="float" type="dimen">0.87</item>
 
     <item name="highlight_alpha_material_light" format="float" type="dimen">0.12</item>
     <item name="highlight_alpha_material_dark" format="float" type="dimen">0.20</item>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index db157bf..7de48d3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2710,4 +2710,10 @@
 
     <!-- Component name of the default cell broadcast receiver -->
     <string name="config_defaultCellBroadcastReceiverComponent" translatable="false">com.android.cellbroadcastreceiver/.PrivilegedCellBroadcastReceiver</string>
+
+    <!-- The component name, flattened to a string, for the default accessibility service to be
+         enabled by the accessibility shortcut. This service must be trusted, as it can be activated
+         without explicit consent of the user. If no accessibility service with the specified name
+         exists on the device, the accessibility shortcut will be disabled by default. -->
+    <string name="config_defaultAccessibilityService" translatable="false"></string>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 064d31e..099fe08 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2793,6 +2793,8 @@
     <public-group type="id" first-id="0x01020041">
     </public-group>
 
+    <public type="attr" name="primaryContentAlpha" />
+
   <!-- ===============================================================
        DO NOT ADD UN-GROUPED ITEMS HERE
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 87a4732..0204e93 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3818,12 +3818,35 @@
        "Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."
     </string>
 
-    <!-- Text spoken when the user is performing a gesture that will enable accessibility. [CHAR LIMIT=none] -->
-    <string name="continue_to_enable_accessibility">Keep holding down two fingers to enable accessibility.</string>
-    <!-- Text spoken when the user enabled accessibility. [CHAR LIMIT=none] -->
-    <string name="accessibility_enabled">Accessibility enabled.</string>
-    <!-- Text spoken when the user stops preforming a gesture that would enable accessibility. [CHAR LIMIT=none] -->
-    <string name="enable_accessibility_canceled">Accessibility canceled.</string>
+    <!-- Dialog title for dialog shown when the accessibility shortcut is activated, and we want
+     to confirm that the user understands what's going to happen-->
+    <string name="accessibility_shortcut_warning_dialog_title">Accessibility Shortcut is ON</string>
+
+    <!-- Message shown in dialog when user is in the process of enabling the accessibility
+    service via the volume buttons shortcut for the first time. [CHAR LIMIT=none] -->
+    <string name="accessibility_shortcut_toogle_warning">
+        Turn <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> on or off by holding down
+        both volume buttons for 3 seconds.\n\nYou can change the service in
+        Settings > Accessibility.
+    </string>
+
+    <!-- Text in button that turns off the accessibility shortcut -->
+    <string name="disable_accessibility_shortcut">Turn Off Shortcut</string>
+
+    <!-- Text in button that closes the warning dialog about the accessibility shortcut, leaving the
+    shortcut enabled.-->
+    <string name="leave_accessibility_shortcut_on">Leave on</string>
+
+    <!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility
+    service.-->
+    <string name="accessibility_shortcut_enabling_service">Accessibility Shortcut turned
+        <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> on</string>
+
+    <!-- Text in toast to alert the user that the accessibility shortcut turned off an accessibility
+    service.-->
+    <string name="accessibility_shortcut_disabling_service">Accessibility Shortcut turned
+        <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> off</string>
+
     <!-- Text spoken when the current user is switched if accessibility is enabled. [CHAR LIMIT=none] -->
     <string name="user_switched">Current user <xliff:g id="name" example="Bob">%1$s</xliff:g>.</string>
     <!-- Message shown when switching to a user [CHAR LIMIT=none] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b923dff..c370ef7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1139,7 +1139,6 @@
   <java-symbol type="string" name="conference_call" />
   <java-symbol type="string" name="tooltip_popup_title" />
 
-
   <java-symbol type="plurals" name="bugreport_countdown" />
   <java-symbol type="plurals" name="last_num_days" />
   <java-symbol type="plurals" name="matches_found" />
@@ -2797,4 +2796,14 @@
 
   <java-symbol type="raw" name="fallback_categories" />
 
+  <java-symbol type="attr" name="primaryContentAlpha" />
+
+  <!-- Accessibility Shortcut -->
+  <java-symbol type="string" name="accessibility_shortcut_warning_dialog_title" />
+  <java-symbol type="string" name="accessibility_shortcut_toogle_warning" />
+  <java-symbol type="string" name="accessibility_shortcut_enabling_service" />
+  <java-symbol type="string" name="accessibility_shortcut_disabling_service" />
+  <java-symbol type="string" name="disable_accessibility_shortcut" />
+  <java-symbol type="string" name="leave_accessibility_shortcut_on" />
+  <java-symbol type="string" name="config_defaultAccessibilityService" />
 </resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 5f0ad8e..d0f202b 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -48,13 +48,14 @@
         <item name="colorBackgroundFloating">@color/background_floating_material_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
         <item name="disabledAlpha">@dimen/disabled_alpha_material_dark</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_material_dark</item>
         <item name="backgroundDimAmount">0.6</item>
 
         <!-- Text styles -->
         <item name="textAppearance">@style/TextAppearance.Material</item>
         <item name="textAppearanceInverse">@style/TextAppearance.Material.Inverse</item>
 
-        <item name="textColorPrimary">@color/primary_text_material_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary</item>
         <item name="textColorPrimaryInverse">@color/primary_text_material_light</item>
         <item name="textColorPrimaryActivated">@color/primary_text_inverse_when_activated_material</item>
         <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_dark</item>
@@ -413,13 +414,14 @@
         <item name="colorBackgroundFloating">@color/background_floating_material_light</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
         <item name="disabledAlpha">@dimen/disabled_alpha_material_light</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_material_light</item>
         <item name="backgroundDimAmount">0.6</item>
 
         <!-- Text styles -->
         <item name="textAppearance">@style/TextAppearance.Material</item>
         <item name="textAppearanceInverse">@style/TextAppearance.Material.Inverse</item>
 
-        <item name="textColorPrimary">@color/primary_text_material_light</item>
+        <item name="textColorPrimary">@color/text_color_primary</item>
         <item name="textColorPrimaryInverse">@color/primary_text_material_dark</item>
         <item name="textColorPrimaryActivated">@color/primary_text_inverse_when_activated_material</item>
         <item name="textColorSecondary">@color/secondary_text_material_light</item>
@@ -805,7 +807,7 @@
         <item name="colorBackgroundFloating">@color/background_floating_material_light</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
 
-        <item name="textColorPrimary">@color/primary_text_material_light</item>
+        <item name="textColorPrimary">@color/text_color_primary</item>
         <item name="textColorPrimaryInverse">@color/primary_text_material_dark</item>
         <item name="textColorSecondary">@color/secondary_text_material_light</item>
         <item name="textColorSecondaryInverse">@color/secondary_text_material_dark</item>
@@ -839,7 +841,7 @@
         <item name="colorBackgroundFloating">@color/background_floating_material_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
 
-        <item name="textColorPrimary">@color/primary_text_material_dark</item>
+        <item name="textColorPrimary">@color/text_color_primary</item>
         <item name="textColorPrimaryInverse">@color/primary_text_material_light</item>
         <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_dark</item>
         <item name="textColorSecondary">@color/secondary_text_material_dark</item>
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
index 37f0007..ff98eb7 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
@@ -18,15 +18,20 @@
 
 import android.content.Context;
 import android.os.Environment;
+import android.os.ProxyFileDescriptorCallback;
+import android.os.ParcelFileDescriptor;
+import android.system.ErrnoException;
+import android.system.Os;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
 import com.android.frameworks.coretests.R;
-
+import com.android.internal.os.FuseAppLoop;
 import java.io.DataInputStream;
 import java.io.IOException;
+import java.util.concurrent.ThreadFactory;
 import java.io.File;
 import java.io.FileInputStream;
 
@@ -243,4 +248,47 @@
         verifyObb1Contents(filePath);
         unmountObb(filePath, DONT_FORCE);
     }
+
+    @LargeTest
+    public void testOpenProxyFileDescriptor() throws Exception {
+        final ProxyFileDescriptorCallback callback = new ProxyFileDescriptorCallback() {
+            @Override
+            public long onGetSize() throws ErrnoException {
+                return 0;
+            }
+
+            @Override
+            public void onRelease() {}
+        };
+
+        final MyThreadFactory factory = new MyThreadFactory();
+        int firstMountId;
+        try (final ParcelFileDescriptor fd = mSm.openProxyFileDescriptor(
+                ParcelFileDescriptor.MODE_READ_ONLY, callback, factory)) {
+            assertNotSame(Thread.State.TERMINATED, factory.thread.getState());
+            firstMountId = mSm.getProxyFileDescriptorMountPointId();
+            assertNotSame(-1, firstMountId);
+        }
+
+        // After closing descriptor, the loop should terminate.
+        factory.thread.join(3000);
+        assertEquals(Thread.State.TERMINATED, factory.thread.getState());
+
+        // StorageManager should mount another bridge on the next open request.
+        try (final ParcelFileDescriptor fd = mSm.openProxyFileDescriptor(
+                ParcelFileDescriptor.MODE_WRITE_ONLY, callback, factory)) {
+            assertNotSame(Thread.State.TERMINATED, factory.thread.getState());
+            assertNotSame(firstMountId, mSm.getProxyFileDescriptorMountPointId());
+        }
+    }
+
+    private static class MyThreadFactory implements ThreadFactory {
+        Thread thread = null;
+
+        @Override
+        public Thread newThread(Runnable r) {
+            thread = new Thread(r);
+            return thread;
+        }
+    }
 }
\ No newline at end of file
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 05db9b0..f1be4a9 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -110,11 +110,21 @@
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.omadm.service">
+        <permission name="android.permission.CHANGE_CONFIGURATION"/>
+        <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+        <permission name="android.permission.WRITE_APN_SETTINGS"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+
     <privapp-permissions package="com.android.packageinstaller">
         <permission name="android.permission.CLEAR_APP_CACHE"/>
         <permission name="android.permission.DELETE_PACKAGES"/>
         <permission name="android.permission.INSTALL_PACKAGES"/>
         <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
     </privapp-permissions>
 
@@ -122,6 +132,7 @@
         <permission name="android.permission.ACCESS_IMS_CALL_SERVICE"/>
         <permission name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"/>
         <permission name="android.permission.BIND_CARRIER_SERVICES"/>
+        <permission name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"/>
         <permission name="android.permission.CALL_PRIVILEGED"/>
         <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
         <permission name="android.permission.CHANGE_CONFIGURATION"/>
@@ -327,4 +338,4 @@
         <permission name="android.permission.CONTROL_VPN"/>
     </privapp-permissions>
 
-</permissions>
+</permissions>
\ No newline at end of file
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index b38a07f..dd9c6a7 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -283,10 +283,19 @@
 
     /**
      * @hide
-     * FIXME return a player proxy instead, make systemApi
-     * @return
+     * Return a proxy for the player associated with this playback configuration
+     * @return a proxy player
      */
-    public IPlayer getPlayerProxy() {
+    @SystemApi
+    public PlayerProxy getPlayerProxy() {
+        return mIPlayerShell == null ? null : new PlayerProxy(this);
+    }
+
+    /**
+     * @hide
+     * @return the IPlayer interface for the associated player
+     */
+    IPlayer getIPlayer() {
         return mIPlayerShell == null ? null : mIPlayerShell.getIPlayer();
     }
 
diff --git a/media/java/android/media/PlayerProxy.java b/media/java/android/media/PlayerProxy.java
new file mode 100644
index 0000000..171be27
--- /dev/null
+++ b/media/java/android/media/PlayerProxy.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.IllegalArgumentException;
+import java.util.Objects;
+
+/**
+ * Class to remotely control a player.
+ * @hide
+ */
+@SystemApi
+public class PlayerProxy {
+
+    private final static String TAG = "PlayerProxy";
+    private final static boolean DEBUG = false;
+
+    private final AudioPlaybackConfiguration mConf; // never null
+
+    /**
+     * @hide
+     * Constructor. Proxy for this player associated with this AudioPlaybackConfiguration
+     * @param conf the configuration being proxied.
+     */
+    PlayerProxy(@NonNull AudioPlaybackConfiguration apc) {
+        if (apc == null) {
+            throw new IllegalArgumentException("Illegal null AudioPlaybackConfiguration");
+        }
+        mConf = apc;
+    };
+
+    //=====================================================================
+    // Methods matching the IPlayer interface
+    /**
+     * @hide
+     * @throws IllegalStateException
+     */
+    @SystemApi
+    public void start() throws IllegalStateException {
+        try {
+            mConf.getIPlayer().start();
+        } catch (NullPointerException|RemoteException e) {
+            throw new IllegalStateException(
+                    "No player to proxy for start operation, player already released?", e);
+        }
+    }
+
+    /**
+     * @hide
+     * @throws IllegalStateException
+     */
+    @SystemApi
+    public void pause() throws IllegalStateException {
+        try {
+            mConf.getIPlayer().pause();
+        } catch (NullPointerException|RemoteException e) {
+            throw new IllegalStateException(
+                    "No player to proxy for pause operation, player already released?", e);
+        }
+    }
+
+    /**
+     * @hide
+     * @throws IllegalStateException
+     */
+    @SystemApi
+    public void stop() throws IllegalStateException {
+        try {
+            mConf.getIPlayer().stop();
+        } catch (NullPointerException|RemoteException e) {
+            throw new IllegalStateException(
+                    "No player to proxy for stop operation, player already released?", e);
+        }
+    }
+
+    /**
+     * @hide
+     * @throws IllegalStateException
+     */
+    @SystemApi
+    public void setVolume(float vol) throws IllegalStateException {
+        try {
+            mConf.getIPlayer().setVolume(vol);
+        } catch (NullPointerException|RemoteException e) {
+            throw new IllegalStateException(
+                    "No player to proxy for setVolume operation, player already released?", e);
+        }
+    }
+
+}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 16847c1..eaec493 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -387,7 +387,6 @@
      * @see BrowserRoot#EXTRA_RECENT
      * @see BrowserRoot#EXTRA_OFFLINE
      * @see BrowserRoot#EXTRA_SUGGESTED
-     * @see BrowserRoot#EXTRA_SUGGESTION_KEYWORDS
      */
     public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
             int clientUid, @Nullable Bundle rootHints);
@@ -550,7 +549,6 @@
      * @see MediaBrowserService.BrowserRoot#EXTRA_RECENT
      * @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE
      * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
-     * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTION_KEYWORDS
      */
     public final Bundle getBrowserRootHints() {
         if (mCurConnection == null) {
@@ -818,7 +816,6 @@
          *
          * @see #EXTRA_OFFLINE
          * @see #EXTRA_SUGGESTED
-         * @see #EXTRA_SUGGESTION_KEYWORDS
          */
         public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
 
@@ -836,7 +833,6 @@
          *
          * @see #EXTRA_RECENT
          * @see #EXTRA_SUGGESTED
-         * @see #EXTRA_SUGGESTION_KEYWORDS
          */
         public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
 
@@ -855,31 +851,9 @@
          *
          * @see #EXTRA_RECENT
          * @see #EXTRA_OFFLINE
-         * @see #EXTRA_SUGGESTION_KEYWORDS
          */
         public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
 
-        /**
-         * The lookup key for a string that indicates specific keywords which will be considered
-         * when the browser service suggests media items.
-         *
-         * <p>When creating a media browser for a given media browser service, this key can be
-         * supplied as a root hint together with {@link #EXTRA_SUGGESTED} for retrieving suggested
-         * media items related with the keywords. The list of media items passed in
-         * {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)}
-         * is considered ordered by relevance, first being the top suggestion.
-         * If the media browser service can provide such media items, the implementation must return
-         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
-         *
-         * <p>The root hint may contain multiple keys.
-         *
-         * @see #EXTRA_RECENT
-         * @see #EXTRA_OFFLINE
-         * @see #EXTRA_SUGGESTED
-         */
-        public static final String EXTRA_SUGGESTION_KEYWORDS
-                = "android.service.media.extra.SUGGESTION_KEYWORDS";
-
         final private String mRootId;
         final private Bundle mExtras;
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index e1657c7..f8f4f2a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -29,6 +29,7 @@
 import android.util.Slog;
 import android.util.TypedValue;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.GridLayout;
 import android.widget.TextClock;
 import android.widget.TextView;
@@ -48,6 +49,7 @@
     private TextClock mDateView;
     private TextClock mClockView;
     private TextView mOwnerInfo;
+    private ViewGroup mClockContainer;
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
 
@@ -105,6 +107,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mClockContainer = (ViewGroup) findViewById(R.id.keyguard_clock_container);
         mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
         mDateView = (TextClock) findViewById(R.id.date_view);
         mClockView = (TextClock) findViewById(R.id.clock_view);
@@ -167,6 +170,11 @@
         }
     }
 
+    public int getClockBottom() {
+        return mClockView.getBottom() +
+                ((MarginLayoutParams) mClockView.getLayoutParams()).bottomMargin;
+    }
+
     public static String formatNextAlarm(Context context, AlarmManager.AlarmClockInfo info) {
         if (info == null) {
             return "";
@@ -260,4 +268,15 @@
             cacheKey = key;
         }
     }
+
+    public void setDark(boolean dark) {
+        final int N = mClockContainer.getChildCount();
+        for (int i = 0; i < N; i++) {
+            View child = mClockContainer.getChildAt(i);
+            if (child == mClockView) {
+                continue;
+            }
+            child.setAlpha(dark ? 0 : 1);
+        }
+    }
 }
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 5c00985..cfb990e 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -105,10 +105,10 @@
 
     <!-- Titles for Bluetooth Audio Codec selection preference. [CHAR LIMIT=40] -->
     <string-array name="bluetooth_a2dp_codec_titles">
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>SBC</item>
         <item>aptX</item>
-        <item>aptX-HD</item>
+        <item>aptX HD</item>
         <item>LDAC</item>
     </string-array>
 
@@ -123,16 +123,16 @@
 
     <!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=40]-->
     <string-array name="bluetooth_a2dp_codec_summaries" >
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>SBC</item>
         <item>aptX</item>
-        <item>aptX-HD</item>
+        <item>aptX HD</item>
         <item>LDAC</item>
     </string-array>
 
     <!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=40] -->
     <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>44.1 kHz</item>
         <item>48.0 kHz</item>
         <item>88.2 kHz</item>
@@ -150,7 +150,7 @@
 
     <!-- Summaries for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=40]-->
     <string-array name="bluetooth_a2dp_codec_sample_rate_summaries" >
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>44.1 kHz</item>
         <item>48.0 kHz</item>
         <item>88.2 kHz</item>
@@ -159,7 +159,7 @@
 
     <!-- Titles for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=40] -->
     <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>16 bits/sample</item>
         <item>24 bits/sample</item>
         <item>32 bits/sample</item>
@@ -175,7 +175,7 @@
 
     <!-- Summaries for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=40]-->
     <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries" >
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>16 bits/sample</item>
         <item>24 bits/sample</item>
         <item>32 bits/sample</item>
@@ -183,7 +183,7 @@
 
     <!-- Titles for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=40] -->
     <string-array name="bluetooth_a2dp_codec_channel_mode_titles">
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>Mono</item>
         <item>Stereo</item>
     </string-array>
@@ -197,16 +197,16 @@
 
     <!-- Summaries for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=40]-->
     <string-array name="bluetooth_a2dp_codec_channel_mode_summaries" >
-        <item>Default</item>
+        <item>Use System Selection (Default)</item>
         <item>Mono</item>
         <item>Stereo</item>
     </string-array>
 
-    <!-- Titles for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=40] -->
+    <!-- Titles for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=70] -->
     <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-        <item>Sound quality preferred (990kbps/909kbps)</item>
-        <item>Standard (660kbps/606kbps)</item>
-        <item>Connection preferred (330kbps/303kbps)</item>
+        <item>Optimize for Audio Quality (990kbps/909kbps)</item>
+        <item>Balanced Audio And Connection Quality (660kbps/606kbps)</item>
+        <item>Optimize for Connection Quality (330kbps/303kbps)</item>
     </string-array>
 
     <!-- Values for Bluetooth Audio Codec LDAC Playback Quaility selection preference. -->
@@ -216,11 +216,11 @@
         <item>1002</item>
     </string-array>
 
-    <!-- Summaries for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=40]-->
+    <!-- Summaries for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=70]-->
     <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries" >
-        <item>Sound quality preferred (990kbps/909kbps)</item>
-        <item>Standard (660kbps/606kbps)</item>
-        <item>Connection preferred (330kbps/303kbps)</item>
+        <item>Optimize for Audio Quality</item>
+        <item>Balanced Audio And Connection Quality</item>
+        <item>Optimize for Connection Quality</item>
     </string-array>
 
     <!-- Titles for logd limit size selection preference. [CHAR LIMIT=14] -->
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 93bd5dc..a1b2bdf 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -430,28 +430,31 @@
 
     <!-- UI debug setting: Select Bluetooth Audio Codec -->
     <string name="bluetooth_select_a2dp_codec_type">Bluetooth Audio Codec</string>
-    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec -->
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title">Select Preferred Bluetooth A2DP Codec</string>
+    <!-- UI debug setting: Select Bluetooth Audio Codec -->
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title">Select Bluetooth Audio Codec</string>
 
     <!-- UI debug setting: Select Bluetooth Audio Sample Rate -->
     <string name="bluetooth_select_a2dp_codec_sample_rate">Bluetooth Audio Sample Rate</string>
-    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec Sample Rate -->
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Select Preferred Bluetooth A2DP Codec Sample Rate</string>
+    <!-- UI debug setting: Select Bluetooth Audio Codec: Sample Rate -->
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Select Bluetooth Audio Codec:\u000ASample Rate</string>
 
     <!-- UI debug setting: Select Bluetooth Audio Bits Per Sample -->
     <string name="bluetooth_select_a2dp_codec_bits_per_sample">Bluetooth Audio Bits Per Sample</string>
-    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec Bits Per Sample -->
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Select Preferred Bluetooth A2DP Codec Bits Per Sample</string>
+    <!-- UI debug setting: Select Bluetooth Audio Codec: Bits Per Sample -->
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Select Bluetooth Audio Codec:\u000ABits Per Sample</string>
 
     <!-- UI debug setting: Select Bluetooth Audio Channel Mode -->
     <string name="bluetooth_select_a2dp_codec_channel_mode">Bluetooth Audio Channel Mode</string>
-    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec Channel Mode -->
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Select Preferred Bluetooth A2DP Codec Channel Mode</string>
+    <!-- UI debug setting: Select Bluetooth Audio Codec: Channel Mode -->
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Select Bluetooth Audio Codec:\u000AChannel Mode</string>
 
     <!-- UI debug setting: Select Bluetooth Audio LDAC Playback Quality -->
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality">Bluetooth Audio LDAC Playback Quality</string>
-    <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec LDAC Playback Quality -->
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Select Preferred Bluetooth A2DP Codec LDAC Playback Quality</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality">Bluetooth Audio LDAC Codec: Playback Quality</string>
+    <!-- UI debug setting: Select Bluetooth Audio LDAC Codec: LDAC Playback Quality -->
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Select Bluetooth Audio LDAC Codec:\u000APlayback Quality</string>
+
+    <!-- [CHAR LIMIT=NONE] Label for displaying Bluetooth Audio Codec Parameters while streaming -->
+    <string name="bluetooth_select_a2dp_codec_streaming_label">Streaming: <xliff:g id="streaming_parameter">%1$s</xliff:g></string>
 
     <!-- setting Checkbox summary whether to show options for wireless display certification  -->
     <string name="wifi_display_certification_summary">Show options for wireless display certification</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index fcff305..9bb3c36 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -28,6 +28,8 @@
 import android.util.ArraySet;
 import android.view.accessibility.AccessibilityManager;
 
+import com.android.internal.R;
+
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -147,6 +149,26 @@
                 enabledServicesBuilder.toString(), userId);
     }
 
+    /**
+     * Get the name of the service that should be toggled by the accessibility shortcut. Use
+     * an OEM-configurable default if the setting has never been set.
+     *
+     * @param context A valid context
+     * @param userId The user whose settings should be checked
+     *
+     * @return The component name, flattened to a string, of the target service.
+     */
+    public static String getShortcutTargetServiceComponentNameString(
+            Context context, int userId) {
+        final String currentShortcutServiceId = Settings.Secure.getStringForUser(
+                context.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                userId);
+        if (currentShortcutServiceId != null) {
+            return currentShortcutServiceId;
+        }
+        return context.getString(R.string.config_defaultAccessibilityService);
+    }
+
     private static Set<ComponentName> getInstalledServices(Context context) {
         final Set<ComponentName> installedServices = new HashSet<>();
         installedServices.clear();
diff --git a/packages/SystemUI/res/drawable/ic_qs_nfc_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_nfc_disabled.xml
new file mode 100644
index 0000000..558f3d0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_nfc_disabled.xml
@@ -0,0 +1,31 @@
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:pathData="M4 20h16V4H4v16z" />
+    <path
+        android:fillColor="#4DFFFFFF"
+        android:pathData="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1 .9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0
+18H4V4h16v16zM18 6h-5c-1.1 0-2 .9-2 2v2.28c-.6 .35 -1 .98-1 1.72 0 1.1 .9 2 2
+2s2-.9 2-2c0-.74-.4-1.38-1-1.72V8h3v8H8V8h2V6H6v12h12V6z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_nfc_enabled.xml b/packages/SystemUI/res/drawable/ic_qs_nfc_enabled.xml
new file mode 100644
index 0000000..becb18a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_nfc_enabled.xml
@@ -0,0 +1,31 @@
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:pathData="M4 20h16V4H4v16z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1 .9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0
+18H4V4h16v16zM18 6h-5c-1.1 0-2 .9-2 2v2.28c-.6 .35 -1 .98-1 1.72 0 1.1 .9 2 2
+2s2-.9 2-2c0-.74-.4-1.38-1-1.72V8h3v8H8V8h2V6H6v12h12V6z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml b/packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml
new file mode 100644
index 0000000..a85beb8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+  <solid android:color="#61FFFFFF" />
+  <corners android:radius="8dp"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 78d211f..6ffdbcb 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -75,6 +75,9 @@
     <!-- Height of a large notification in the status bar -->
     <dimen name="notification_max_height">284dp</dimen>
 
+    <!-- Height of an ambient notification on ambient display -->
+    <dimen name="notification_ambient_height">400dp</dimen>
+
     <!-- Height of a heads up notification in the status bar for legacy custom views -->
     <dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
 
diff --git a/packages/SystemUI/res/values/dimens_grid.xml b/packages/SystemUI/res/values/dimens_grid.xml
index 94ffdb1..0b9836ff 100644
--- a/packages/SystemUI/res/values/dimens_grid.xml
+++ b/packages/SystemUI/res/values/dimens_grid.xml
@@ -21,5 +21,6 @@
   <dimen name="recents_grid_padding_task_view">20dp</dimen>
   <dimen name="recents_grid_task_view_header_height">44dp</dimen>
   <dimen name="recents_grid_task_view_header_button_padding">8dp</dimen>
+  <dimen name="recents_grid_task_view_focused_frame_thickness">8dp</dimen>
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 69515fa..a17430a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -760,6 +760,12 @@
     <string name="quick_settings_work_mode_label">Work mode</string>
     <!-- QuickSettings: Label for the toggle to activate Night display (renamed "Night Light" with title caps). [CHAR LIMIT=20] -->
     <string name="quick_settings_night_display_label">Night Light</string>
+    <!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_nfc_label">NFC</string>
+    <!-- QuickSettings: NFC (off) [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_nfc_off">NFC is disabled</string>
+    <!-- QuickSettings: NFC (on) [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_nfc_on">NFC is enabled</string>
 
     <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
     <string name="recents_empty_message">No recent items</string>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index e081b53..00d2298 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -37,7 +37,7 @@
 
     interface Callback {
         default void onNewNotifications() {}
-        default void onBuzzBeepBlinked() {}
+        default void onNotificationHeadsUp() {}
         default void onNotificationLight(boolean on) {}
         default void onPowerSaveChanged(boolean active) {}
     }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 84b22ea..db5a392 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -309,7 +309,7 @@
 
     private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
         @Override
-        public void onBuzzBeepBlinked() {
+        public void onNotificationHeadsUp() {
             onNotification();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
new file mode 100644
index 0000000..967c922
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2016, The Android Open Source Project
+ * Contributed by the Paranoid Android 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.qs.tiles;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.nfc.NfcAdapter;
+import android.provider.Settings;
+import android.widget.Switch;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+
+/** Quick settings tile: Enable/Disable NFC **/
+public class NfcTile extends QSTile<QSTile.BooleanState> {
+
+    private NfcAdapter mAdapter;
+
+    private boolean mListening;
+
+    public NfcTile(Host host) {
+        super(host);
+    }
+
+    @Override
+    public BooleanState newTileState() {
+        return new BooleanState();
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        mListening = listening;
+        if (mListening) {
+            mContext.registerReceiver(mNfcReceiver,
+                    new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED));
+            if (mAdapter == null) {
+                try {
+                    mAdapter = NfcAdapter.getNfcAdapter(mContext);
+                } catch (UnsupportedOperationException e) {
+                    mAdapter = null;
+                }
+            }
+        } else {
+            mContext.unregisterReceiver(mNfcReceiver);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC);
+    }
+
+    @Override
+    protected void handleUserSwitch(int newUserId) {
+    }
+
+    @Override
+    public Intent getLongClickIntent() {
+        return new Intent(Settings.ACTION_NFC_SETTINGS);
+    }
+
+    @Override
+    protected void handleClick() {
+        if (mAdapter == null) return;
+        MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
+        if (!mAdapter.isEnabled()) {
+            mAdapter.enable();
+        } else {
+            mAdapter.disable();
+        }
+    }
+
+    @Override
+    protected void handleSecondaryClick() {
+        handleClick();
+    }
+
+    @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_nfc_label);
+    }
+
+    @Override
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        final Drawable mEnable = mContext.getDrawable(R.drawable.ic_qs_nfc_enabled);
+        final Drawable mDisable = mContext.getDrawable(R.drawable.ic_qs_nfc_disabled);
+        state.value = mAdapter == null ? false : mAdapter.isEnabled();
+        state.label = mContext.getString(R.string.quick_settings_nfc_label);
+        state.icon = new DrawableIcon(state.value ? mEnable : mDisable);
+        state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
+                = Switch.class.getName();
+        state.contentDescription = state.label;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.QS_NFC;
+    }
+
+    @Override
+    protected String composeChangeAnnouncement() {
+        if (mState.value) {
+            return mContext.getString(R.string.quick_settings_nfc_on);
+        } else {
+            return mContext.getString(R.string.quick_settings_nfc_off);
+        }
+    }
+
+    private BroadcastReceiver mNfcReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            refreshState();
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 914035b..a7f6b70 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -50,7 +50,7 @@
     /**
      * Returns the task to focus given the current launch state.
      */
-    public int getInitialFocusTaskIndex(int numTasks) {
+    public int getInitialFocusTaskIndex(int numTasks, boolean useGridLayout) {
         RecentsDebugFlags debugFlags = Recents.getDebugFlags();
         RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
         if (launchedFromApp) {
@@ -66,6 +66,11 @@
                 return numTasks - 1;
             }
 
+            if (useGridLayout) {
+                // If coming from another app to the grid layout, focus the front most task
+                return numTasks - 1;
+            }
+
             // If coming from another app, focus the next task
             return Math.max(0, numTasks - 2);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 2f8a78b..cf6357b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -745,8 +745,12 @@
             Task toTask = new Task();
             TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask,
                     windowOverrideRect);
-            Bitmap thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform,
-                    mThumbTransitionBitmapCache);
+            // When using a grid layout, the header is already visible on screen at the target
+            // location, making it unnecessary to draw it in the transition thumbnail.
+            Bitmap thumbnail = stackView.useGridLayout()
+                    ? mThumbTransitionBitmapCache.createAshmemBitmap()
+                    : drawThumbnailTransitionBitmap(toTask, toTransform,
+                            mThumbTransitionBitmapCache);
             if (thumbnail != null) {
                 RectF toTaskRect = toTransform.rect;
                 return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
@@ -777,7 +781,6 @@
         // Get the transform for the running task
         stackView.updateLayoutAlgorithm(true /* boundScroll */);
         stackView.updateToInitialState();
-        boolean isInSplitScreen = Recents.getSystemServices().hasDockedTask();
         stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
                 stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect);
         return mTmpTransform;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 178cb9f..9b25ef8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -247,6 +247,9 @@
      */
     public static class DockState implements DropTarget {
 
+        public static final int DOCK_AREA_BG_COLOR = 0xFFffffff;
+        public static final int DOCK_AREA_GRID_BG_COLOR = 0xFF000000;
+
         // The rotation to apply to the hint text
         @Retention(RetentionPolicy.SOURCE)
         @IntDef({HORIZONTAL, VERTICAL})
@@ -319,7 +322,8 @@
             private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation,
                     int hintTextResId) {
                 dockAreaAlpha = areaAlpha;
-                dockAreaOverlay = new ColorDrawable(0xFFffffff);
+                dockAreaOverlay = new ColorDrawable(Recents.getConfiguration().isGridEnabled
+                        ? DOCK_AREA_GRID_BG_COLOR : DOCK_AREA_BG_COLOR);
                 dockAreaOverlay.setAlpha(0);
                 hintTextAlpha = hintAlpha;
                 hintTextOrientation = hintOrientation;
@@ -435,7 +439,7 @@
          * @param createMode used to pass to ActivityManager to dock the task
          * @param touchArea the area in which touch will initiate this dock state
          * @param dockArea the visible dock area
-         * @param expandedTouchDockArea the areain which touch will continue to dock after entering
+         * @param expandedTouchDockArea the area in which touch will continue to dock after entering
          *                              the initial touch area.  This is also the new dock area to
          *                              draw.
          */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index ed86d4c..a2ee4c5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -105,6 +105,7 @@
     private static final Interpolator ENTER_WHILE_DOCKING_INTERPOLATOR =
             Interpolators.LINEAR_OUT_SLOW_IN;
 
+    private final int mEnterAndExitFromHomeTranslationOffset;
     private TaskStackView mStackView;
 
     private TaskViewTransform mTmpTransform = new TaskViewTransform();
@@ -113,6 +114,8 @@
 
     public TaskStackAnimationHelper(Context context, TaskStackView stackView) {
         mStackView = stackView;
+        mEnterAndExitFromHomeTranslationOffset = Recents.getConfiguration().isGridEnabled
+                ? 0 : DOUBLE_FRAME_OFFSET_MS;
     }
 
     /**
@@ -260,7 +263,7 @@
                 AnimationProps taskAnimation = new AnimationProps()
                         .setInitialPlayTime(AnimationProps.BOUNDS,
                                 Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) *
-                                        DOUBLE_FRAME_OFFSET_MS)
+                                        mEnterAndExitFromHomeTranslationOffset)
                         .setStartDelay(AnimationProps.ALPHA,
                                 Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) *
                                         FRAME_OFFSET_MS)
@@ -321,7 +324,7 @@
             AnimationProps taskAnimation;
             if (animated) {
                 int delay = Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS , taskIndexFromFront) *
-                        DOUBLE_FRAME_OFFSET_MS;
+                        mEnterAndExitFromHomeTranslationOffset;
                 taskAnimation = new AnimationProps()
                         .setStartDelay(AnimationProps.BOUNDS, delay)
                         .setDuration(AnimationProps.BOUNDS, EXIT_TO_HOME_TRANSLATION_DURATION)
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index a58896a..4fa7ecb5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -291,6 +291,9 @@
     @ViewDebug.ExportedProperty(category="recents")
     private int mStackBottomOffset;
 
+    /** The height, in pixels, of each task view's title bar. */
+    private int mTitleBarHeight;
+
     // The paths defining the motion of the tasks when the stack is focused and unfocused
     private Path mUnfocusedCurve;
     private Path mFocusedCurve;
@@ -403,6 +406,14 @@
         mBaseBottomMargin = res.getDimensionPixelSize(R.dimen.recents_layout_bottom_margin);
         mFreeformStackGap =
                 res.getDimensionPixelSize(R.dimen.recents_freeform_layout_bottom_margin);
+        mTitleBarHeight = getDimensionForDevice(mContext,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_grid_task_view_header_height);
     }
 
     /**
@@ -903,12 +914,17 @@
      * Transforms the given {@param transformOut} to the screen coordinates, overriding the current
      * window rectangle with {@param windowOverrideRect} if non-null.
      */
-    public TaskViewTransform transformToScreenCoordinates(TaskViewTransform transformOut,
+    TaskViewTransform transformToScreenCoordinates(TaskViewTransform transformOut,
             Rect windowOverrideRect) {
         Rect windowRect = windowOverrideRect != null
                 ? windowOverrideRect
                 : Recents.getSystemServices().getWindowRect();
         transformOut.rect.offset(windowRect.left, windowRect.top);
+        if (useGridLayout()) {
+            // Draw the thumbnail a little lower to perfectly coincide with the view we are
+            // transitioning to, where the header bar has already been drawn.
+            transformOut.rect.offset(0, mTitleBarHeight);
+        }
         return transformOut;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index b7686ce..fc2550a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -70,6 +70,7 @@
 import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
 import com.android.systemui.recents.events.activity.PackagesChangedEvent;
 import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
 import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
 import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
@@ -93,6 +94,7 @@
 import com.android.systemui.recents.model.TaskStack;
 
 import com.android.systemui.recents.views.grid.GridTaskView;
+import com.android.systemui.recents.views.grid.TaskViewFocusFrame;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -206,6 +208,10 @@
     private int mLastWidth;
     private int mLastHeight;
 
+    // We keep track of the task view focused by user interaction and draw a frame around it in the
+    // grid layout.
+    private TaskViewFocusFrame mTaskViewFocusFrame;
+
     // A convenience update listener to request updating clipping of tasks
     private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
             new ValueAnimator.AnimatorUpdateListener() {
@@ -265,6 +271,14 @@
         mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
         mDisplayRect = ssp.getDisplayRect();
 
+        // Create a frame to draw around the focused task view
+        if (Recents.getConfiguration().isGridEnabled) {
+            mTaskViewFocusFrame = new TaskViewFocusFrame(mContext, this,
+                mLayoutAlgorithm.mTaskGridLayoutAlgorithm);
+            addView(mTaskViewFocusFrame);
+            getViewTreeObserver().addOnGlobalFocusChangeListener(mTaskViewFocusFrame);
+        }
+
         int taskBarDismissDozeDelaySeconds = getResources().getInteger(
                 R.integer.recents_task_bar_dismiss_delay_seconds);
         mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
@@ -878,7 +892,7 @@
      *
      * @return whether or not the stack will scroll as a part of this focus change
      */
-    private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
+    public boolean setFocusedTask(int taskIndex, boolean scrollToTask,
             final boolean requestViewFocus) {
         return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
     }
@@ -888,7 +902,7 @@
      *
      * @return whether or not the stack will scroll as a part of this focus change
      */
-    private boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
+    public boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
             boolean requestViewFocus, int timerIndicatorDuration) {
         // Find the next task to focus
         int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
@@ -940,6 +954,10 @@
                     newFocusedTaskView.setFocusedState(true, requestViewFocus);
                 }
             }
+            // Any time a task view gets the focus, we move the focus frame around it.
+            if (mTaskViewFocusFrame != null) {
+                mTaskViewFocusFrame.moveGridTaskViewFocus(getChildViewForTask(newFocusedTask));
+            }
         }
         return willScroll;
     }
@@ -1005,20 +1023,28 @@
             float stackScroll = mStackScroller.getStackScroll();
             ArrayList<Task> tasks = mStack.getStackTasks();
             int taskCount = tasks.size();
-            if (forward) {
-                // Walk backwards and focus the next task smaller than the current stack scroll
-                for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
-                    float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
-                    if (Float.compare(taskP, stackScroll) <= 0) {
-                        break;
-                    }
-                }
+            if (useGridLayout()) {
+                // For the grid layout, we directly set focus to the most recently used task
+                // no matter we're moving forwards or backwards.
+                newIndex = taskCount - 1;
             } else {
-                // Walk forwards and focus the next task larger than the current stack scroll
-                for (newIndex = 0; newIndex < taskCount; newIndex++) {
-                    float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
-                    if (Float.compare(taskP, stackScroll) >= 0) {
-                        break;
+                // For the grid layout we pick a proper task to focus, according to the current
+                // stack scroll.
+                if (forward) {
+                    // Walk backwards and focus the next task smaller than the current stack scroll
+                    for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
+                        float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
+                        if (Float.compare(taskP, stackScroll) <= 0) {
+                            break;
+                        }
+                    }
+                } else {
+                    // Walk forwards and focus the next task larger than the current stack scroll
+                    for (newIndex = 0; newIndex < taskCount; newIndex++) {
+                        float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
+                        if (Float.compare(taskP, stackScroll) >= 0) {
+                            break;
+                        }
                     }
                 }
             }
@@ -1037,20 +1063,23 @@
     /**
      * Resets the focused task.
      */
-    void resetFocusedTask(Task task) {
+    public void resetFocusedTask(Task task) {
         if (task != null) {
             TaskView tv = getChildViewForTask(task);
             if (tv != null) {
                 tv.setFocusedState(false, false /* requestViewFocus */);
             }
         }
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+        }
         mFocusedTask = null;
     }
 
     /**
      * Returns the focused task.
      */
-    Task getFocusedTask() {
+    public Task getFocusedTask() {
         return mFocusedTask;
     }
 
@@ -1253,6 +1282,9 @@
         for (int i = 0; i < taskViewCount; i++) {
             measureTaskView(mTmpTaskViews.get(i));
         }
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.measure();
+        }
 
         setMeasuredDimension(width, height);
         mLastWidth = width;
@@ -1287,6 +1319,9 @@
         for (int i = 0; i < taskViewCount; i++) {
             layoutTaskView(changed, mTmpTaskViews.get(i));
         }
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.layout();
+        }
 
         if (changed) {
             if (mStackScroller.isScrollOutOfBounds()) {
@@ -1339,10 +1374,19 @@
         // until after the enter-animation
         RecentsConfiguration config = Recents.getConfiguration();
         RecentsActivityLaunchState launchState = config.getLaunchState();
-        int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount());
-        if (focusedTaskIndex != -1) {
-            setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
-                    false /* requestViewFocus */);
+
+        // We set the initial focused task view iff the following conditions are satisfied:
+        // 1. Recents is showing task views in stack layout.
+        // 2. Recents is launched with ALT + TAB.
+        boolean setFocusOnFirstLayout = !useGridLayout() ||
+            Recents.getConfiguration().getLaunchState().launchedWithAltTab;
+        if (setFocusOnFirstLayout) {
+            int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount(),
+                useGridLayout());
+            if (focusedTaskIndex != -1) {
+                setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
+                        false /* requestViewFocus */);
+            }
         }
         updateStackActionButtonVisibility();
     }
@@ -1443,6 +1487,11 @@
         // Remove the task from the ignored set
         removeIgnoreTask(removedTask);
 
+        // Resize the grid layout task view focus frame
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.resize();
+        }
+
         // If requested, relayout with the given animation
         if (animation != null) {
             updateLayoutAlgorithm(true /* boundScroll */);
@@ -1740,10 +1789,18 @@
         int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
         animateFreeformWorkspaceBackgroundAlpha(0, new AnimationProps(taskViewExitToHomeDuration,
                 Interpolators.FAST_OUT_SLOW_IN));
+
+        // Dismiss the grid task view focus frame
+        if (mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+        }
     }
 
     public final void onBusEvent(DismissFocusedTaskViewEvent event) {
         if (mFocusedTask != null) {
+            if (mTaskViewFocusFrame != null) {
+                mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+            }
             TaskView tv = getChildViewForTask(mFocusedTask);
             if (tv != null) {
                 tv.dismissTask();
@@ -2073,6 +2130,12 @@
         mResetToInitialStateWhenResized = true;
     }
 
+    public final void onBusEvent(RecentsVisibilityChangedEvent event) {
+        if (!event.visible && mTaskViewFocusFrame != null) {
+            mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+        }
+    }
+
     public void reloadOnConfigurationChange() {
         mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
         mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
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 33fa3b0..5817e92 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -342,8 +342,9 @@
                         mSv.invalidate();
                     }
 
-                    // Reset the focused task after the user has scrolled
-                    if (!mSv.mTouchExplorationEnabled) {
+                    // Reset the focused task after the user has scrolled, but we have no scrolling
+                    // in grid layout and therefore we don't want to reset the focus there.
+                    if (!mSv.mTouchExplorationEnabled && !mSv.useGridLayout()) {
                         mSv.resetFocusedTask(mSv.getFocusedTask());
                     }
                 } else if (mActiveTaskView == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
index 6fc4ad7..70536b1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
@@ -51,6 +51,9 @@
     private float mAppAspectRatio;
     private Rect mSystemInsets = new Rect();
 
+    /** The thickness of the focused task view frame. */
+    private int mFocusedFrameThickness;
+
     /**
      * When the amount of tasks is determined, the size and position of every task view can be
      * decided. Each instance of TaskGridRectInfo store the task view information for a certain
@@ -137,6 +140,9 @@
     public void reloadOnConfigurationChange(Context context) {
         Resources res = context.getResources();
         mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
+        mFocusedFrameThickness = res.getDimensionPixelSize(
+            R.dimen.recents_grid_task_view_focused_frame_thickness);
+
         mTaskGridRect = new Rect();
         mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
 
@@ -159,6 +165,10 @@
      */
     public TaskViewTransform getTransform(int taskIndex, int taskCount,
         TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
+        if (taskCount == 0) {
+            transformOut.reset();
+            return transformOut;
+        }
 
         TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
         mTaskGridRect.set(gridInfo.size);
@@ -174,7 +184,7 @@
 
         // We also need to invert the index in order to display the most recent tasks first.
         int taskLayoutIndex = taskCount - taskIndex - 1;
-        boolean isTaskViewVisible = (taskLayoutIndex < MAX_LAYOUT_TASK_COUNT);
+        boolean isTaskViewVisible = taskLayoutIndex < MAX_LAYOUT_TASK_COUNT;
 
         // Fill out the transform
         transformOut.scale = 1f;
@@ -223,7 +233,18 @@
         return buttonRect;
     }
 
+    public void updateTaskGridRect(int taskCount) {
+        if (taskCount > 0) {
+            TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
+            mTaskGridRect.set(gridInfo.size);
+        }
+    }
+
     public Rect getTaskGridRect() {
         return mTaskGridRect;
     }
+
+    public int getFocusFrameThickness() {
+        return mFocusedFrameThickness;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
new file mode 100644
index 0000000..86ed583
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 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.recents.views.grid;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
+import com.android.systemui.R;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskStackView;
+
+public class TaskViewFocusFrame extends View implements OnGlobalFocusChangeListener {
+
+    private TaskStackView mSv;
+    private TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
+    public TaskViewFocusFrame(Context context) {
+        this(context, null);
+    }
+
+    public TaskViewFocusFrame(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr,
+        int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        setBackground(mContext.getDrawable(
+            R.drawable.recents_grid_task_view_focus_frame_background));
+        setFocusable(false);
+        hide();
+    }
+
+    public TaskViewFocusFrame(Context context, TaskStackView stackView,
+        TaskGridLayoutAlgorithm taskGridLayoutAlgorithm) {
+        this(context);
+        mSv = stackView;
+        mTaskGridLayoutAlgorithm = taskGridLayoutAlgorithm;
+    }
+
+    /**
+     * Measure the width and height of the focus frame according to the current grid task view size.
+     */
+    public void measure() {
+        int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
+        Rect rect = mTaskGridLayoutAlgorithm.getTaskGridRect();
+        measure(
+            MeasureSpec.makeMeasureSpec(rect.width() + thickness * 2, MeasureSpec.EXACTLY),
+            MeasureSpec.makeMeasureSpec(rect.height() + thickness * 2, MeasureSpec.EXACTLY));
+    }
+
+    /**
+     * Layout the focus frame with its size.
+     */
+    public void layout() {
+        layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
+    }
+
+    /**
+     * Update the current size of grid task view and the focus frame.
+     */
+    public void resize() {
+        if (mSv.useGridLayout()) {
+            mTaskGridLayoutAlgorithm.updateTaskGridRect(mSv.getStack().getTaskCount());
+            measure();
+            requestLayout();
+        }
+    }
+
+    /**
+     * Move the task view focus frame to surround the newly focused view. If it's {@code null} or
+     * it's not an instance of GridTaskView, we hide the focus frame.
+     * @param newFocus The newly focused view.
+     */
+    public void moveGridTaskViewFocus(View newFocus) {
+        if (mSv.useGridLayout()) {
+            // The frame only shows up in the grid layout. It shouldn't show up in the stack
+            // layout including when we're in the split screen.
+            if (newFocus instanceof GridTaskView) {
+                // If the focus goes to a GridTaskView, we show the frame and layout it.
+                int[] location = new int[2];
+                newFocus.getLocationInWindow(location);
+                int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
+                setTranslationX(location[0] - thickness);
+                setTranslationY(location[1] - thickness);
+                show();
+            } else {
+                // If focus goes to other views, we hide the frame.
+                hide();
+            }
+        }
+    }
+
+    @Override
+    public void onGlobalFocusChanged(View oldFocus, View newFocus) {
+        if (!mSv.useGridLayout()) {
+            return;
+        }
+        if (newFocus == null) {
+            // We're going to touch mode, unset the focus.
+            moveGridTaskViewFocus(null);
+            return;
+        }
+        if (oldFocus == null) {
+            // We're returning from touch mode, set the focus to the previously focused task.
+            final TaskStack stack = mSv.getStack();
+            final int taskCount = stack.getTaskCount();
+            final int k = stack.indexOfStackTask(mSv.getFocusedTask());
+            final int taskIndexToFocus = k == -1 ? (taskCount - 1) : (k % taskCount);
+            mSv.setFocusedTask(taskIndexToFocus, false, true);
+        }
+    }
+
+    private void show() {
+        setAlpha(1f);
+    }
+
+    private void hide() {
+        setAlpha(0f);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 19034f8..faf143e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1132,6 +1132,11 @@
                 // Post to ensure the the guts are properly laid out.
                 guts.post(new Runnable() {
                     public void run() {
+                        if (row.getWindowToken() == null) {
+                            Log.e(TAG, "Trying to show notification guts, but not attached to "
+                                    + "window");
+                            return;
+                        }
                         dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */,
                                 false /* animate */);
                         guts.setVisibility(View.VISIBLE);
@@ -1519,6 +1524,7 @@
         final RemoteViews bigContentView = entry.cachedBigContentView;
         final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
         final RemoteViews publicContentView = entry.cachedPublicContentView;
+        final RemoteViews ambientContentView = entry.cachedAmbientContentView;
 
         if (contentView == null) {
             Log.v(TAG, "no contentView for: " + sbn.getNotification());
@@ -1599,6 +1605,7 @@
         View bigContentViewLocal = null;
         View headsUpContentViewLocal = null;
         View publicViewLocal = null;
+        View ambientViewLocal = null;
         try {
             contentViewLocal = contentView.apply(
                     sbn.getPackageContext(mContext),
@@ -1621,6 +1628,11 @@
                         sbn.getPackageContext(mContext),
                         contentContainerPublic, mOnClickHandler);
             }
+            if (ambientContentView != null) {
+                ambientViewLocal = ambientContentView.apply(
+                        sbn.getPackageContext(mContext),
+                        contentContainer, mOnClickHandler);
+            }
 
             if (contentViewLocal != null) {
                 contentViewLocal.setIsRootNamespace(true);
@@ -1638,6 +1650,11 @@
                 publicViewLocal.setIsRootNamespace(true);
                 contentContainerPublic.setContractedChild(publicViewLocal);
             }
+
+            if (ambientViewLocal != null) {
+                ambientViewLocal.setIsRootNamespace(true);
+                contentContainer.setAmbientChild(ambientViewLocal);
+            }
         }
         catch (RuntimeException e) {
             final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
@@ -2134,6 +2151,7 @@
                 row.setOnKeyguard(false);
                 row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
             }
+            entry.row.setShowAmbient(isDozing());
             int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
                     entry.notification) && !entry.row.isRemoved();
@@ -2171,6 +2189,10 @@
         mStackScroller.changeViewPosition(mNotificationShelf, mStackScroller.getChildCount() - 3);
     }
 
+    public boolean isDozing() {
+        return false;
+    }
+
     public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
         return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
     }
@@ -2383,7 +2405,7 @@
             Log.d(TAG, "failed to query dream manager", e);
         }
 
-        if (!inUse) {
+        if (!inUse && !isDozing()) {
             if (DEBUG) {
                 Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 173a110..93c48f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -74,6 +74,7 @@
     private int mMaxHeadsUpHeight;
     private int mNotificationMinHeight;
     private int mNotificationMaxHeight;
+    private int mNotificationAmbientHeight;
     private int mIncreasedPaddingBetweenElements;
 
     /** Does this row contain layouts that can adapt to row expansion */
@@ -197,6 +198,7 @@
     private float mContentTransformationAmount;
     private boolean mIconsVisible = true;
     private boolean mAboveShelf;
+    private boolean mShowAmbient;
     private boolean mIsLastChild;
     private Runnable mOnDismissRunnable;
 
@@ -326,7 +328,8 @@
                         != com.android.internal.R.id.status_bar_latest_event_content;
         int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
                 : mMaxHeadsUpHeight;
-        layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
+        layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
+                mNotificationAmbientHeight);
     }
 
     public StatusBarNotification getStatusBarNotification() {
@@ -954,6 +957,7 @@
         mNotificationMinHeightLegacy = getFontScaledHeight(R.dimen.notification_min_height_legacy);
         mNotificationMinHeight = getFontScaledHeight(R.dimen.notification_min_height);
         mNotificationMaxHeight = getFontScaledHeight(R.dimen.notification_max_height);
+        mNotificationAmbientHeight = getFontScaledHeight(R.dimen.notification_ambient_height);
         mMaxHeadsUpHeightLegacy = getFontScaledHeight(
                 R.dimen.notification_max_heads_up_height_legacy);
         mMaxHeadsUpHeight = getFontScaledHeight(R.dimen.notification_max_heads_up_height);
@@ -1353,6 +1357,8 @@
             return mGuts.getHeight();
         } else if ((isChildInGroup() && !isGroupExpanded())) {
             return mPrivateLayout.getMinHeight();
+        } else if (mShowAmbient) {
+            return getAmbientHeight();
         } else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
             return getMinHeight();
         } else if (mIsSummaryWithChildren && !mOnKeyguard) {
@@ -1683,6 +1689,13 @@
         return showingLayout.getMinHeight();
     }
 
+    private int getAmbientHeight() {
+        NotificationContentView showingLayout = getShowingLayout();
+        return showingLayout.getAmbientChild() != null
+                ? showingLayout.getAmbientChild().getHeight()
+                : getCollapsedHeight();
+    }
+
     @Override
     public int getCollapsedHeight() {
         if (mIsSummaryWithChildren && !mShowingPublic) {
@@ -1879,6 +1892,13 @@
         return mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf);
     }
 
+    public void setShowAmbient(boolean showAmbient) {
+        if (showAmbient != mShowAmbient) {
+            mShowAmbient = showAmbient;
+            notifyHeightChanged(false /* needsAnimation */);
+        }
+    }
+
     public void setAboveShelf(boolean aboveShelf) {
         mAboveShelf = aboveShelf;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 9ce2115..b45cde8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -31,6 +31,7 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
@@ -52,6 +53,7 @@
     private static final int VISIBLE_TYPE_EXPANDED = 1;
     private static final int VISIBLE_TYPE_HEADSUP = 2;
     private static final int VISIBLE_TYPE_SINGLELINE = 3;
+    private static final int VISIBLE_TYPE_AMBIENT = 4;
     public static final int UNDEFINED = -1;
 
     private final Rect mClipBounds = new Rect();
@@ -62,6 +64,7 @@
     private View mExpandedChild;
     private View mHeadsUpChild;
     private HybridNotificationView mSingleLineView;
+    private View mAmbientChild;
 
     private RemoteInputView mExpandedRemoteInput;
     private RemoteInputView mHeadsUpRemoteInput;
@@ -69,6 +72,7 @@
     private NotificationViewWrapper mContractedWrapper;
     private NotificationViewWrapper mExpandedWrapper;
     private NotificationViewWrapper mHeadsUpWrapper;
+    private NotificationViewWrapper mAmbientWrapper;
     private HybridGroupManager mHybridGroupManager;
     private int mClipTopAmount;
     private int mContentHeight;
@@ -81,6 +85,7 @@
     private int mSmallHeight;
     private int mHeadsUpHeight;
     private int mNotificationMaxHeight;
+    private int mNotificationAmbientHeight;
     private StatusBarNotification mStatusBarNotification;
     private NotificationGroupManager mGroupManager;
     private RemoteInputController mRemoteInputController;
@@ -136,10 +141,12 @@
         reset();
     }
 
-    public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
+    public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
+            int ambientHeight) {
         mSmallHeight = smallHeight;
         mHeadsUpHeight = headsUpMaxHeight;
         mNotificationMaxHeight = maxHeight;
+        mNotificationAmbientHeight = ambientHeight;
     }
 
     @Override
@@ -215,6 +222,17 @@
                     MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
             maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
         }
+        if (mAmbientChild != null) {
+            int size = Math.min(maxSize, mNotificationAmbientHeight);
+            ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
+            if (layoutParams.height >= 0) {
+                // An actual height is set
+                size = Math.min(size, layoutParams.height);
+            }
+            mAmbientChild.measure(widthMeasureSpec,
+                    MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
+            maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
+        }
         int ownHeight = Math.min(maxChildHeight, maxSize);
         setMeasuredDimension(width, ownHeight);
     }
@@ -339,6 +357,10 @@
         return mHeadsUpChild;
     }
 
+    public View getAmbientChild() {
+        return mAmbientChild;
+    }
+
     public void setContractedChild(View child) {
         if (mContractedChild != null) {
             mContractedChild.animate().cancel();
@@ -373,6 +395,17 @@
                 mContainingNotification);
     }
 
+    public void setAmbientChild(View child) {
+        if (mAmbientChild != null) {
+            mAmbientChild.animate().cancel();
+            removeView(mAmbientChild);
+        }
+        addView(child);
+        mAmbientChild = child;
+        mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
+                mContainingNotification);
+    }
+
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
@@ -447,6 +480,11 @@
                         com.android.internal.R.dimen.notification_action_list_height);
         }
 
+        if (isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
+            return mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
+                    com.android.internal.R.dimen.notification_action_list_height);
+        }
+
         // Transition between heads-up & expanded, or pinned.
         if (mHeadsUpChild != null && mExpandedChild != null) {
             boolean transitioningBetweenHunAndExpanded =
@@ -651,39 +689,26 @@
     }
 
     private void forceUpdateVisibilities() {
-        boolean contractedVisible = mVisibleType == VISIBLE_TYPE_CONTRACTED
-                || mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED;
-        boolean expandedVisible = mVisibleType == VISIBLE_TYPE_EXPANDED
-                || mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED;
-        boolean headsUpVisible = mVisibleType == VISIBLE_TYPE_HEADSUP
-                || mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP;
-        boolean singleLineVisible = mVisibleType == VISIBLE_TYPE_SINGLELINE
-                || mTransformationStartVisibleType == VISIBLE_TYPE_SINGLELINE;
-        if (!contractedVisible) {
-            mContractedChild.setVisibility(View.INVISIBLE);
+        forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
+        forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
+        forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
+        forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
+        forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
+        // forceUpdateVisibilities cancels outstanding animations without updating the
+        // mAnimationStartVisibleType. Do so here instead.
+        mAnimationStartVisibleType = UNDEFINED;
+    }
+
+    private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
+        if (view == null) {
+            return;
+        }
+        boolean visible = mVisibleType == type
+                || mTransformationStartVisibleType == type;
+        if (!visible) {
+            view.setVisibility(INVISIBLE);
         } else {
-            mContractedWrapper.setVisible(true);
-        }
-        if (mExpandedChild != null) {
-            if (!expandedVisible) {
-                mExpandedChild.setVisibility(View.INVISIBLE);
-            } else {
-                mExpandedWrapper.setVisible(true);
-            }
-        }
-        if (mHeadsUpChild != null) {
-            if (!headsUpVisible) {
-                mHeadsUpChild.setVisibility(View.INVISIBLE);
-            } else {
-                mHeadsUpWrapper.setVisible(true);
-            }
-        }
-        if (mSingleLineView != null) {
-            if (!singleLineVisible) {
-                mSingleLineView.setVisibility(View.INVISIBLE);
-            } else {
-                mSingleLineView.setVisible(true);
-            }
+            wrapper.setVisible(true);
         }
     }
 
@@ -717,19 +742,25 @@
     }
 
     private void updateViewVisibilities(int visibleType) {
-        boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED;
-        mContractedWrapper.setVisible(contractedVisible);
-        if (mExpandedChild != null) {
-            boolean expandedVisible = visibleType == VISIBLE_TYPE_EXPANDED;
-            mExpandedWrapper.setVisible(expandedVisible);
-        }
-        if (mHeadsUpChild != null) {
-            boolean headsUpVisible = visibleType == VISIBLE_TYPE_HEADSUP;
-            mHeadsUpWrapper.setVisible(headsUpVisible);
-        }
-        if (mSingleLineView != null) {
-            boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
-            mSingleLineView.setVisible(singleLineVisible);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
+                mContractedChild, mContractedWrapper);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
+                mExpandedChild, mExpandedWrapper);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
+                mHeadsUpChild, mHeadsUpWrapper);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
+                mSingleLineView, mSingleLineView);
+        updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
+                mAmbientChild, mAmbientWrapper);
+        // updateViewVisibilities cancels outstanding animations without updating the
+        // mAnimationStartVisibleType. Do so here instead.
+        mAnimationStartVisibleType = UNDEFINED;
+    }
+
+    private void updateViewVisibility(int visibleType, int type, View view,
+            TransformableView wrapper) {
+        if (view != null) {
+            wrapper.setVisible(visibleType == type);
         }
     }
 
@@ -779,6 +810,8 @@
                 return mHeadsUpWrapper;
             case VISIBLE_TYPE_SINGLELINE:
                 return mSingleLineView;
+            case VISIBLE_TYPE_AMBIENT:
+                return mAmbientWrapper;
             default:
                 return mContractedWrapper;
         }
@@ -796,6 +829,8 @@
                 return mHeadsUpChild;
             case VISIBLE_TYPE_SINGLELINE:
                 return mSingleLineView;
+            case VISIBLE_TYPE_AMBIENT:
+                return mAmbientChild;
             default:
                 return mContractedChild;
         }
@@ -809,6 +844,8 @@
                 return mHeadsUpWrapper;
             case VISIBLE_TYPE_CONTRACTED:
                 return mContractedWrapper;
+            case VISIBLE_TYPE_AMBIENT:
+                return mAmbientWrapper;
             default:
                 return null;
         }
@@ -818,6 +855,10 @@
      * @return one of the static enum types in this view, calculated form the current state
      */
     public int calculateVisibleType() {
+        if (mDark && !mIsChildInGroup) {
+            // TODO: Handle notification groups
+            return VISIBLE_TYPE_AMBIENT;
+        }
         if (mUserExpanding) {
             int height = !mIsChildInGroup || isGroupExpanded()
                     || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
@@ -890,6 +931,7 @@
         if (mSingleLineView != null && (mVisibleType == VISIBLE_TYPE_SINGLELINE || !dark)) {
             mSingleLineView.setDark(dark, fade, delay);
         }
+        selectLayout(!dark && fade /* animate */, false /* force */);
     }
 
     public void setHeadsUp(boolean headsUp) {
@@ -942,6 +984,9 @@
         if (mHeadsUpChild != null) {
             mHeadsUpWrapper.notifyContentUpdated(entry.notification);
         }
+        if (mAmbientChild != null) {
+            mAmbientWrapper.notifyContentUpdated(entry.notification);
+        }
         updateShowingLegacyBackground();
         mForceSelectNextLayout = true;
         setDark(mDark, false /* animate */, 0 /* delay */);
@@ -1128,6 +1173,9 @@
         if (header == null && mHeadsUpChild != null) {
             header = mHeadsUpWrapper.getNotificationHeader();
         }
+        if (header == null && mAmbientChild != null) {
+            header = mAmbientWrapper.getNotificationHeader();
+        }
         return header;
     }
 
@@ -1195,6 +1243,11 @@
         }
     }
 
+    @VisibleForTesting
+    boolean isAnimatingVisibleType() {
+        return mAnimationStartVisibleType != UNDEFINED;
+    }
+
     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
         mHeadsUpAnimatingAway = headsUpAnimatingAway;
         selectLayout(false /* animate */, true /* force */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index c06e639..458daf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -67,6 +67,7 @@
         public RemoteViews cachedBigContentView;
         public RemoteViews cachedHeadsUpContentView;
         public RemoteViews cachedPublicContentView;
+        public RemoteViews cachedAmbientContentView;
         public CharSequence remoteInputText;
         private int mCachedContrastColor = COLOR_INVALID;
         private int mCachedContrastColorIsFor = COLOR_INVALID;
@@ -126,6 +127,8 @@
                         updatedNotificationBuilder.createHeadsUpContentView();
                 final RemoteViews newPublicNotification
                         = updatedNotificationBuilder.makePublicContentView();
+                final RemoteViews newAmbientNotification
+                        = updatedNotificationBuilder.makeAmbientNotification();
 
                 boolean sameCustomView = Objects.equals(
                         notification.getNotification().extras.getBoolean(
@@ -136,11 +139,13 @@
                         && compareRemoteViews(cachedBigContentView, newBigContentView)
                         && compareRemoteViews(cachedHeadsUpContentView, newHeadsUpContentView)
                         && compareRemoteViews(cachedPublicContentView, newPublicNotification)
+                        && compareRemoteViews(cachedAmbientContentView, newAmbientNotification)
                         && sameCustomView;
                 cachedPublicContentView = newPublicNotification;
                 cachedHeadsUpContentView = newHeadsUpContentView;
                 cachedBigContentView = newBigContentView;
                 cachedContentView = newContentView;
+                cachedAmbientContentView = newAmbientNotification;
             } else {
                 final Notification.Builder builder
                         = Notification.Builder.recoverBuilder(ctx, notification.getNotification());
@@ -149,6 +154,7 @@
                 cachedBigContentView = builder.createBigContentView();
                 cachedHeadsUpContentView = builder.createHeadsUpContentView();
                 cachedPublicContentView = builder.makePublicContentView();
+                cachedAmbientContentView = builder.makeAmbientNotification();
 
                 applyInPlace = false;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 64c4949..e8e9d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -129,6 +129,7 @@
         } else {
             mViewInvertHelper.update(dark);
         }
+        mShelfIcons.setAmbient(dark);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index 7ca2df9..b984c0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -116,8 +116,10 @@
 
     private void resolveTemplateViews(StatusBarNotification notification) {
         mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
-        mPicture.setTag(ImageTransformState.ICON_TAG,
-                notification.getNotification().getLargeIcon());
+        if (mPicture != null) {
+            mPicture.setTag(ImageTransformState.ICON_TAG,
+                    notification.getNotification().getLargeIcon());
+        }
         mTitle = (TextView) mView.findViewById(com.android.internal.R.id.title);
         mText = (TextView) mView.findViewById(com.android.internal.R.id.text);
         final View progress = mView.findViewById(com.android.internal.R.id.progress);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 5047041..a4e5916 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -39,6 +39,7 @@
     private VisibilityLocationProvider mVisibilityLocationProvider;
     private ArraySet<View> mAllowedReorderViews = new ArraySet<>();
     private ArraySet<View> mAddedChildren = new ArraySet<>();
+    private boolean mPulsing;
 
     /**
      * Add a callback to invoke when reordering is allowed again.
@@ -67,8 +68,16 @@
         updateReorderingAllowed();
     }
 
+    /**
+     * @param pulsing whether we are currently pulsing for ambient display.
+     */
+    public void setPulsing(boolean pulsing) {
+        mPulsing = pulsing;
+        updateReorderingAllowed();
+    }
+
     private void updateReorderingAllowed() {
-        boolean reorderingAllowed = !mScreenOn || !mPanelExpanded;
+        boolean reorderingAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing;
         boolean changed = reorderingAllowed && !mReorderingAllowed;
         mReorderingAllowed = reorderingAllowed;
         if (changed) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 01ffe01..b78f748 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -26,6 +26,7 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.Interpolators;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
@@ -41,7 +42,9 @@
     private final Handler mHandler = new Handler();
     private final ScrimController mScrimController;
 
+    private final Context mContext;
     private final View mStackScroller;
+    private final NotificationPanelView mNotificationPanelView;
 
     private boolean mDozing;
     private DozeHost.PulseCallback mPulseCallback;
@@ -52,10 +55,12 @@
     private float mBehindTarget;
 
     public DozeScrimController(ScrimController scrimController, Context context,
-            View stackScroller) {
+            View stackScroller, NotificationPanelView notificationPanelView) {
+        mContext = context;
         mStackScroller = stackScroller;
         mScrimController = scrimController;
         mDozeParameters = new DozeParameters(context);
+        mNotificationPanelView = notificationPanelView;
     }
 
     public void setDozing(boolean dozing, boolean animate) {
@@ -65,10 +70,7 @@
             abortAnimations();
             mScrimController.setDozeBehindAlpha(1f);
             mScrimController.setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? 0f : 1f);
-            if (mDozeParameters.getAlwaysOn()) {
-                mStackScroller.setAlpha(0f);
-                mHandler.postDelayed(() -> mStackScroller.setAlpha(0f), 30);
-            }
+            mNotificationPanelView.setDark(true);
         } else {
             cancelPulsing();
             if (animate) {
@@ -83,9 +85,8 @@
                 mScrimController.setDozeBehindAlpha(0f);
                 mScrimController.setDozeInFrontAlpha(0f);
             }
-            if (mDozeParameters.getAlwaysOn()) {
-                mStackScroller.setAlpha(1f);
-            }
+            // TODO: animate
+            mNotificationPanelView.setDark(false);
         }
     }
 
@@ -123,9 +124,6 @@
         if (isPulsing()) {
             final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
                     || mPulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
-            if (mDozeParameters.getAlwaysOn()) {
-                mStackScroller.setAlpha(1f);
-            }
             startScrimAnimation(true /* inFront */, 0f,
                     mDozeParameters.getPulseInDuration(pickupOrDoubleTap),
                     pickupOrDoubleTap ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT,
@@ -291,9 +289,6 @@
         @Override
         public void run() {
             if (DEBUG) Log.d(TAG, "Pulse out finished");
-            if (mDozeParameters.getAlwaysOn()) {
-                mStackScroller.setAlpha(0f);
-            }
             DozeLog.tracePulseFinish();
 
             // Signal that the pulse is all finished so we can turn the screen off now.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 70beac8ea..c78ec83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -68,6 +68,8 @@
     }
 
     private AccelerateInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
+    private int mClockBottom;
+    private boolean mDark;
 
     /**
      * Refreshes the dimension values.
@@ -86,7 +88,8 @@
     }
 
     public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight,
-            int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount) {
+            int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount,
+            int clockBottom, boolean dark) {
         mMaxKeyguardNotifications = maxKeyguardNotifications;
         mMaxPanelHeight = maxPanelHeight;
         mExpandedHeight = expandedHeight;
@@ -94,6 +97,8 @@
         mHeight = height;
         mKeyguardStatusHeight = keyguardStatusHeight;
         mEmptyDragAmount = emptyDragAmount;
+        mClockBottom = clockBottom;
+        mDark = dark;
     }
 
     public float getMinStackScrollerPadding(int height, int keyguardStatusHeight) {
@@ -115,6 +120,9 @@
                 result.clockY,
                 y + getClockNotificationsPadding() + mKeyguardStatusHeight);
         result.clockAlpha = getClockAlpha(result.clockScale);
+        if (mDark) {
+            result.stackScrollerPadding = mClockBottom + y;
+        }
     }
 
     private float getClockScale(int notificationPadding, int clockY, int startPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 9fb5980..c25a45c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -22,9 +22,7 @@
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.util.AttributeSet;
-import android.util.Property;
 import android.view.View;
-import android.view.animation.Interpolator;
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -32,7 +30,6 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.stack.AnimationFilter;
 import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.HeadsUpAppearInterpolator;
 import com.android.systemui.statusbar.stack.ViewState;
 
 import java.util.HashMap;
@@ -98,6 +95,7 @@
     private int mActualLayoutWidth = NO_VALUE;
     private float mActualPaddingEnd = NO_VALUE;
     private float mActualPaddingStart = NO_VALUE;
+    private boolean mCentered;
     private boolean mChangingViewPositions;
     private int mAddAnimationStartIndex = -1;
     private int mCannedAnimationStartIndex = -1;
@@ -105,6 +103,7 @@
     private int mIconSize;
     private float mOpenedAmount = 0.0f;
     private float mVisualOverflowAdaption;
+    private boolean mDisallowNextAnimation;
 
     public NotificationIconContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -165,6 +164,7 @@
         }
         mAddAnimationStartIndex = -1;
         mCannedAnimationStartIndex = -1;
+        mDisallowNextAnimation = false;
     }
 
     @Override
@@ -310,6 +310,15 @@
                 numDots++;
             }
         }
+        if (mCentered && translationX < getLayoutEnd()) {
+            float delta = (getLayoutEnd() - translationX) / 2;
+            for (int i = 0; i < childCount; i++) {
+                View view = getChildAt(i);
+                IconState iconState = mIconStates.get(view);
+                iconState.xTranslation += delta;
+            }
+        }
+
         if (isLayoutRtl()) {
             for (int i = 0; i < childCount; i++) {
                 View view = getChildAt(i);
@@ -379,6 +388,11 @@
         mChangingViewPositions = changingViewPositions;
     }
 
+    public void setAmbient(boolean ambient) {
+        mCentered = ambient;
+        mDisallowNextAnimation = true;
+    }
+
     public IconState getIconState(StatusBarIconView icon) {
         return mIconStates.get(icon);
     }
@@ -469,7 +483,7 @@
                     animate = true;
                 }
                 icon.setVisibleState(visibleState);
-                if (animate) {
+                if (animate && !mDisallowNextAnimation) {
                     animateTo(icon, animationProperties);
                 } else {
                     super.applyToView(view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index d48819a..3bdd5e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -211,6 +211,7 @@
     private boolean mOpening;
     private int mIndicationBottomPadding;
     private boolean mIsFullWidth;
+    private boolean mDark;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -391,7 +392,9 @@
                     mNotificationStackScroller.getNotGoneChildCount(),
                     getHeight(),
                     mKeyguardStatusView.getHeight(),
-                    mEmptyDragAmount);
+                    mEmptyDragAmount,
+                    mKeyguardStatusView.getClockBottom(),
+                    mDark);
             mClockPositionAlgorithm.run(mClockPositionResult);
             if (animate || mClockAnimator != null) {
                 startClockAnimation(mClockPositionResult.clockY);
@@ -2453,4 +2456,10 @@
             }
         }
     };
+
+    public void setDark(boolean dark) {
+        mDark = dark;
+        mKeyguardStatusView.setDark(dark);
+        positionClockAndNotifications();
+    }
 }
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 5998ed2..191718e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -74,7 +74,6 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.Process;
@@ -879,7 +878,8 @@
         mHeadsUpManager.addListener(mScrimController);
         mStackScroller.setScrimController(mScrimController);
         mStatusBarView.setScrimController(mScrimController);
-        mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller);
+        mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller,
+                mNotificationPanel);
 
         // Other icons
         mLocationController = new LocationControllerImpl(mContext,
@@ -2401,6 +2401,7 @@
         return getBarState() == StatusBarState.KEYGUARD;
     }
 
+    @Override
     public boolean isDozing() {
         return mDozing;
     }
@@ -2487,6 +2488,9 @@
             }
         } else {
             updateNotificationRanking(null);
+            if (isHeadsUp) {
+                mDozeServiceHost.fireNotificationHeadsUp();
+            }
         }
 
     }
@@ -2886,9 +2890,6 @@
 
     @Override // CommandQueue
     public void buzzBeepBlinked() {
-        if (mDozeServiceHost != null) {
-            mDozeServiceHost.fireBuzzBeepBlinked();
-        }
     }
 
     @Override
@@ -3518,6 +3519,9 @@
         if (mSecurityController != null) {
             mSecurityController.onUserSwitched(mCurrentUserId);
         }
+        if (mNetworkController != null) {
+            mNetworkController.onUserSwitched(mCurrentUserId);
+        }
     }
 
     private void resetUserSetupObserver() {
@@ -4234,6 +4238,7 @@
         mDozeScrimController.setDozing(mDozing &&
                 mFingerprintUnlockController.getMode()
                         != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING, animate);
+        updateRowStates();
         Trace.endSection();
     }
 
@@ -4904,9 +4909,9 @@
             }
         }
 
-        public void fireBuzzBeepBlinked() {
+        public void fireNotificationHeadsUp() {
             for (Callback callback : mCallbacks) {
-                callback.onBuzzBeepBlinked();
+                callback.onNotificationHeadsUp();
             }
         }
 
@@ -4950,12 +4955,14 @@
                 public void onPulseStarted() {
                     callback.onPulseStarted();
                     mStackScroller.setPulsing(true);
+                    mVisualStabilityManager.setPulsing(true);
                 }
 
                 @Override
                 public void onPulseFinished() {
                     callback.onPulseFinished();
                     mStackScroller.setPulsing(false);
+                    mVisualStabilityManager.setPulsing(false);
                 }
             }, reason);
         }
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 227ebdf..d4cf533 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -52,6 +52,7 @@
 import com.android.systemui.qs.tiles.HotspotTile;
 import com.android.systemui.qs.tiles.IntentTile;
 import com.android.systemui.qs.tiles.LocationTile;
+import com.android.systemui.qs.tiles.NfcTile;
 import com.android.systemui.qs.tiles.NightDisplayTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.qs.tiles.UserTile;
@@ -440,6 +441,7 @@
         else if (tileSpec.equals("battery")) return new BatteryTile(this);
         else if (tileSpec.equals("saver")) return new DataSaverTile(this);
         else if (tileSpec.equals("night")) return new NightDisplayTile(this);
+        else if (tileSpec.equals("nfc")) return new NfcTile(this);
         // Intent tiles.
         else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
         else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec);
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 9545fd8..395e8f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1883,12 +1883,16 @@
         float previousIncreasedAmount = 0.0f;
         int numShownItems = 0;
         boolean finish = false;
+        int maxDisplayedNotifications = mAmbientState.isDark()
+                ? (mPulsing ? 1 : 0)
+                : mMaxDisplayedNotifications;
+
         for (int i = 0; i < getChildCount(); i++) {
             ExpandableView expandableView = (ExpandableView) getChildAt(i);
             if (expandableView.getVisibility() != View.GONE
                     && !expandableView.hasNoContentHeight()) {
-                if (mMaxDisplayedNotifications != -1
-                        && numShownItems >= mMaxDisplayedNotifications) {
+                if (maxDisplayedNotifications != -1
+                        && numShownItems >= maxDisplayedNotifications) {
                     expandableView = mShelf;
                     finish = true;
                 }
@@ -3486,6 +3490,8 @@
             updateBackground();
             setWillNotDraw(false);
         }
+        updateContentHeight();
+        notifyHeightChangeListener(mShelf);
     }
 
     private void setBackgroundFadeAmount(float fadeAmount) {
@@ -3921,6 +3927,8 @@
     public void setPulsing(boolean pulsing) {
         mPulsing = pulsing;
         updateNotificationAnimationStates();
+        updateContentHeight();
+        notifyHeightChangeListener(mShelf);
     }
 
     public void setFadingOut(boolean fadingOut) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
index be6290b..76bb6c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
@@ -150,4 +150,28 @@
         mVisualStabilityManager.onReorderingFinished();
         assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
     }
+
+    @Test
+    public void testPulsing() {
+        mVisualStabilityManager.setPulsing(true);
+        assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+        mVisualStabilityManager.setPulsing(false);
+        assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+    }
+
+    @Test
+    public void testReorderingAllowedChanges_Pulsing() {
+        mVisualStabilityManager.setPulsing(true);
+        assertEquals(mVisualStabilityManager.isReorderingAllowed(), false);
+        mVisualStabilityManager.setPulsing(false);
+        assertEquals(mVisualStabilityManager.isReorderingAllowed(), true);
+    }
+
+    @Test
+    public void testCallBackCalled_Pulsing() {
+        mVisualStabilityManager.setPulsing(true);
+        mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+        mVisualStabilityManager.setPulsing(false);
+        verify(mCallback).onReorderingAllowed();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
new file mode 100644
index 0000000..3bb9f5f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationContentViewTest {
+
+    NotificationContentView mView;
+    Context mContext;
+
+    @Before
+    public void setup() {
+        ExpandableNotificationRow rowMock = mock(ExpandableNotificationRow.class);
+        when(rowMock.getIntrinsicHeight()).thenReturn(10);
+
+        mContext = InstrumentationRegistry.getTargetContext();
+        mView = new NotificationContentView(mContext, null);
+        mView.setContainingNotification(rowMock);
+        mView.setHeights(10, 20, 30, 40);
+
+        mView.setContractedChild(createViewWithHeight(10));
+        mView.setExpandedChild(createViewWithHeight(20));
+        mView.setHeadsUpChild(createViewWithHeight(30));
+        mView.setAmbientChild(createViewWithHeight(40));
+
+        mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+        mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+    }
+
+    private View createViewWithHeight(int height) {
+        View view = new View(mContext, null);
+        view.setMinimumHeight(height);
+        return view;
+    }
+
+    @Test
+    @UiThreadTest
+    public void animationStartType_getsClearedAfterUpdatingVisibilitiesWithoutAnimation() {
+        mView.setHeadsUp(true);
+        mView.setDark(true, false, 0);
+        mView.setDark(false, true, 0);
+        mView.setHeadsUpAnimatingAway(true);
+        Assert.assertFalse(mView.isAnimatingVisibleType());
+    }
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index dc92f56..690eb88 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3191,8 +3191,100 @@
     // ACTION: Clicking on any search result in Settings.
     ACTION_CLICK_SETTINGS_SEARCH_RESULT = 763;
 
+    // ACTION: Allow Battery optimization for an app
+    APP_SPECIAL_PERMISSION_BATTERY_ALLOW = 764;
+
+    // ACTION: Deny Battery optimization for an app
+    APP_SPECIAL_PERMISSION_BATTERY_DENY = 765;
+
+    // ACTION: Enable Device Admin app
+    APP_SPECIAL_PERMISSION_ADMIN_ALLOW = 766;
+
+    // ACTION: Disable Device Admin app
+    APP_SPECIAL_PERMISSION_ADMIN_DENY = 767;
+
+    // ACTION: Allow "Do Not Disturb access" for an app
+    APP_SPECIAL_PERMISSION_DND_ALLOW = 768;
+
+    // ACTION: Deny "Do Not Disturb access" for an app
+    APP_SPECIAL_PERMISSION_DND_DENY = 769;
+
+    // ACTION: Allow "Draw over other apps" for an app
+    APP_SPECIAL_PERMISSION_APPDRAW_ALLOW = 770;
+
+    // ACTION: Deny "Draw over other apps" for an app
+    APP_SPECIAL_PERMISSION_APPDRAW_DENY = 771;
+
+    // ACTION: Allow "VR helper services" for an app
+    APP_SPECIAL_PERMISSION_VRHELPER_ALLOW = 772;
+
+    // ACTION: Deny "VR helper services" for an app
+    APP_SPECIAL_PERMISSION_VRHELPER_DENY = 773;
+
+    // ACTION: Allow "Modify system settings" for an app
+    APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_ALLOW = 774;
+
+    // ACTION: Deny "Modify system settings" for an app
+    APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_DENY = 775;
+
+    // ACTION: Allow "Notification access" for an app
+    APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW = 776;
+
+    // ACTION: Deny "Notification access" for an app
+    APP_SPECIAL_PERMISSION_NOTIVIEW_DENY = 777;
+
+    // ACTION: "Premium SMS access" for an app - "ask user" option
+    APP_SPECIAL_PERMISSION_PREMIUM_SMS_ASK = 778;
+
+    // ACTION: "Premium SMS access" for an app - "never allow" option
+    APP_SPECIAL_PERMISSION_PREMIUM_SMS_DENY = 779;
+
+    // ACTION: "Premium SMS access" for an app - "always allow" option
+    APP_SPECIAL_PERMISSION_PREMIUM_SMS_ALWAYS_ALLOW = 780;
+
+    // ACTION: Allow "Unrestricted data access" for an app
+    APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW = 781;
+
+    // ACTION: Deny "Unrestricted data access" for an app
+    APP_SPECIAL_PERMISSION_UNL_DATA_DENY = 782;
+
+    // ACTION: Allow "Usage access" for an app
+    APP_SPECIAL_PERMISSION_USAGE_VIEW_ALLOW = 783;
+
+    // ACTION: Deny "Usage access" for an app
+    APP_SPECIAL_PERMISSION_USAGE_VIEW_DENY = 784;
+
+    // OPEN: Settings > Apps > Default Apps > Default browser
+    DEFAULT_BROWSER_PICKER = 785;
+
+    // OPEN: Settings > Apps > Default Apps > Default emergency app
+    DEFAULT_EMERGENCY_APP_PICKER = 786;
+
+    // OPEN: Settings > Apps > Default Apps > Default home
+    DEFAULT_HOME_PICKER = 787;
+
+    // OPEN: Settings > Apps > Default Apps > Default phone
+    DEFAULT_PHONE_PICKER = 788;
+
+    // OPEN: Settings > Apps > Default Apps > Default sms
+    DEFAULT_SMS_PICKER = 789;
+
+    // OPEN: Settings > Apps > Default Apps > Default notification assistant
+    DEFAULT_NOTIFICATION_ASSISTANT = 790;
+
+    // OPEN: Settings > Apps > Default Apps > Warning dialog to confirm selection
+    DEFAULT_APP_PICKER_CONFIRMATION_DIALOG = 791;
+
+    // OPEN: Settings > Apps > Default Apps > Default auto-fill app
+    DEFAULT_AUTO_FILL_PICKER = 792;
+
     // ---- End O Constants, all O constants go above this line ----
 
+    // OPEN: QS NFC tile shown
+    // ACTION: QS NFC tile tapped
+    // CATEGORY: QUICK_SETTINGS
+    QS_NFC = 800;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b34e4e4..ece5149 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -97,6 +97,7 @@
 import com.android.internal.os.SomeArgs;
 import com.android.server.LocalServices;
 
+import com.android.server.policy.AccessibilityShortcutController;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -1489,6 +1490,7 @@
         mInitialized = true;
         updateLegacyCapabilitiesLocked(userState);
         updateServicesLocked(userState);
+        updateAccessibilityShortcutLocked(userState);
         updateWindowsForAccessibilityCallbackLocked(userState);
         updateAccessibilityFocusBehaviorLocked(userState);
         updateFilterKeyEventsLocked(userState);
@@ -1613,7 +1615,7 @@
         somethingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState);
         somethingChanged |= readDisplayMagnificationEnabledSettingLocked(userState);
         somethingChanged |= readAutoclickEnabledSettingLocked(userState);
-
+        somethingChanged |= readAccessibilityShortcutSettingLocked(userState);
         return somethingChanged;
     }
 
@@ -1722,6 +1724,50 @@
         }
     }
 
+    private boolean readAccessibilityShortcutSettingLocked(UserState userState) {
+        String componentNameToEnableString = AccessibilityShortcutController
+                .getTargetServiceComponentNameString(mContext, userState.mUserId);
+        if ((componentNameToEnableString == null) || componentNameToEnableString.isEmpty()) {
+            if (userState.mServiceToEnableWithShortcut == null) {
+                return false;
+            }
+            userState.mServiceToEnableWithShortcut = null;
+            return true;
+        }
+        ComponentName componentNameToEnable =
+            ComponentName.unflattenFromString(componentNameToEnableString);
+        if (componentNameToEnable.equals(userState.mServiceToEnableWithShortcut)) {
+            return false;
+        }
+        userState.mServiceToEnableWithShortcut = componentNameToEnable;
+        return true;
+    }
+
+    /**
+     * Check if the service that will be enabled by the shortcut is installed. If it isn't,
+     * clear the value and the associated setting so a sideloaded service can't spoof the
+     * package name of the default service.
+     *
+     * @param userState
+     */
+    private void updateAccessibilityShortcutLocked(UserState userState) {
+        if (userState.mServiceToEnableWithShortcut == null) {
+            return;
+        }
+        boolean shortcutServiceIsInstalled = false;
+        for (int i = 0; i < userState.mInstalledServices.size(); i++) {
+            if (userState.mInstalledServices.get(i).getComponentName()
+                    .equals(userState.mServiceToEnableWithShortcut)) {
+                shortcutServiceIsInstalled = true;
+            }
+        }
+        if (!shortcutServiceIsInstalled) {
+            userState.mServiceToEnableWithShortcut = null;
+            Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "", userState.mUserId);
+        }
+    }
+
     private boolean canRequestAndRequestsTouchExplorationLocked(Service service) {
         // Service not ready or cannot request the feature - well nothing to do.
         if (!service.canReceiveEventsLocked() || !service.mRequestTouchExplorationMode) {
@@ -1895,44 +1941,63 @@
     }
 
     /**
+     * AIDL-exposed method to be called when the accessibility shortcut is enabled. Requires
+     * permission to write secure settings, since someone with that permission can enable
+     * accessibility services themselves.
+     */
+    public void performAccessibilityShortcut() {
+        if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
+                && (mContext.checkCallingPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+                != PackageManager.PERMISSION_GRANTED)) {
+            throw new SecurityException(
+                    "performAccessibilityShortcut requires the WRITE_SECURE_SETTINGS permission");
+        }
+        synchronized(mLock) {
+            UserState userState = getUserStateLocked(mCurrentUserId);
+            ComponentName serviceName = userState.mServiceToEnableWithShortcut;
+            if (serviceName == null) {
+                return;
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                if (userState.mComponentNameToServiceMap.get(serviceName) == null) {
+                    enableAccessibilityServiceLocked(serviceName, mCurrentUserId);
+                } else {
+                    disableAccessibilityServiceLocked(serviceName, mCurrentUserId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    };
+
+    /**
      * Enables accessibility service specified by {@param componentName} for the {@param userId}.
      */
-    public void enableAccessibilityService(ComponentName componentName, int userId) {
-        synchronized(mLock) {
-            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-                throw new SecurityException("only SYSTEM can call enableAccessibilityService.");
-            }
+    private void enableAccessibilityServiceLocked(ComponentName componentName, int userId) {
+        SettingsStringHelper settingsHelper = new SettingsStringHelper(
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
+        settingsHelper.addService(componentName);
+        settingsHelper.writeToSettings();
 
-            SettingsStringHelper settingsHelper = new SettingsStringHelper(
-                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
-            settingsHelper.addService(componentName);
-            settingsHelper.writeToSettings();
-
-            UserState userState = getUserStateLocked(userId);
-            if (userState.mEnabledServices.add(componentName)) {
-                onUserStateChangedLocked(userState);
-            }
+        UserState userState = getUserStateLocked(userId);
+        if (userState.mEnabledServices.add(componentName)) {
+            onUserStateChangedLocked(userState);
         }
     }
 
     /**
      * Disables accessibility service specified by {@param componentName} for the {@param userId}.
      */
-    public void disableAccessibilityService(ComponentName componentName, int userId) {
-        synchronized(mLock) {
-            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-                throw new SecurityException("only SYSTEM can call disableAccessibility");
-            }
+    private void disableAccessibilityServiceLocked(ComponentName componentName, int userId) {
+        SettingsStringHelper settingsHelper = new SettingsStringHelper(
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
+        settingsHelper.deleteService(componentName);
+        settingsHelper.writeToSettings();
 
-            SettingsStringHelper settingsHelper = new SettingsStringHelper(
-                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
-            settingsHelper.deleteService(componentName);
-            settingsHelper.writeToSettings();
-
-            UserState userState = getUserStateLocked(userId);
-            if (userState.mEnabledServices.remove(componentName)) {
-                onUserStateChangedLocked(userState);
-            }
+        UserState userState = getUserStateLocked(userId);
+        if (userState.mEnabledServices.remove(componentName)) {
+            onUserStateChangedLocked(userState);
         }
     }
 
@@ -4307,6 +4372,8 @@
 
         public ComponentName mServiceChangingSoftKeyboardMode;
 
+        public ComponentName mServiceToEnableWithShortcut;
+
         public int mLastSentClientState = -1;
 
         public int mSoftKeyboardShowMode = 0;
@@ -4439,6 +4506,9 @@
         private final Uri mAccessibilitySoftKeyboardModeUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
 
+        private final Uri mAccessibilityShortcutServiceIdUri = Settings.Secure.getUriFor(
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+
         public AccessibilityContentObserver(Handler handler) {
             super(handler);
         }
@@ -4467,6 +4537,8 @@
                     mHighTextContrastUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
                     mAccessibilitySoftKeyboardModeUri, false, this, UserHandle.USER_ALL);
+            contentResolver.registerContentObserver(
+                    mAccessibilityShortcutServiceIdUri, false, this, UserHandle.USER_ALL);
         }
 
         @Override
@@ -4519,6 +4591,10 @@
                         notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
                         onUserStateChangedLocked(userState);
                     }
+                } else if (mAccessibilityShortcutServiceIdUri.equals(uri)) {
+                    if (readAccessibilityShortcutSettingLocked(userState)) {
+                        onUserStateChangedLocked(userState);
+                    }
                 }
             }
         }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index 3de8a8b..ae21b07 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -202,7 +202,7 @@
 
         final AutoFillServiceInfo info;
         try {
-            info = new AutoFillServiceInfo(component, mUserId);
+            info = new AutoFillServiceInfo(context.getPackageManager(), component, mUserId);
         } catch (PackageManager.NameNotFoundException e) {
             Slog.w(TAG, "Auto-fill service not found: " + component, e);
             mInfo = null;
diff --git a/services/core/java/com/android/server/DiskStatsService.java b/services/core/java/com/android/server/DiskStatsService.java
index dd95f67..962ac6f 100644
--- a/services/core/java/com/android/server/DiskStatsService.java
+++ b/services/core/java/com/android/server/DiskStatsService.java
@@ -95,9 +95,7 @@
             pw.println("File-based Encryption: true");
         }
 
-        if (isCheckin(args)) {
-            reportCachedValues(pw);
-        }
+        reportCachedValues(pw);
 
         // TODO: Read /proc/yaffs and report interesting values;
         // add configurable (through args) performance test parameters.
@@ -130,15 +128,6 @@
         }
     }
 
-    private boolean isCheckin(String[] args) {
-        for (String opt : args) {
-            if ("--checkin".equals(opt)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private void reportCachedValues(PrintWriter pw) {
         try {
             String jsonString = IoUtils.readFileAsString(DISKSTATS_DUMP_FILE);
diff --git a/services/core/java/com/android/server/RecoverySystemService.java b/services/core/java/com/android/server/RecoverySystemService.java
index 2010e64..3c8c699 100644
--- a/services/core/java/com/android/server/RecoverySystemService.java
+++ b/services/core/java/com/android/server/RecoverySystemService.java
@@ -181,7 +181,7 @@
         }
 
         @Override // Binder call
-        public void rebootRecoveryWithCommand(String command, boolean update) {
+        public void rebootRecoveryWithCommand(String command) {
             if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
             synchronized (sRequestLock) {
                 if (!setupOrClearBcb(true, command)) {
@@ -190,10 +190,7 @@
 
                 // Having set up the BCB, go ahead and reboot.
                 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-                // PowerManagerService may additionally request uncrypting the package when it's
-                // to install an update (REBOOT_RECOVERY_UPDATE).
-                pm.reboot(update ? PowerManager.REBOOT_RECOVERY_UPDATE :
-                        PowerManager.REBOOT_RECOVERY);
+                pm.reboot(PowerManager.REBOOT_RECOVERY);
             }
         }
 
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 55d31c3..f9b9d6f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -88,11 +88,13 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IMediaContainerService;
+import com.android.internal.os.AppFuseMount;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
@@ -104,7 +106,7 @@
 import com.android.server.NativeDaemonConnector.Command;
 import com.android.server.NativeDaemonConnector.SensitiveArg;
 import com.android.server.pm.PackageManagerService;
-
+import com.android.server.storage.AppFuseBridge;
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
@@ -135,6 +137,7 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -337,6 +340,15 @@
 
     private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
 
+    /** Holding lock for AppFuse business */
+    private final Object mAppFuseLock = new Object();
+
+    @GuardedBy("mAppFuseLock")
+    private int mNextAppFuseName = 0;
+
+    @GuardedBy("mAppFuseLock")
+    private final SparseArray<Integer> mAppFusePids = new SparseArray<>();
+
     private VolumeInfo findVolumeByIdOrThrow(String id) {
         synchronized (mLock) {
             final VolumeInfo vol = mVolumes.get(id);
@@ -3010,6 +3022,128 @@
         }
     }
 
+
+    class CloseableHolder<T extends AutoCloseable> implements AutoCloseable {
+        @Nullable T mCloseable;
+
+        CloseableHolder(T closeable) {
+            mCloseable = closeable;
+        }
+
+        @Nullable T get() {
+            return mCloseable;
+        }
+
+        @Nullable T release() {
+            final T result = mCloseable;
+            mCloseable = null;
+            return result;
+        }
+
+        @Override
+        public void close() {
+            if (mCloseable != null) {
+                IoUtils.closeQuietly(mCloseable);
+            }
+        }
+    }
+
+    class AppFuseMountScope implements AppFuseBridge.IMountScope {
+        final int mUid;
+        final int mName;
+        final ParcelFileDescriptor mDeviceFd;
+
+        AppFuseMountScope(int uid, int pid, int name) throws NativeDaemonConnectorException {
+            final NativeDaemonEvent event = mConnector.execute(
+                    "appfuse", "mount", uid, Process.myPid(), name);
+            mUid = uid;
+            mName = name;
+            synchronized (mLock) {
+                mAppFusePids.put(name, pid);
+            }
+            if (event.getFileDescriptors() != null &&
+                    event.getFileDescriptors().length > 0) {
+                mDeviceFd = new ParcelFileDescriptor(event.getFileDescriptors()[0]);
+            } else {
+                mDeviceFd = null;
+            }
+        }
+
+        @Override
+        public void close() throws NativeDaemonConnectorException {
+            try {
+                IoUtils.closeQuietly(mDeviceFd);
+                mConnector.execute(
+                        "appfuse", "unmount", mUid, Process.myPid(), mName);
+            } finally {
+                synchronized (mLock) {
+                    mAppFusePids.delete(mName);
+                }
+            }
+        }
+
+        @Override
+        public ParcelFileDescriptor getDeviceFileDescriptor() {
+            return mDeviceFd;
+        }
+    }
+
+    @Override
+    public AppFuseMount mountProxyFileDescriptorBridge() throws RemoteException {
+        final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
+        final int name;
+        synchronized (mAppFuseLock) {
+            name = mNextAppFuseName++;
+        }
+        try (CloseableHolder<AppFuseMountScope> mountScope =
+                new CloseableHolder<>(new AppFuseMountScope(uid, pid, name))) {
+            if (mountScope.get().getDeviceFileDescriptor() == null) {
+                throw new RemoteException("Failed to obtain device FD");
+            }
+
+            // Create communication channel.
+            final ArrayBlockingQueue<Boolean> channel = new ArrayBlockingQueue<>(1);
+            final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createSocketPair();
+            try (CloseableHolder<ParcelFileDescriptor> remote = new CloseableHolder<>(fds[0])) {
+                new Thread(
+                        new AppFuseBridge(mountScope.release(), fds[1], channel),
+                        AppFuseBridge.TAG).start();
+                if (!channel.take()) {
+                    throw new RemoteException("Failed to init AppFuse mount point");
+                }
+
+                return new AppFuseMount(name, remote.release());
+            }
+        } catch (NativeDaemonConnectorException e){
+            throw e.rethrowAsParcelableException();
+        } catch (IOException | InterruptedException error) {
+            throw new RemoteException(error.getMessage());
+        }
+    }
+
+    @Override
+    public ParcelFileDescriptor openProxyFileDescriptor(int mountId, int fileId, int mode) {
+        final int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
+        try {
+            synchronized (mAppFuseLock) {
+                final int expectedPid = mAppFusePids.get(mountId, -1);
+                if (expectedPid == -1) {
+                    Slog.i(TAG, "The mount point has already been unmounted");
+                    return null;
+                }
+                if (expectedPid != pid) {
+                    throw new SecurityException("Mount point was not created by this process.");
+                }
+            }
+            return AppFuseBridge.openFile(uid, mountId, fileId, mode);
+        } catch (FileNotFoundException error) {
+            Slog.e(TAG, "Failed to openProxyFileDescriptor", error);
+            return null;
+        }
+    }
+
     @Override
     public int mkdirs(String callingPkg, String appPath) {
         final int userId = UserHandle.getUserId(Binder.getCallingUid());
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 47c3e6f..a2fb9f9 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1802,6 +1802,9 @@
     }
 
     void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {
+        if (mWindowContainerController == null) {
+            return;
+        }
         final CompatibilityInfo compatInfo =
                 service.compatibilityInfoForPackageLocked(info.applicationInfo);
         final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
diff --git a/services/core/java/com/android/server/am/NativeCrashListener.java b/services/core/java/com/android/server/am/NativeCrashListener.java
index e2870d8..9348023 100644
--- a/services/core/java/com/android/server/am/NativeCrashListener.java
+++ b/services/core/java/com/android/server/am/NativeCrashListener.java
@@ -20,7 +20,6 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructTimeval;
-import android.system.StructUcred;
 import android.system.UnixSocketAddress;
 import android.util.Slog;
 
@@ -105,9 +104,9 @@
 
         if (DEBUG) Slog.i(TAG, "Starting up");
 
-        // The file system entity for this socket is created with 0700 perms, owned
-        // by system:system.  debuggerd runs as root, so is capable of connecting to
-        // it, but 3rd party apps cannot.
+        // The file system entity for this socket is created with 0777 perms, owned
+        // by system:system. selinux restricts things so that only crash_dump can
+        // access it.
         {
             File socketFile = new File(DEBUGGERD_SOCKET_PATH);
             if (socketFile.exists()) {
@@ -121,6 +120,7 @@
                     DEBUGGERD_SOCKET_PATH);
             Os.bind(serverFd, sockAddr);
             Os.listen(serverFd, 1);
+            Os.chmod(DEBUGGERD_SOCKET_PATH, 0777);
 
             while (true) {
                 FileDescriptor peerFd = null;
@@ -129,19 +129,14 @@
                     peerFd = Os.accept(serverFd, null /* peerAddress */);
                     if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
                     if (peerFd != null) {
-                        // Only the superuser is allowed to talk to us over this socket
-                        StructUcred credentials =
-                                Os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED);
-                        if (credentials.uid == 0) {
-                            // the reporting thread may take responsibility for
-                            // acking the debugger; make sure we play along.
-                            consumeNativeCrashData(peerFd);
-                        }
+                        // the reporting thread may take responsibility for
+                        // acking the debugger; make sure we play along.
+                        consumeNativeCrashData(peerFd);
                     }
                 } catch (Exception e) {
                     Slog.w(TAG, "Error handling connection", e);
                 } finally {
-                    // Always ack debuggerd's connection to us.  The actual
+                    // Always ack crash_dump's connection to us.  The actual
                     // byte written is irrelevant.
                     if (peerFd != null) {
                         try {
@@ -194,7 +189,7 @@
         return totalRead;
     }
 
-    // Read the crash report from the debuggerd connection
+    // Read a crash report from the connection
     void consumeNativeCrashData(FileDescriptor fd) {
         if (MORE_DEBUG) Slog.i(TAG, "debuggerd connected");
         final byte[] buf = new byte[4096];
@@ -205,6 +200,10 @@
             Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout);
             Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout);
 
+            // The socket is guarded by an selinux neverallow rule that only
+            // permits crash_dump to connect to it. This allows us to trust the
+            // received values.
+
             // first, the pid and signal number
             int headerBytes = readExactly(fd, buf, 0, 8);
             if (headerBytes != 8) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 71ebad9..f516e99 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -259,6 +259,11 @@
                     int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000);
                     MetricsLogger.histogram(mInjector.getContext(),
                             "framework_locked_boot_completed", uptimeSeconds);
+                    final int MAX_UPTIME_SECONDS = 120;
+                    if (uptimeSeconds > MAX_UPTIME_SECONDS) {
+                        Slog.wtf("SystemServerTiming",
+                                "finishUserBoot took too long. uptimeSeconds=" + uptimeSeconds);
+                    }
                 }
 
                 mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0f3f9ce..df5f01d 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -144,7 +144,8 @@
  */
 public class AudioService extends IAudioService.Stub
         implements AccessibilityManager.TouchExplorationStateChangeListener,
-            AccessibilityManager.AccessibilityStateChangeListener{
+            AccessibilityManager.AccessibilityStateChangeListener,
+            AccessibilityManager.AccessibilityServicesStateChangeListener {
 
     private static final String TAG = "AudioService";
 
@@ -780,7 +781,7 @@
                 TAG,
                 SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
 
-        initA11yMonitoring(mContext);
+        initA11yMonitoring();
         onIndicateSystemReady();
     }
 
@@ -5925,13 +5926,25 @@
     //==========================================================================================
     // Accessibility
 
-    private void initA11yMonitoring(Context ctxt) {
-        AccessibilityManager accessibilityManager =
-                (AccessibilityManager) ctxt.getSystemService(Context.ACCESSIBILITY_SERVICE);
+    /**
+     * Compile-time constant to enable the use of an independent a11y volume:
+     * - set to true to listen to a11y services state changes and read
+     *   the whether any exposes the FLAG_ENABLE_ACCESSIBILITY_VOLUME flag
+     * - set to false to listen to when accessibility services are started (e.g. "TalkBack started")
+     */
+    private static final boolean USE_FLAG_ENABLE_ACCESSIBILITY_VOLUME = true;
+
+    private void initA11yMonitoring() {
+        final AccessibilityManager accessibilityManager =
+                (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
         updateDefaultStreamOverrideDelay(accessibilityManager.isTouchExplorationEnabled());
         updateA11yVolumeAlias(accessibilityManager.isEnabled());
         accessibilityManager.addTouchExplorationStateChangeListener(this);
-        accessibilityManager.addAccessibilityStateChangeListener(this);
+        if (USE_FLAG_ENABLE_ACCESSIBILITY_VOLUME) {
+            accessibilityManager.addAccessibilityServicesStateChangeListener(this);
+        } else {
+            accessibilityManager.addAccessibilityStateChangeListener(this);
+        }
     }
 
     //---------------------------------------------------------------------------------
@@ -5969,21 +5982,31 @@
 
     private static boolean sIndependentA11yVolume = false;
 
+    // implementation of AccessibilityStateChangeListener
     @Override
     public void onAccessibilityStateChanged(boolean enabled) {
         updateA11yVolumeAlias(enabled);
     }
 
-    private void updateA11yVolumeAlias(boolean a11Enabled) {
-        if (DEBUG_VOL) Log.d(TAG, "Accessibility mode changed to " + a11Enabled);
-        // a11y has its own volume stream when a11y service is enabled
-        sIndependentA11yVolume = a11Enabled;
-        // update the volume mapping scheme
-        updateStreamVolumeAlias(true /*updateVolumes*/, TAG);
-        // update the volume controller behavior
-        mVolumeController.setA11yMode(sIndependentA11yVolume ?
-                VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME :
-                    VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME);
+    // implementation of AccessibilityServicesStateChangeListener
+    @Override
+    public void onAccessibilityServicesStateChanged() {
+        final AccessibilityManager accessibilityManager =
+                (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        updateA11yVolumeAlias(accessibilityManager.isAccessibilityVolumeStreamActive());
+    }
+
+    private void updateA11yVolumeAlias(boolean a11VolEnabled) {
+        if (DEBUG_VOL) Log.d(TAG, "Accessibility volume enabled = " + a11VolEnabled);
+        if (sIndependentA11yVolume != a11VolEnabled) {
+            sIndependentA11yVolume = a11VolEnabled;
+            // update the volume mapping scheme
+            updateStreamVolumeAlias(true /*updateVolumes*/, TAG);
+            // update the volume controller behavior
+            mVolumeController.setA11yMode(sIndependentA11yVolume ?
+                    VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME :
+                        VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME);
+        }
     }
 
     //==========================================================================================
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 87f4030..fbb39384 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -25,6 +25,7 @@
 import com.android.internal.inputmethod.InputMethodSubtypeHandle;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.R;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
@@ -1700,6 +1701,7 @@
     // Binder call
     @Override
     public void setCustomPointerIcon(PointerIcon icon) {
+        Preconditions.checkNotNull(icon);
         nativeSetCustomPointerIcon(mPtr, icon);
     }
 
diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java
index c48f430..ee00fdc 100644
--- a/services/core/java/com/android/server/net/NetworkIdentitySet.java
+++ b/services/core/java/com/android/server/net/NetworkIdentitySet.java
@@ -17,6 +17,8 @@
 package com.android.server.net;
 
 import android.net.NetworkIdentity;
+import android.service.NetworkIdentitySetProto;
+import android.util.proto.ProtoOutputStream;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -143,4 +145,14 @@
         final NetworkIdentity anotherIdent = another.iterator().next();
         return ident.compareTo(anotherIdent);
     }
+
+    public void writeToProto(ProtoOutputStream proto, long tag) {
+        final long start = proto.start(tag);
+
+        for (NetworkIdentity ident : this) {
+            ident.writeToProto(proto, NetworkIdentitySetProto.IDENTITIES);
+        }
+
+        proto.end(start);
+    }
 }
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index c45b416..0354300 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -34,9 +34,13 @@
 import android.net.NetworkTemplate;
 import android.net.TrafficStats;
 import android.os.Binder;
+import android.service.NetworkStatsCollectionKeyProto;
+import android.service.NetworkStatsCollectionProto;
+import android.service.NetworkStatsCollectionStatsProto;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.IntArray;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FileRotator;
@@ -532,12 +536,15 @@
                 / mBucketDuration);
     }
 
-    public void dump(IndentingPrintWriter pw) {
+    private ArrayList<Key> getSortedKeys() {
         final ArrayList<Key> keys = Lists.newArrayList();
         keys.addAll(mStats.keySet());
         Collections.sort(keys);
+        return keys;
+    }
 
-        for (Key key : keys) {
+    public void dump(IndentingPrintWriter pw) {
+        for (Key key : getSortedKeys()) {
             pw.print("ident="); pw.print(key.ident.toString());
             pw.print(" uid="); pw.print(key.uid);
             pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
@@ -550,6 +557,29 @@
         }
     }
 
+    public void writeToProto(ProtoOutputStream proto, long tag) {
+        final long start = proto.start(tag);
+
+        for (Key key : getSortedKeys()) {
+            final long startStats = proto.start(NetworkStatsCollectionProto.STATS);
+
+            // Key
+            final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY);
+            key.ident.writeToProto(proto, NetworkStatsCollectionKeyProto.IDENTITY);
+            proto.write(NetworkStatsCollectionKeyProto.UID, key.uid);
+            proto.write(NetworkStatsCollectionKeyProto.SET, key.set);
+            proto.write(NetworkStatsCollectionKeyProto.TAG, key.tag);
+            proto.end(startKey);
+
+            // Value
+            final NetworkStatsHistory history = mStats.get(key);
+            history.writeToProto(proto, NetworkStatsCollectionStatsProto.HISTORY);
+            proto.end(startStats);
+        }
+
+        proto.end(start);
+    }
+
     public void dumpCheckin(PrintWriter pw, long start, long end) {
         dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell");
         dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi");
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index 090a076..80309e1 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -29,9 +29,11 @@
 import android.net.NetworkTemplate;
 import android.net.TrafficStats;
 import android.os.DropBoxManager;
+import android.service.NetworkStatsRecorderProto;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.net.VpnInfo;
 import com.android.internal.util.FileRotator;
@@ -465,6 +467,15 @@
         }
     }
 
+    public void writeToProtoLocked(ProtoOutputStream proto, long tag) {
+        final long start = proto.start(tag);
+        if (mPending != null) {
+            proto.write(NetworkStatsRecorderProto.PENDING_TOTAL_BYTES, mPending.getTotalBytes());
+        }
+        getOrLoadCompleteLocked().writeToProto(proto, NetworkStatsRecorderProto.COMPLETE_HISTORY);
+        proto.end(start);
+    }
+
     public void dumpCheckin(PrintWriter pw, long start, long end) {
         // Only load and dump stats from the requested window
         getOrLoadPartialLocked(start, end).dumpCheckin(pw, start, end);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 386e78b..104c296 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -104,6 +104,8 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.service.NetworkInterfaceProto;
+import android.service.NetworkStatsServiceDumpProto;
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
@@ -115,6 +117,7 @@
 import android.util.Slog;
 import android.util.SparseIntArray;
 import android.util.TrustedTime;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.net.VpnInfo;
@@ -1255,6 +1258,12 @@
         final IndentingPrintWriter pw = new IndentingPrintWriter(rawWriter, "  ");
 
         synchronized (mStatsLock) {
+            if (args.length > 0 && "--proto".equals(args[0])) {
+                // In this case ignore all other arguments.
+                dumpProto(fd);
+                return;
+            }
+
             if (poll) {
                 performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE);
                 pw.println("Forced poll");
@@ -1327,6 +1336,33 @@
         }
     }
 
+    private void dumpProto(FileDescriptor fd) {
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+        // TODO Right now it writes all history.  Should it limit to the "since-boot" log?
+
+        dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_INTERFACES, mActiveIfaces);
+        dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_UID_INTERFACES, mActiveUidIfaces);
+        mDevRecorder.writeToProtoLocked(proto, NetworkStatsServiceDumpProto.DEV_STATS);
+        mXtRecorder.writeToProtoLocked(proto, NetworkStatsServiceDumpProto.XT_STATS);
+        mUidRecorder.writeToProtoLocked(proto, NetworkStatsServiceDumpProto.UID_STATS);
+        mUidTagRecorder.writeToProtoLocked(proto, NetworkStatsServiceDumpProto.UID_TAG_STATS);
+
+        proto.flush();
+    }
+
+    private static void dumpInterfaces(ProtoOutputStream proto, long tag,
+            ArrayMap<String, NetworkIdentitySet> ifaces) {
+        for (int i = 0; i < ifaces.size(); i++) {
+            final long start = proto.start(tag);
+
+            proto.write(NetworkInterfaceProto.INTERFACE, ifaces.keyAt(i));
+            ifaces.valueAt(i).writeToProto(proto, NetworkInterfaceProto.IDENTITIES);
+
+            proto.end(start);
+        }
+    }
+
     /**
      * Return snapshot of current UID statistics, including any
      * {@link TrafficStats#UID_TETHERING} and {@link #mUidOperations} values.
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 48e000d8..2ddf6db 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -21,9 +21,11 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ILauncherApps;
@@ -277,24 +279,11 @@
         @Override
         public ParceledListSlice<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
                 throws RemoteException {
-            ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
-            if (!isUserEnabled(user)) {
-                return null;
-            }
-
-            final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
-            mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-            mainIntent.setPackage(packageName);
-            long ident = Binder.clearCallingIdentity();
-            try {
-                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent,
-                        PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                        user.getIdentifier());
-                return new ParceledListSlice<>(apps);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            return queryActivitiesForUser(
+                    new Intent(Intent.ACTION_MAIN)
+                            .addCategory(Intent.CATEGORY_LAUNCHER)
+                            .setPackage(packageName),
+                    user);
         }
 
         @Override
@@ -318,6 +307,53 @@
         }
 
         @Override
+        public ParceledListSlice getShortcutConfigActivities(String packageName, UserHandle user)
+                throws RemoteException {
+            return queryActivitiesForUser(
+                    new Intent(Intent.ACTION_CREATE_SHORTCUT).setPackage(packageName), user);
+        }
+
+        private ParceledListSlice<ResolveInfo> queryActivitiesForUser(Intent intent,
+                UserHandle user) {
+            ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
+            if (!isUserEnabled(user)) {
+                return null;
+            }
+
+            long ident = injectClearCallingIdentity();
+            try {
+                List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(intent,
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                        user.getIdentifier());
+                return new ParceledListSlice<>(apps);
+            } finally {
+                injectRestoreCallingIdentity(ident);
+            }
+        }
+
+        @Override
+        public IntentSender getShortcutConfigActivityIntent(String callingPackage,
+                ComponentName component, UserHandle user) throws RemoteException {
+            ensureShortcutPermission(callingPackage, user);
+            Preconditions.checkNotNull(component);
+            Preconditions.checkArgument(isUserEnabled(user), "User not enabled");
+
+            // All right, create the sender.
+            Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(component);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return PendingIntent.getActivityAsUser(
+                        mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
+                                | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT,
+                        null, user)
+                        .getIntentSender();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public boolean isPackageEnabled(String packageName, UserHandle user)
                 throws RemoteException {
             ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index d25abbf..d516acf 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -145,6 +145,7 @@
     private static final String ATTR_ABI_OVERRIDE = "abiOverride";
     private static final String ATTR_VOLUME_UUID = "volumeUuid";
     private static final String ATTR_NAME = "name";
+    private static final String ATTR_INSTALL_REASON = "installRason";
 
     /** Automatically destroy sessions older than this */
     private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
@@ -412,6 +413,7 @@
         params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
         params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
         params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
+        params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
 
         final File appIconFile = buildAppIconFile(sessionId);
         if (appIconFile.exists()) {
@@ -484,6 +486,7 @@
         writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
         writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
         writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
+        writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
 
         // Persist app icon if changed since last written
         final File appIconFile = buildAppIconFile(session.sessionId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 66c3d44..af1e007 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -12217,6 +12217,67 @@
         mHandler.sendMessage(msg);
     }
 
+
+    /**
+     * Ensure that the install reason matches what we know about the package installer (e.g. whether
+     * it is acting on behalf on an enterprise or the user).
+     *
+     * Note that the ordering of the conditionals in this method is important. The checks we perform
+     * are as follows, in this order:
+     *
+     * 1) If the install is being performed by a system app, we can trust the app to have set the
+     *    install reason correctly. Thus, we pass through the install reason unchanged, no matter
+     *    what it is.
+     * 2) If the install is being performed by a device or profile owner app, the install reason
+     *    should be enterprise policy. However, we cannot be sure that the device or profile owner
+     *    set the install reason correctly. If the app targets an older SDK version where install
+     *    reasons did not exist yet, or if the app author simply forgot, the install reason may be
+     *    unset or wrong. Thus, we force the install reason to be enterprise policy.
+     * 3) In all other cases, the install is being performed by a regular app that is neither part
+     *    of the system nor a device or profile owner. We have no reason to believe that this app is
+     *    acting on behalf of the enterprise admin. Thus, we check whether the install reason was
+     *    set to enterprise policy and if so, change it to unknown instead.
+     */
+    private int fixUpInstallReason(String installerPackageName, int installerUid,
+            int installReason) {
+        if (checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
+                == PERMISSION_GRANTED) {
+            // If the install is being performed by a system app, we trust that app to have set the
+            // install reason correctly.
+            return installReason;
+        }
+
+        final IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
+            ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+        if (dpm != null) {
+            ComponentName owner = null;
+            try {
+                owner = dpm.getDeviceOwnerComponent(true /* callingUserOnly */);
+                if (owner == null) {
+                    owner = dpm.getProfileOwner(UserHandle.getUserId(installerUid));
+                }
+            } catch (RemoteException e) {
+            }
+            if (owner != null && owner.getPackageName().equals(installerPackageName)) {
+                // If the install is being performed by a device or profile owner, the install
+                // reason should be enterprise policy.
+                return PackageManager.INSTALL_REASON_POLICY;
+            }
+        }
+
+        if (installReason == PackageManager.INSTALL_REASON_POLICY) {
+            // If the install is being performed by a regular app (i.e. neither system app nor
+            // device or profile owner), we have no reason to believe that the app is acting on
+            // behalf of an enterprise. If the app set the install reason to enterprise policy,
+            // change it to unknown instead.
+            return PackageManager.INSTALL_REASON_UNKNOWN;
+        }
+
+        // If the install is being performed by a regular app and the install reason was set to any
+        // value but enterprise policy, leave the install reason unchanged.
+        return installReason;
+    }
+
     void installStage(String packageName, File stagedDir, String stagedCid,
             IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
             String installerPackageName, int installerUid, UserHandle user,
@@ -12238,10 +12299,12 @@
         }
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
+        final int installReason = fixUpInstallReason(installerPackageName, installerUid,
+                sessionParams.installReason);
         final InstallParams params = new InstallParams(origin, null, observer,
                 sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
                 verificationInfo, user, sessionParams.abiOverride,
-                sessionParams.grantedRuntimePermissions, certificates, sessionParams.installReason);
+                sessionParams.grantedRuntimePermissions, certificates, installReason);
         params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
         msg.obj = params;
 
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index c8ddf0a..a156356 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.pm;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -24,6 +25,7 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.LauncherApps.PinItemRequest;
 import android.content.pm.ShortcutInfo;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.util.Log;
@@ -50,18 +52,31 @@
     private static class PinItemRequestInner extends IPinItemRequest.Stub {
         protected final ShortcutRequestPinProcessor mProcessor;
         private final IntentSender mResultIntent;
+        private final int mLauncherUid;
 
         @GuardedBy("this")
         private boolean mAccepted;
 
         private PinItemRequestInner(ShortcutRequestPinProcessor processor,
-                IntentSender resultIntent) {
+                IntentSender resultIntent, int launcherUid) {
             mProcessor = processor;
             mResultIntent = resultIntent;
+            mLauncherUid = launcherUid;
+        }
+
+        /**
+         * Returns true if the caller is same as the default launcher app when this request
+         * object was created.
+         */
+        private boolean isCallerValid() {
+            return mProcessor.isCallerUid(mLauncherUid);
         }
 
         @Override
         public boolean isValid() {
+            if (!isCallerValid()) {
+                return false;
+            }
             // TODO When an app calls requestPinShortcut(), all pending requests should be
             // invalidated.
             synchronized (this) {
@@ -76,6 +91,9 @@
         public boolean accept(Bundle options) {
             // Make sure the options are unparcellable by the FW. (e.g. not containing unknown
             // classes.)
+            if (!isCallerValid()) {
+                throw new SecurityException("Calling uid mismatch");
+            }
             Intent extras = null;
             if (options != null) {
                 try {
@@ -126,8 +144,8 @@
         private PinShortcutRequestInner(ShortcutRequestPinProcessor processor,
                 ShortcutInfo shortcutOriginal, ShortcutInfo shortcutForLauncher,
                 IntentSender resultIntent,
-                String launcherPackage, int launcherUserId, boolean preExisting) {
-            super(processor, resultIntent);
+                String launcherPackage, int launcherUserId, int launcherUid, boolean preExisting) {
+            super(processor, resultIntent, launcherUid);
             this.shortcutOriginal = shortcutOriginal;
             this.shortcutForLauncher = shortcutForLauncher;
             this.launcherPackage = launcherPackage;
@@ -157,6 +175,7 @@
     /**
      * Handle {@link android.content.pm.ShortcutManager#requestPinShortcut)} and
      * {@link android.appwidget.AppWidgetManager#requestPinAppWidget}.
+     * In this flow the PinItemRequest is delivered directly to the default launcher app.
      * One of {@param inShortcut} and {@param inAppWidget} is always non-null and the other is
      * always null.
      */
@@ -184,9 +203,13 @@
         // Next, validate the incoming shortcut, etc.
         final PinItemRequest request;
         if (inShortcut != null) {
-            request = requestPinShortcutLocked(inShortcut, resultIntent, confirmActivity);
+            request = requestPinShortcutLocked(inShortcut, resultIntent, confirmActivity,
+                    true /* ignoreIfAlreadyPinned */);
         } else {
-            request = new PinItemRequest(inAppWidget, new PinItemRequestInner(this, resultIntent));
+            int launcherUid = mService.injectGetPackageUid(
+                    confirmActivity.first.getPackageName(), launcherUserId);
+            request = new PinItemRequest(inAppWidget,
+                    new PinItemRequestInner(this, resultIntent, launcherUid));
         }
 
         if (request == null) {
@@ -197,10 +220,41 @@
     }
 
     /**
+     * Handle {@link android.content.pm.ShortcutManager#createShortcutResultIntent(ShortcutInfo)}.
+     * In this flow the PinItemRequest is delivered to the caller app. Its the app's responsibility
+     * to send it to the Launcher app (via {@link android.app.Activity#setResult(int, Intent)}).
+     */
+    public Intent createShortcutResultIntent(@NonNull ShortcutInfo inShortcut, int userId) {
+        // Find the default launcher activity
+        final int launcherUserId = mService.getParentOrSelfUserId(userId);
+        final ComponentName defaultLauncher = mService.getDefaultLauncher(launcherUserId);
+        if (defaultLauncher == null) {
+            Log.e(TAG, "Default launcher not found.");
+            return null;
+        }
+
+        // Make sure the launcher user is unlocked. (it's always the parent profile, so should
+        // really be unlocked here though.)
+        mService.throwIfUserLockedL(launcherUserId);
+
+        // Next, validate the incoming shortcut, etc.
+        PinItemRequest request = requestPinShortcutLocked(inShortcut, null,
+                Pair.create(defaultLauncher, launcherUserId), false /* ignoreIfAlreadyPinned */);
+        if (request == null) {
+            return null;
+        }
+        return new Intent().putExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST, request);
+    }
+
+    /**
      * Handle {@link android.content.pm.ShortcutManager#requestPinShortcut)}.
+     *
+     * @param ignoreIfAlreadyPinned if true and the {@param inShortcut} is already pinned for
+     *                              {@param confirmActivity}, null is returned instead.
      */
     private PinItemRequest requestPinShortcutLocked(ShortcutInfo inShortcut,
-            IntentSender resultIntent, Pair<ComponentName, Integer> confirmActivity) {
+            IntentSender resultIntent, Pair<ComponentName, Integer> confirmActivity,
+            boolean ignoreIfAlreadyPinned) {
         final ShortcutPackage ps = mService.getPackageShortcutsForPublisherLocked(
                 inShortcut.getPackage(), inShortcut.getUserId());
 
@@ -221,9 +275,10 @@
         if (existsAlready) {
             validateExistingShortcut(existing);
 
+            final boolean isAlreadyPinned = mService.getLauncherShortcutsLocked(
+                    launcherPackage, existing.getUserId(), launcherUserId).hasPinned(existing);
             // See if it's already pinned.
-            if (mService.getLauncherShortcutsLocked(
-                    launcherPackage, existing.getUserId(), launcherUserId).hasPinned(existing)) {
+            if (ignoreIfAlreadyPinned && isAlreadyPinned) {
                 Log.i(TAG, "Launcher's already pinning shortcut " + existing.getId()
                         + " for package " + existing.getPackage());
                 return null;
@@ -233,8 +288,10 @@
             // Note this will remove the intent and icons.
             shortcutForLauncher = existing.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
 
-            // FLAG_PINNED is still set, if it's pinned by other launchers.
-            shortcutForLauncher.clearFlags(ShortcutInfo.FLAG_PINNED);
+            if (!isAlreadyPinned) {
+                // FLAG_PINNED is still set, if it's pinned by other launchers.
+                shortcutForLauncher.clearFlags(ShortcutInfo.FLAG_PINNED);
+            }
         } else {
             // If the shortcut has no default activity, try to set the main activity.
             // But in the request-pin case, it's optional, so it's okay even if the caller
@@ -264,7 +321,9 @@
         // Create a request object.
         final PinShortcutRequestInner inner =
                 new PinShortcutRequestInner(this, inShortcut, shortcutForLauncher, resultIntent,
-                        launcherPackage, launcherUserId, existsAlready);
+                        launcherPackage, launcherUserId,
+                        mService.injectGetPackageUid(launcherPackage, launcherUserId),
+                        existsAlready);
 
         return new PinItemRequest(shortcutForLauncher, inner);
     }
@@ -327,6 +386,10 @@
         mService.injectSendIntentSender(intent, extras);
     }
 
+    public boolean isCallerUid(int uid) {
+        return uid == mService.injectBinderCallingUid();
+    }
+
     /**
      * The last step of the "request pin shortcut" flow.  Called when the launcher accepted a
      * request.
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index a890526..ae709fe 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1529,7 +1529,7 @@
         if (UserHandle.getUserId(callingUid) != userId) {
             throw new SecurityException("Invalid user-ID");
         }
-        if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) {
+        if (injectGetPackageUid(packageName, userId) == callingUid) {
             return; // Caller is valid.
         }
         throw new SecurityException("Calling package name mismatch");
@@ -1854,6 +1854,25 @@
         return requestPinItem(packageName, userId, shortcut, null, resultIntent);
     }
 
+    @Override
+    public Intent createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId)
+            throws RemoteException {
+        Preconditions.checkNotNull(shortcut);
+        Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
+        verifyCaller(packageName, userId);
+
+        final Intent ret;
+        synchronized (mLock) {
+            throwIfUserLockedL(userId);
+
+            // Send request to the launcher, if supported.
+            ret = mShortcutRequestPinProcessor.createShortcutResultIntent(shortcut, userId);
+        }
+
+        verifyStates();
+        return ret;
+    }
+
     /**
      * Handles {@link #requestPinShortcut} and {@link ShortcutServiceInternal#requestPinAppWidget}.
      * After validating the caller, it passes the request to {@link #mShortcutRequestPinProcessor}.
diff --git a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java b/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
new file mode 100644
index 0000000..133881a
--- /dev/null
+++ b/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.server.policy;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.ContentObserver;
+import android.media.AudioAttributes;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+
+import android.widget.Toast;
+import com.android.internal.R;
+
+import java.util.List;
+
+import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+
+/**
+ * Class to help manage the accessibility shortcut
+ */
+public class AccessibilityShortcutController {
+    private static final String TAG = "AccessibilityShortcutController";
+
+    private final Context mContext;
+    private AlertDialog mAlertDialog;
+    private boolean mIsShortcutEnabled;
+    // Visible for testing
+    public FrameworkObjectProvider mFrameworkObjectProvider = new FrameworkObjectProvider();
+
+    public static String getTargetServiceComponentNameString(
+            Context context, int userId) {
+        final String currentShortcutServiceId = Settings.Secure.getStringForUser(
+                context.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                userId);
+        if (currentShortcutServiceId != null) {
+            return currentShortcutServiceId;
+        }
+        return context.getString(R.string.config_defaultAccessibilityService);
+    }
+
+    public AccessibilityShortcutController(Context context, Handler handler) {
+        mContext = context;
+
+        // Keep track of state of shortcut
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE),
+                false,
+                new ContentObserver(handler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        onSettingsChanged();
+                    }
+                },
+                UserHandle.USER_ALL);
+        updateShortcutEnabled();
+    }
+
+    public boolean isAccessibilityShortcutAvailable() {
+        return mIsShortcutEnabled;
+    }
+
+    public void onSettingsChanged() {
+        updateShortcutEnabled();
+    }
+
+    /**
+     * Called when the accessibility shortcut is activated
+     */
+    public void performAccessibilityShortcut() {
+        Slog.d(TAG, "Accessibility shortcut activated");
+        final ContentResolver cr = mContext.getContentResolver();
+        final int userId = ActivityManager.getCurrentUser();
+        final int dialogAlreadyShown = Settings.Secure.getIntForUser(
+                cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId);
+        final Ringtone tone =
+                RingtoneManager.getRingtone(mContext, Settings.System.DEFAULT_NOTIFICATION_URI);
+        if (tone != null) {
+            tone.setAudioAttributes(new AudioAttributes.Builder()
+                .setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
+                .build());
+            tone.play();
+        }
+        if (dialogAlreadyShown == 0) {
+            // The first time, we show a warning rather than toggle the service to give the user a
+            // chance to turn off this feature before stuff gets enabled.
+            mAlertDialog = createShortcutWarningDialog(userId);
+            if (mAlertDialog == null) {
+                return;
+            }
+            Window w = mAlertDialog.getWindow();
+            WindowManager.LayoutParams attr = w.getAttributes();
+            attr.type = TYPE_KEYGUARD_DIALOG;
+            w.setAttributes(attr);
+            mAlertDialog.show();
+            Settings.Secure.putIntForUser(
+                    cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1, userId);
+        } else {
+            if (mAlertDialog != null) {
+                mAlertDialog.dismiss();
+                mAlertDialog = null;
+            }
+
+            // Show a toast alerting the user to what's happening
+            final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
+            if (serviceInfo == null) {
+                Slog.e(TAG, "Accessibility shortcut set to invalid service");
+                return;
+            }
+            String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
+                    ? R.string.accessibility_shortcut_disabling_service
+                    : R.string.accessibility_shortcut_enabling_service);
+            String toastMessage = String.format(toastMessageFormatString,
+                    serviceInfo.getResolveInfo()
+                            .loadLabel(mContext.getPackageManager()).toString());
+            mFrameworkObjectProvider.makeToastFromText(mContext, toastMessage, Toast.LENGTH_LONG)
+                    .show();
+
+            mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext)
+                    .performAccessibilityShortcut();
+        }
+    }
+
+    private void updateShortcutEnabled() {
+        mIsShortcutEnabled = !TextUtils.isEmpty(getTargetServiceComponentNameString(
+                mContext, UserHandle.myUserId()));
+    }
+
+    private AlertDialog createShortcutWarningDialog(int userId) {
+        final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
+
+        if (serviceInfo == null) {
+            return null;
+        }
+
+        final String warningMessage = String.format(
+                mContext.getString(R.string.accessibility_shortcut_toogle_warning),
+                serviceInfo.getResolveInfo().loadLabel(mContext.getPackageManager()).toString());
+        final AlertDialog alertDialog = mFrameworkObjectProvider.getAlertDialogBuilder(mContext)
+                .setTitle(R.string.accessibility_shortcut_warning_dialog_title)
+                .setMessage(warningMessage)
+                .setCancelable(false)
+                .setPositiveButton(R.string.leave_accessibility_shortcut_on, null)
+                .setNegativeButton(R.string.disable_accessibility_shortcut,
+                        (DialogInterface d, int which) -> {
+                            Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                                    Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "",
+                                    userId);
+                        })
+                .setOnCancelListener((DialogInterface d) -> {
+                    // If canceled, treat as if the dialog has never been shown
+                    Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                        Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId);
+                })
+                .create();
+        return alertDialog;
+    }
+
+    private AccessibilityServiceInfo getInfoForTargetService() {
+        final String currentShortcutServiceString = getTargetServiceComponentNameString(
+                mContext, UserHandle.myUserId());
+        if (currentShortcutServiceString == null) {
+            return null;
+        }
+        AccessibilityManager accessibilityManager =
+                mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext);
+        return accessibilityManager.getInstalledServiceInfoWithComponentName(
+                        ComponentName.unflattenFromString(currentShortcutServiceString));
+    }
+
+    private boolean isServiceEnabled(AccessibilityServiceInfo serviceInfo) {
+        AccessibilityManager accessibilityManager =
+                mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext);
+        return accessibilityManager.getEnabledAccessibilityServiceList(
+                AccessibilityServiceInfo.FEEDBACK_ALL_MASK).contains(serviceInfo);
+    }
+
+    // Class to allow mocking of static framework calls
+    public static class FrameworkObjectProvider {
+        public AccessibilityManager getAccessibilityManagerInstance(Context context) {
+            return AccessibilityManager.getInstance(context);
+        }
+
+        public AlertDialog.Builder getAlertDialogBuilder(Context context) {
+            return new AlertDialog.Builder(context);
+        }
+
+        public Toast makeToastFromText(Context context, CharSequence charSequence, int duration) {
+            return Toast.makeText(context, charSequence, duration);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/policy/EnableAccessibilityController.java b/services/core/java/com/android/server/policy/EnableAccessibilityController.java
deleted file mode 100644
index 6b203a9..0000000
--- a/services/core/java/com/android/server/policy/EnableAccessibilityController.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.server.policy;
-
-import android.accessibilityservice.AccessibilityService;
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.pm.ServiceInfo;
-import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.speech.tts.TextToSpeech;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.IWindowManager;
-import android.view.MotionEvent;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.WindowManagerInternal;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IAccessibilityManager;
-
-import com.android.internal.R;
-import com.android.server.LocalServices;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-public class EnableAccessibilityController {
-    private static final String TAG = "EnableAccessibilityController";
-
-    private static final int SPEAK_WARNING_DELAY_MILLIS = 2000;
-    private static final int ENABLE_ACCESSIBILITY_DELAY_MILLIS = 6000;
-
-    public static final int MESSAGE_SPEAK_WARNING = 1;
-    public static final int MESSAGE_SPEAK_ENABLE_CANCELED = 2;
-    public static final int MESSAGE_ENABLE_ACCESSIBILITY = 3;
-
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message message) {
-            switch (message.what) {
-                case MESSAGE_SPEAK_WARNING: {
-                    String text = mContext.getString(R.string.continue_to_enable_accessibility);
-                    mTts.speak(text, TextToSpeech.QUEUE_FLUSH, null);
-                } break;
-                case MESSAGE_SPEAK_ENABLE_CANCELED: {
-                    String text = mContext.getString(R.string.enable_accessibility_canceled);
-                    mTts.speak(text, TextToSpeech.QUEUE_FLUSH, null);
-                } break;
-                case MESSAGE_ENABLE_ACCESSIBILITY: {
-                    enableAccessibility();
-                    mTone.play();
-                    mTts.speak(mContext.getString(R.string.accessibility_enabled),
-                            TextToSpeech.QUEUE_FLUSH, null);
-                } break;
-            }
-        }
-    };
-
-    private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager
-            .Stub.asInterface(ServiceManager.getService("accessibility"));
-
-
-    private final Context mContext;
-    private final Runnable mOnAccessibilityEnabledCallback;
-    private final UserManager mUserManager;
-    private final TextToSpeech mTts;
-    private final Ringtone mTone;
-
-    private final float mTouchSlop;
-
-    private boolean mDestroyed;
-    private boolean mCanceled;
-
-    private float mFirstPointerDownX;
-    private float mFirstPointerDownY;
-    private float mSecondPointerDownX;
-    private float mSecondPointerDownY;
-
-    public EnableAccessibilityController(Context context, Runnable onAccessibilityEnabledCallback) {
-        mContext = context;
-        mOnAccessibilityEnabledCallback = onAccessibilityEnabledCallback;
-        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        mTts = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
-            @Override
-            public void onInit(int status) {
-                if (mDestroyed) {
-                    mTts.shutdown();
-                }
-            }
-        });
-        mTone = RingtoneManager.getRingtone(context, Settings.System.DEFAULT_NOTIFICATION_URI);
-        mTone.setStreamType(AudioManager.STREAM_MUSIC);
-        mTouchSlop = context.getResources().getDimensionPixelSize(
-                R.dimen.accessibility_touch_slop);
-    }
-
-    public static boolean canEnableAccessibilityViaGesture(Context context) {
-        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(context);
-        // Accessibility is enabled and there is an enabled speaking
-        // accessibility service, then we have nothing to do.
-        if (accessibilityManager.isEnabled()
-                && !accessibilityManager.getEnabledAccessibilityServiceList(
-                        AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty()) {
-            return false;
-        }
-        // If the global gesture is enabled and there is a speaking service
-        // installed we are good to go, otherwise there is nothing to do.
-        return Settings.Global.getInt(context.getContentResolver(),
-                Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, 0) == 1
-                && !getInstalledSpeakingAccessibilityServices(context).isEmpty();
-    }
-
-    public static List<AccessibilityServiceInfo> getInstalledSpeakingAccessibilityServices(
-            Context context) {
-        List<AccessibilityServiceInfo> services = new ArrayList<AccessibilityServiceInfo>();
-        services.addAll(AccessibilityManager.getInstance(context)
-                .getInstalledAccessibilityServiceList());
-        Iterator<AccessibilityServiceInfo> iterator = services.iterator();
-        while (iterator.hasNext()) {
-            AccessibilityServiceInfo service = iterator.next();
-            if ((service.feedbackType & AccessibilityServiceInfo.FEEDBACK_SPOKEN) == 0) {
-                iterator.remove();
-            }
-        }
-        return services;
-    }
-
-    public void onDestroy() {
-        mDestroyed = true;
-    }
-
-    public boolean onInterceptTouchEvent(MotionEvent event) {
-        if (event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN
-                && event.getPointerCount() == 2) {
-            mFirstPointerDownX = event.getX(0);
-            mFirstPointerDownY = event.getY(0);
-            mSecondPointerDownX = event.getX(1);
-            mSecondPointerDownY = event.getY(1);
-            mHandler.sendEmptyMessageDelayed(MESSAGE_SPEAK_WARNING,
-                    SPEAK_WARNING_DELAY_MILLIS);
-            mHandler.sendEmptyMessageDelayed(MESSAGE_ENABLE_ACCESSIBILITY,
-                   ENABLE_ACCESSIBILITY_DELAY_MILLIS);
-            return true;
-        }
-        return false;
-    }
-
-    public boolean onTouchEvent(MotionEvent event) {
-        final int pointerCount = event.getPointerCount();
-        final int action = event.getActionMasked();
-        if (mCanceled) {
-            if (action == MotionEvent.ACTION_UP) {
-                mCanceled = false;
-            }
-            return true;
-        }
-        switch (action) {
-            case MotionEvent.ACTION_POINTER_DOWN: {
-                if (pointerCount > 2) {
-                    cancel();
-                }
-            } break;
-            case MotionEvent.ACTION_MOVE: {
-                final float firstPointerMove = MathUtils.dist(event.getX(0),
-                        event.getY(0), mFirstPointerDownX, mFirstPointerDownY);
-                if (Math.abs(firstPointerMove) > mTouchSlop) {
-                    cancel();
-                }
-                final float secondPointerMove = MathUtils.dist(event.getX(1),
-                        event.getY(1), mSecondPointerDownX, mSecondPointerDownY);
-                if (Math.abs(secondPointerMove) > mTouchSlop) {
-                    cancel();
-                }
-            } break;
-            case MotionEvent.ACTION_POINTER_UP:
-            case MotionEvent.ACTION_CANCEL: {
-                cancel();
-            } break;
-        }
-        return true;
-    }
-
-    private void cancel() {
-        mCanceled = true;
-        if (mHandler.hasMessages(MESSAGE_SPEAK_WARNING)) {
-            mHandler.removeMessages(MESSAGE_SPEAK_WARNING);
-        } else if (mHandler.hasMessages(MESSAGE_ENABLE_ACCESSIBILITY)) {
-            mHandler.sendEmptyMessage(MESSAGE_SPEAK_ENABLE_CANCELED);
-        }
-        mHandler.removeMessages(MESSAGE_ENABLE_ACCESSIBILITY);
-    }
-
-    private void enableAccessibility() {
-        if (enableAccessibility(mContext)) {
-            mOnAccessibilityEnabledCallback.run();
-        }
-    }
-
-    public static boolean enableAccessibility(Context context) {
-        final IAccessibilityManager accessibilityManager = IAccessibilityManager
-                .Stub.asInterface(ServiceManager.getService("accessibility"));
-        final WindowManagerInternal windowManager = LocalServices.getService(
-                WindowManagerInternal.class);
-        final UserManager userManager = (UserManager) context.getSystemService(
-                Context.USER_SERVICE);
-        ComponentName componentName = getInstalledSpeakingAccessibilityServiceComponent(context);
-        if (componentName == null) {
-            return false;
-        }
-
-        boolean keyguardLocked = windowManager.isKeyguardLocked();
-        final boolean hasMoreThanOneUser = userManager.getUsers().size() > 1;
-        try {
-            if (!keyguardLocked || !hasMoreThanOneUser) {
-                final int userId = ActivityManager.getCurrentUser();
-                accessibilityManager.enableAccessibilityService(componentName, userId);
-            } else if (keyguardLocked) {
-                accessibilityManager.temporaryEnableAccessibilityStateUntilKeyguardRemoved(
-                        componentName, true /* enableTouchExploration */);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "cannot enable accessibilty: " + e);
-        }
-
-        return true;
-    }
-
-    public static void disableAccessibility(Context context) {
-        final IAccessibilityManager accessibilityManager = IAccessibilityManager
-                .Stub.asInterface(ServiceManager.getService("accessibility"));
-        ComponentName componentName = getInstalledSpeakingAccessibilityServiceComponent(context);
-        if (componentName == null) {
-            return;
-        }
-
-        final int userId = ActivityManager.getCurrentUser();
-        try {
-            accessibilityManager.disableAccessibilityService(componentName, userId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "cannot disable accessibility " + e);
-        }
-    }
-
-    public static boolean isAccessibilityEnabled(Context context) {
-        final AccessibilityManager accessibilityManager =
-                context.getSystemService(AccessibilityManager.class);
-        List enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(
-                AccessibilityServiceInfo.FEEDBACK_SPOKEN);
-        return enabledServices != null && !enabledServices.isEmpty();
-    }
-
-    @Nullable
-    public static ComponentName getInstalledSpeakingAccessibilityServiceComponent(
-            Context context) {
-        List<AccessibilityServiceInfo> services =
-                getInstalledSpeakingAccessibilityServices(context);
-        if (services.isEmpty()) {
-            return null;
-        }
-
-        ServiceInfo serviceInfo = services.get(0).getResolveInfo().serviceInfo;
-        return new ComponentName(serviceInfo.packageName, serviceInfo.name);
-    }
-}
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index d4adcc4..335a230 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -44,7 +44,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -59,12 +58,9 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.TypedValue;
-import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
@@ -1194,21 +1190,14 @@
 
     private static final class GlobalActionsDialog extends Dialog implements DialogInterface {
         private final Context mContext;
-        private final int mWindowTouchSlop;
         private final AlertController mAlert;
         private final MyAdapter mAdapter;
 
-        private EnableAccessibilityController mEnableAccessibilityController;
-
-        private boolean mIntercepted;
-        private boolean mCancelOnUp;
-
         public GlobalActionsDialog(Context context, AlertParams params) {
             super(context, getDialogTheme(context));
             mContext = getContext();
             mAlert = AlertController.create(mContext, this, getWindow());
             mAdapter = (MyAdapter) params.mAdapter;
-            mWindowTouchSlop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
             params.apply(mAlert);
         }
 
@@ -1221,76 +1210,10 @@
 
         @Override
         protected void onStart() {
-            // If global accessibility gesture can be performed, we will take care
-            // of dismissing the dialog on touch outside. This is because the dialog
-            // is dismissed on the first down while the global gesture is a long press
-            // with two fingers anywhere on the screen.
-            if (EnableAccessibilityController.canEnableAccessibilityViaGesture(mContext)) {
-                mEnableAccessibilityController = new EnableAccessibilityController(mContext,
-                        new Runnable() {
-                    @Override
-                    public void run() {
-                        dismiss();
-                    }
-                });
-                super.setCanceledOnTouchOutside(false);
-            } else {
-                mEnableAccessibilityController = null;
-                super.setCanceledOnTouchOutside(true);
-            }
-
+            super.setCanceledOnTouchOutside(true);
             super.onStart();
         }
 
-        @Override
-        protected void onStop() {
-            if (mEnableAccessibilityController != null) {
-                mEnableAccessibilityController.onDestroy();
-            }
-            super.onStop();
-        }
-
-        @Override
-        public boolean dispatchTouchEvent(MotionEvent event) {
-            if (mEnableAccessibilityController != null) {
-                final int action = event.getActionMasked();
-                if (action == MotionEvent.ACTION_DOWN) {
-                    View decor = getWindow().getDecorView();
-                    final int eventX = (int) event.getX();
-                    final int eventY = (int) event.getY();
-                    if (eventX < -mWindowTouchSlop
-                            || eventY < -mWindowTouchSlop
-                            || eventX >= decor.getWidth() + mWindowTouchSlop
-                            || eventY >= decor.getHeight() + mWindowTouchSlop) {
-                        mCancelOnUp = true;
-                    }
-                }
-                try {
-                    if (!mIntercepted) {
-                        mIntercepted = mEnableAccessibilityController.onInterceptTouchEvent(event);
-                        if (mIntercepted) {
-                            final long now = SystemClock.uptimeMillis();
-                            event = MotionEvent.obtain(now, now,
-                                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
-                            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-                            mCancelOnUp = true;
-                        }
-                    } else {
-                        return mEnableAccessibilityController.onTouchEvent(event);
-                    }
-                } finally {
-                    if (action == MotionEvent.ACTION_UP) {
-                        if (mCancelOnUp) {
-                            cancel();
-                        }
-                        mCancelOnUp = false;
-                        mIntercepted = false;
-                    }
-                }
-            }
-            return super.dispatchTouchEvent(event);
-        }
-
         public ListView getListView() {
             return mAlert.getListView();
         }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4b2b184..32b8c9b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -149,8 +149,6 @@
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.media.IAudioService;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
 import android.media.session.MediaSessionLegacyHelper;
 import android.os.Binder;
 import android.os.Build;
@@ -441,6 +439,9 @@
     /** If true, hitting shift & menu will broadcast Intent.ACTION_BUG_REPORT */
     boolean mEnableShiftMenuBugReports = false;
 
+    /** Controller that supports enabling an AccessibilityService by holding down the volume keys */
+    private AccessibilityShortcutController mAccessibilityShortcutController;
+
     boolean mSafeMode;
     WindowState mStatusBar = null;
     int mStatusBarHeight;
@@ -748,7 +749,10 @@
     private boolean mScreenshotChordVolumeDownKeyTriggered;
     private long mScreenshotChordVolumeDownKeyTime;
     private boolean mScreenshotChordVolumeDownKeyConsumed;
-    private boolean mScreenshotChordVolumeUpKeyTriggered;
+    private boolean mA11yShortcutChordVolumeUpKeyTriggered;
+    private long mA11yShortcutChordVolumeUpKeyTime;
+    private boolean mA11yShortcutChordVolumeUpKeyConsumed;
+
     private boolean mScreenshotChordPowerKeyTriggered;
     private long mScreenshotChordPowerKeyTime;
 
@@ -794,6 +798,7 @@
     private static final int MSG_BACK_LONG_PRESS = 18;
     private static final int MSG_DISPOSE_INPUT_CONSUMER = 19;
     private static final int MSG_BACK_DELAYED_PRESS = 20;
+    private static final int MSG_ACCESSIBILITY_SHORTCUT = 21;
 
     private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
     private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
@@ -869,6 +874,9 @@
                     backMultiPressAction((Long) msg.obj, msg.arg1);
                     finishBackKeyPress();
                     break;
+                case MSG_ACCESSIBILITY_SHORTCUT:
+                    accessibilityShortcutActivated();
+                    break;
             }
         }
     }
@@ -1213,7 +1221,7 @@
         // If the power key has still not yet been handled, then detect short
         // press, long press, or multi press and decide what to do.
         mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
-                || mScreenshotChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
+                || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
         if (!mPowerKeyHandled) {
             if (interactive) {
                 // When interactive, we're already awake.
@@ -1406,9 +1414,7 @@
             break;
         case LONG_PRESS_POWER_GLOBAL_ACTIONS:
             mPowerKeyHandled = true;
-            if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
-                performAuditoryFeedbackForAccessibilityIfNeed();
-            }
+            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
             showGlobalActionsInternal();
             break;
         case LONG_PRESS_POWER_SHUT_OFF:
@@ -1439,6 +1445,10 @@
         }
     }
 
+    private void accessibilityShortcutActivated() {
+        mAccessibilityShortcutController.performAccessibilityShortcut();
+    }
+
     private void disposeInputConsumer(InputConsumer inputConsumer) {
         if (inputConsumer != null) {
             inputConsumer.dismiss();
@@ -1484,7 +1494,7 @@
     private void interceptScreenshotChord() {
         if (mScreenshotChordEnabled
                 && mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered
-                && !mScreenshotChordVolumeUpKeyTriggered) {
+                && !mA11yShortcutChordVolumeUpKeyTriggered) {
             final long now = SystemClock.uptimeMillis();
             if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
                     && now <= mScreenshotChordPowerKeyTime
@@ -1497,6 +1507,22 @@
         }
     }
 
+    private void interceptAccessibilityShortcutChord() {
+        if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable()
+                && mScreenshotChordVolumeDownKeyTriggered && mA11yShortcutChordVolumeUpKeyTriggered
+                && !mScreenshotChordPowerKeyTriggered) {
+            final long now = SystemClock.uptimeMillis();
+            if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
+                    && now <= mA11yShortcutChordVolumeUpKeyTime
+                    + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
+                mScreenshotChordVolumeDownKeyConsumed = true;
+                mA11yShortcutChordVolumeUpKeyConsumed = true;
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT),
+                        ViewConfiguration.get(mContext).getAccessibilityShortcutKeyTimeout());
+            }
+        }
+    }
+
     private long getScreenshotChordLongPressDelay() {
         if (mKeyguardDelegate.isShowing()) {
             // Double the time it takes to take a screenshot from the keyguard
@@ -1510,13 +1536,15 @@
         mHandler.removeCallbacks(mScreenshotRunnable);
     }
 
+    private void cancelPendingAccessibilityShortcutAction() {
+        mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
+    }
+
     private final Runnable mEndCallLongPress = new Runnable() {
         @Override
         public void run() {
             mEndCallKeyHandled = true;
-            if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
-                performAuditoryFeedbackForAccessibilityIfNeed();
-            }
+            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
             showGlobalActionsInternal();
         }
     };
@@ -1698,7 +1726,8 @@
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
-
+        mAccessibilityShortcutController =
+                new AccessibilityShortcutController(mContext, new Handler());
         // Init display burn-in protection
         boolean burnInProtectionEnabled = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_enableBurnInProtection);
@@ -3251,6 +3280,33 @@
             }
         }
 
+        // If an accessibility shortcut might be partially complete, hold off dispatching until we
+        // know if it is complete or not
+        if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable()
+                && (flags & KeyEvent.FLAG_FALLBACK) == 0) {
+            if (mScreenshotChordVolumeDownKeyTriggered ^ mA11yShortcutChordVolumeUpKeyTriggered) {
+                final long now = SystemClock.uptimeMillis();
+                final long timeoutTime = (mScreenshotChordVolumeDownKeyTriggered
+                        ? mScreenshotChordVolumeDownKeyTime : mA11yShortcutChordVolumeUpKeyTime)
+                        + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS;
+                if (now < timeoutTime) {
+                    return timeoutTime - now;
+                }
+            }
+            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN && mScreenshotChordVolumeDownKeyConsumed) {
+                if (!down) {
+                    mScreenshotChordVolumeDownKeyConsumed = false;
+                }
+                return -1;
+            }
+            if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mA11yShortcutChordVolumeUpKeyConsumed) {
+                if (!down) {
+                    mA11yShortcutChordVolumeUpKeyConsumed = false;
+                }
+                return -1;
+            }
+        }
+
         // Cancel any pending meta actions if we see any other keys being pressed between the down
         // of the meta key and its corresponding up.
         if (mPendingMetaAction && !KeyEvent.isMetaKey(keyCode)) {
@@ -5760,22 +5816,32 @@
                             mScreenshotChordVolumeDownKeyConsumed = false;
                             cancelPendingPowerKeyAction();
                             interceptScreenshotChord();
+                            if (!keyguardActive) {
+                                interceptAccessibilityShortcutChord();
+                            }
                         }
                     } else {
                         mScreenshotChordVolumeDownKeyTriggered = false;
                         cancelPendingScreenshotChordAction();
+                        cancelPendingAccessibilityShortcutAction();
                     }
                 } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
                     if (down) {
-                        if (interactive && !mScreenshotChordVolumeUpKeyTriggered
+                        if (interactive && !mA11yShortcutChordVolumeUpKeyTriggered
                                 && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
-                            mScreenshotChordVolumeUpKeyTriggered = true;
+                            mA11yShortcutChordVolumeUpKeyTriggered = true;
+                            mA11yShortcutChordVolumeUpKeyTime = event.getDownTime();
+                            mA11yShortcutChordVolumeUpKeyConsumed = false;
                             cancelPendingPowerKeyAction();
                             cancelPendingScreenshotChordAction();
+                            if (!keyguardActive) {
+                                interceptAccessibilityShortcutChord();
+                            }
                         }
                     } else {
-                        mScreenshotChordVolumeUpKeyTriggered = false;
+                        mA11yShortcutChordVolumeUpKeyTriggered = false;
                         cancelPendingScreenshotChordAction();
+                        cancelPendingAccessibilityShortcutAction();
                     }
                 }
                 if (down) {
@@ -5863,6 +5929,8 @@
             }
 
             case KeyEvent.KEYCODE_POWER: {
+                // Any activity on the power button stops the accessibility shortcut
+                cancelPendingAccessibilityShortcutAction();
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false; // wake-up will be handled separately
                 if (down) {
@@ -7416,31 +7484,11 @@
         }
     }
 
-    private void performAuditoryFeedbackForAccessibilityIfNeed() {
-        if (!isGlobalAccessibilityGestureEnabled()) {
-            return;
-        }
-        AudioManager audioManager = (AudioManager) mContext.getSystemService(
-                Context.AUDIO_SERVICE);
-        if (audioManager.isSilentMode()) {
-            return;
-        }
-        Ringtone ringTone = RingtoneManager.getRingtone(mContext,
-                Settings.System.DEFAULT_NOTIFICATION_URI);
-        ringTone.setStreamType(AudioManager.STREAM_MUSIC);
-        ringTone.play();
-    }
-
     private boolean isTheaterModeEnabled() {
         return Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.THEATER_MODE_ON, 0) == 1;
     }
 
-    private boolean isGlobalAccessibilityGestureEnabled() {
-        return Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, 0) == 1;
-    }
-
     private boolean areSystemNavigationKeysEnabled() {
         return Settings.Secure.getIntForUser(mContext.getContentResolver(),
                 Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
diff --git a/services/core/java/com/android/server/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java
index 23be9a3..5a1f473 100644
--- a/services/core/java/com/android/server/storage/AppFuseBridge.java
+++ b/services/core/java/com/android/server/storage/AppFuseBridge.java
@@ -16,79 +16,95 @@
 
 package com.android.server.storage;
 
-import android.annotation.CallSuper;
-import android.annotation.WorkerThread;
-import android.os.Handler;
 import android.os.ParcelFileDescriptor;
 import android.system.ErrnoException;
 import android.system.Os;
-import android.system.OsConstants;
-import android.util.Log;
-import com.android.internal.os.AppFuseMount;
+import com.android.internal.util.Preconditions;
 import libcore.io.IoUtils;
-
 import java.io.File;
-import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.BlockingQueue;
 
-public class AppFuseBridge implements Runnable {
-    private static final String TAG = AppFuseBridge.class.getSimpleName();
-
-    private final FileDescriptor mDeviceFd;
-    private final FileDescriptor mProxyFd;
-    private final CountDownLatch mMountLatch = new CountDownLatch(1);
+/**
+ * Runnable that delegates FUSE command from the kernel to application.
+ * run() blocks until all opened files on the FUSE mount point are closed. So this should be run in
+ * a separated thread.
+ */
+public class AppFuseBridge implements Runnable, AutoCloseable {
+    public static final String TAG = "AppFuseBridge";
 
     /**
-     * @param deviceFd FD of /dev/fuse. Ownership of fd is taken by AppFuseBridge.
-     * @param proxyFd FD of socket pair. Ownership of fd is taken by AppFuseBridge.
+     * The path AppFuse is mounted to.
+     * The first number is UID who is mounting the FUSE.
+     * THe second number is mount ID.
+     * The path must be sync with vold.
      */
-    private AppFuseBridge(FileDescriptor deviceFd, FileDescriptor proxyFd) {
-        mDeviceFd = deviceFd;
+    private static final String APPFUSE_MOUNT_NAME_TEMPLATE = "/mnt/appfuse/%d_%d";
+
+    private final IMountScope mMountScope;
+    private final ParcelFileDescriptor mProxyFd;
+    private final BlockingQueue<Boolean> mChannel;
+
+    /**
+     * @param mountScope Listener to unmount mount point.
+     * @param proxyFd FD of socket pair. Ownership of FD is taken by AppFuseBridge.
+     * @param channel Channel that the runnable send mount result to.
+     */
+    public AppFuseBridge(
+            IMountScope mountScope, ParcelFileDescriptor proxyFd, BlockingQueue<Boolean> channel) {
+        Preconditions.checkNotNull(mountScope);
+        Preconditions.checkNotNull(proxyFd);
+        Preconditions.checkNotNull(channel);
+        mMountScope = mountScope;
         mProxyFd = proxyFd;
-    }
-
-    public static AppFuseMount startMessageLoop(
-            int uid,
-            String name,
-            FileDescriptor deviceFd,
-            Handler handler,
-            ParcelFileDescriptor.OnCloseListener listener)
-                    throws IOException, ErrnoException, InterruptedException {
-        final FileDescriptor localFd = new FileDescriptor();
-        final FileDescriptor remoteFd = new FileDescriptor();
-        // Needs to specify OsConstants.SOCK_SEQPACKET to keep message boundaries.
-        Os.socketpair(OsConstants.AF_UNIX, OsConstants.SOCK_SEQPACKET, 0, remoteFd, localFd);
-
-        // Caller must invoke #start() after instantiate AppFuseBridge.
-        // Otherwise FDs will be leaked.
-        final AppFuseBridge bridge = new AppFuseBridge(deviceFd, localFd);
-        final Thread thread = new Thread(bridge, TAG);
-        thread.start();
-        try {
-            bridge.mMountLatch.await();
-        } catch (InterruptedException error) {
-            throw error;
-        }
-        return new AppFuseMount(
-                new File("/mnt/appfuse/" + uid + "_" + name),
-                ParcelFileDescriptor.fromFd(remoteFd, handler, listener));
+        mChannel = channel;
     }
 
     @Override
     public void run() {
-        // deviceFd and proxyFd must be closed in native_start_loop.
-        final int deviceFd = mDeviceFd.getInt$();
-        final int proxyFd = mProxyFd.getInt$();
-        mDeviceFd.setInt$(-1);
-        mProxyFd.setInt$(-1);
-        native_start_loop(deviceFd, proxyFd);
+        try {
+            // deviceFd and proxyFd must be closed in native_start_loop.
+            native_start_loop(
+                    mMountScope.getDeviceFileDescriptor().detachFd(),
+                    mProxyFd.detachFd());
+        } finally {
+            close();
+        }
+    }
+
+    public static ParcelFileDescriptor openFile(int uid, int mountId, int fileId, int mode)
+            throws FileNotFoundException {
+        final File mountPoint = getMountPoint(uid, mountId);
+        try {
+            if (Os.stat(mountPoint.getPath()).st_ino != 1) {
+                throw new FileNotFoundException("Could not find bridge mount point.");
+            }
+        } catch (ErrnoException e) {
+            throw new FileNotFoundException(
+                    "Failed to stat mount point: " + mountPoint.getParent());
+        }
+        return ParcelFileDescriptor.open(new File(mountPoint, String.valueOf(fileId)), mode);
+    }
+
+    private static File getMountPoint(int uid, int mountId) {
+        return new File(String.format(APPFUSE_MOUNT_NAME_TEMPLATE,  uid, mountId));
+    }
+
+    @Override
+    public void close() {
+        IoUtils.closeQuietly(mMountScope);
+        IoUtils.closeQuietly(mProxyFd);
+        // Invoke countDown here in case where close is invoked before mount.
+        mChannel.offer(false);
     }
 
     // Used by com_android_server_storage_AppFuse.cpp.
     private void onMount() {
-        mMountLatch.countDown();
+        mChannel.offer(true);
+    }
+
+    public static interface IMountScope extends AutoCloseable {
+        ParcelFileDescriptor getDeviceFileDescriptor();
     }
 
     private native boolean native_start_loop(int deviceFd, int proxyFd);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7325daab..4b680e5 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -637,6 +637,7 @@
         return mFillsParent || !StackId.isTaskResizeAllowed(mStack.mStackId);
     }
 
+    @Override
     TaskWindowContainerController getController() {
         return (TaskWindowContainerController) super.getController();
     }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index c3e3141..4a09423 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -35,7 +35,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Slog;
-import android.view.Display;
 import android.view.IWindowSession;
 import android.view.Surface;
 import android.view.View;
@@ -147,6 +146,7 @@
         if (reportNextDraw) {
             reportDrawn();
         }
+        mSurface.release();
     }
 
     private void reportDrawn() {
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 0ca1887..26e36dc 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -52,7 +52,9 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case REPORT_SNAPSHOT_CHANGED:
-                    mListener.onSnapshotChanged((TaskSnapshot) msg.obj);
+                    if (mListener != null) {
+                        mListener.onSnapshotChanged((TaskSnapshot) msg.obj);
+                    }
                     break;
             }
         }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 27efd05..6791da9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1462,7 +1462,11 @@
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
     PointerIcon pointerIcon;
-    android_view_PointerIcon_getLoadedIcon(env, iconObj, &pointerIcon);
+    status_t result = android_view_PointerIcon_getLoadedIcon(env, iconObj, &pointerIcon);
+    if (result) {
+        jniThrowRuntimeException(env, "Failed to load custom pointer icon.");
+        return;
+    }
 
     SpriteIcon spriteIcon;
     pointerIcon.bitmap.copyTo(&spriteIcon.bitmap, kN32_SkColorType);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1eeac82..111f37f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -191,6 +191,7 @@
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -235,7 +236,7 @@
 
     private static final int REQUEST_EXPIRE_PASSWORD = 5571;
 
-    private static final long MS_PER_DAY = 86400 * 1000;
+    private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
 
     private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms
 
@@ -330,7 +331,7 @@
      * Minimum timeout in milliseconds after which unlocking with weak auth times out,
      * i.e. the user has to use a strong authentication method like password, PIN or pattern.
      */
-    private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1h
+    private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = TimeUnit.HOURS.toMillis(1);
 
     /**
      * Strings logged with {@link
@@ -6783,6 +6784,18 @@
         enforceManageUsers();
     }
 
+    private void enforceProfileOwnerOrSystemUser(ComponentName admin) {
+        synchronized (this) {
+            if (getActiveAdminWithPolicyForUidLocked(admin,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid())
+                            != null) {
+                return;
+            }
+        }
+        Preconditions.checkState(isCallerWithSystemUid(),
+                "Only profile owner, device owner and system may call this method.");
+    }
+
     private void ensureCallerPackage(@Nullable String packageName) {
         if (packageName == null) {
             Preconditions.checkState(isCallerWithSystemUid(),
@@ -8925,8 +8938,8 @@
         PackageManager packageManager = mInjector.getPackageManager();
 
         UserHandle user = mInjector.binderGetCallingUserHandle();
+        enforceProfileOwnerOrSystemUser(admin);
         synchronized (this) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             long ident = mInjector.binderClearCallingIdentity();
             try {
                 int granted = mIPackageManager.checkPermission(permission,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 08fb591..b6c518b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -282,12 +282,6 @@
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, uptimeMillis);
             if (!mRuntimeRestart && !mFirstBoot) {
                 MetricsLogger.histogram(null, "boot_system_server_init", uptimeMillis);
-                // Also report when first stage of init has started
-                long initStartNs = SystemProperties.getLong("ro.boottime.init", -1);
-                if (initStartNs >= 0) {
-                    MetricsLogger.histogram(null, "boot_android_init",
-                            (int)(initStartNs / 1000000));
-                }
             }
 
             // In case the runtime switched since last boot (such as when
@@ -383,8 +377,13 @@
             Slog.i(TAG, "Enabled StrictMode for system server main thread.");
         }
         if (!mRuntimeRestart && !mFirstBoot) {
-            MetricsLogger.histogram(null, "boot_system_server_ready",
-                    (int) SystemClock.elapsedRealtime());
+            int uptimeMillis = (int) SystemClock.elapsedRealtime();
+            MetricsLogger.histogram(null, "boot_system_server_ready", uptimeMillis);
+            final int MAX_UPTIME_MILLIS = 60 * 1000;
+            if (uptimeMillis > MAX_UPTIME_MILLIS) {
+                Slog.wtf("SystemServerTiming",
+                        "SystemServer init took too long. uptimeMillis=" + uptimeMillis);
+            }
         }
 
         // Loop forever.
@@ -955,6 +954,21 @@
                 }
                 traceEnd();
 
+                // Wifi Service must be started first for wifi-related services.
+                traceBeginAndSlog("StartWifi");
+                mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+                traceEnd();
+                traceBeginAndSlog("StartWifiScanning");
+                mSystemServiceManager.startService(
+                        "com.android.server.wifi.scanner.WifiScanningService");
+                traceEnd();
+
+                if (!disableRtt) {
+                    traceBeginAndSlog("StartWifiRtt");
+                    mSystemServiceManager.startService("com.android.server.wifi.RttService");
+                    traceEnd();
+                }
+
                 if (context.getPackageManager().hasSystemFeature(
                         PackageManager.FEATURE_WIFI_AWARE)) {
                     traceBeginAndSlog("StartWifiAware");
@@ -970,19 +984,6 @@
                     mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
                     traceEnd();
                 }
-                traceBeginAndSlog("StartWifi");
-                mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
-                traceEnd();
-                traceBeginAndSlog("StartWifiScanning");
-                mSystemServiceManager.startService(
-                            "com.android.server.wifi.scanner.WifiScanningService");
-                traceEnd();
-
-                if (!disableRtt) {
-                    traceBeginAndSlog("StartWifiRtt");
-                    mSystemServiceManager.startService("com.android.server.wifi.RttService");
-                    traceEnd();
-                }
 
                 if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
                     mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 1393615..133bc3d 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -138,6 +138,12 @@
             android:enabled="true" android:exported="true">
             <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_1"/>
         </activity-alias>
+        <activity-alias android:name="a.ShortcutConfigActivity"
+                        android:targetActivity="com.android.server.pm.ShortcutTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.CREATE_SHORTCUT" />
+            </intent-filter>
+        </activity-alias>
 
         <activity-alias android:name="a.DisabledMain"
             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index b0665d5..8da47c8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -66,9 +66,11 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
@@ -2117,8 +2119,37 @@
         setupDeviceOwner();
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
-        final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1h
-        final long ONE_MINUTE = 60 * 1000;
+        final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = TimeUnit.HOURS.toMillis(1);
+        final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1);
+        final long MIN_PLUS_ONE_MINUTE = MINIMUM_STRONG_AUTH_TIMEOUT_MS + ONE_MINUTE;
+        final long MAX_MINUS_ONE_MINUTE = DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS
+                - ONE_MINUTE;
+
+        // verify that the minimum timeout cannot be modified on user builds (system property is
+        // not being read)
+        mContext.buildMock.isDebuggable = false;
+
+        dpm.setRequiredStrongAuthTimeout(admin1, MAX_MINUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MAX_MINUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(null), MAX_MINUS_ONE_MINUTE);
+
+        verify(mContext.systemProperties, never()).getLong(anyString(), anyLong());
+
+        // restore to the debuggable build state
+        mContext.buildMock.isDebuggable = true;
+
+        // Always return the default (second arg) when getting system property for long type
+        when(mContext.systemProperties.getLong(anyString(), anyLong())).thenAnswer(
+                new Answer<Long>() {
+                    @Override
+                    public Long answer(InvocationOnMock invocation) throws Throwable {
+                        return (Long) invocation.getArguments()[1];
+                    }
+                }
+        );
+
+        // reset to default (0 means the admin is not participating, so default should be returned)
+        dpm.setRequiredStrongAuthTimeout(admin1, 0);
 
         // aggregation should be the default if unset by any admin
         assertEquals(dpm.getRequiredStrongAuthTimeout(null),
@@ -2135,7 +2166,7 @@
         assertEquals(dpm.getRequiredStrongAuthTimeout(null),
                 DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS);
 
-        // 0 means default
+        // 0 means the admin is not participating, so default should be returned
         dpm.setRequiredStrongAuthTimeout(admin1, 0);
         assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), 0);
         assertEquals(dpm.getRequiredStrongAuthTimeout(null),
@@ -2146,12 +2177,14 @@
         assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MINIMUM_STRONG_AUTH_TIMEOUT_MS);
         assertEquals(dpm.getRequiredStrongAuthTimeout(null), MINIMUM_STRONG_AUTH_TIMEOUT_MS);
 
-        // value within range
-        dpm.setRequiredStrongAuthTimeout(admin1, MINIMUM_STRONG_AUTH_TIMEOUT_MS + ONE_MINUTE);
-        assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MINIMUM_STRONG_AUTH_TIMEOUT_MS
-                + ONE_MINUTE);
-        assertEquals(dpm.getRequiredStrongAuthTimeout(null), MINIMUM_STRONG_AUTH_TIMEOUT_MS
-                + ONE_MINUTE);
+        // values within range
+        dpm.setRequiredStrongAuthTimeout(admin1, MIN_PLUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MIN_PLUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(null), MIN_PLUS_ONE_MINUTE);
+
+        dpm.setRequiredStrongAuthTimeout(admin1, MAX_MINUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MAX_MINUS_ONE_MINUTE);
+        assertEquals(dpm.getRequiredStrongAuthTimeout(null), MAX_MINUS_ONE_MINUTE);
 
         // reset to default
         dpm.setRequiredStrongAuthTimeout(admin1, 0);
@@ -3200,6 +3233,48 @@
         }
     }
 
+    public void testGetPermissionGrantState() throws Exception {
+        final String permission = "some.permission";
+        final String app1 = "com.example.app1";
+        final String app2 = "com.example.app2";
+
+        when(mContext.ipackageManager.checkPermission(eq(permission), eq(app1), anyInt()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        doReturn(PackageManager.FLAG_PERMISSION_POLICY_FIXED).when(mContext.packageManager)
+                .getPermissionFlags(permission, app1, UserHandle.SYSTEM);
+        when(mContext.packageManager.getPermissionFlags(permission, app1,
+                UserHandle.of(DpmMockContext.CALLER_USER_HANDLE)))
+                .thenReturn(PackageManager.FLAG_PERMISSION_POLICY_FIXED);
+        when(mContext.ipackageManager.checkPermission(eq(permission), eq(app2), anyInt()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        doReturn(0).when(mContext.packageManager).getPermissionFlags(permission, app2,
+                UserHandle.SYSTEM);
+        when(mContext.packageManager.getPermissionFlags(permission, app2,
+                UserHandle.of(DpmMockContext.CALLER_USER_HANDLE))).thenReturn(0);
+
+        // System can retrieve permission grant state.
+        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED,
+                dpm.getPermissionGrantState(null, app1, permission));
+        assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT,
+                dpm.getPermissionGrantState(null, app2, permission));
+
+        // A regular app cannot retrieve permission grant state.
+        mMockContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        try {
+            dpm.getPermissionGrantState(null, app1, permission);
+            fail("Didn't throw IllegalStateException");
+        } catch (IllegalStateException expected) {
+        }
+
+        // Profile owner can retrieve permission grant state.
+        setAsProfileOwner(admin1);
+        assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED,
+                dpm.getPermissionGrantState(admin1, app1, permission));
+        assertEquals(DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT,
+                dpm.getPermissionGrantState(admin1, app2, permission));
+    }
+
     private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {
         when(mContext.settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
                 userhandle)).thenReturn(isUserSetupComplete ? 1 : 0);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java
new file mode 100644
index 0000000..ca1e6af
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils
+        .assertExpectException;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps.PinItemRequest;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.os.Process;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static org.mockito.Mockito.*;
+
+/**
+ * Tests for {@link ShortcutManager#createShortcutResultIntent(ShortcutInfo)} and relevant APIs.
+ *
+ m FrameworksServicesTests &&
+ adb install \
+ -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest10 \
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ */
+@SmallTest
+public class ShortcutManagerTest10 extends BaseShortcutManagerTest {
+
+    private PinItemRequest mRequest;
+
+    private PinItemRequest verifyAndGetCreateShortcutResult(Intent resultIntent) {
+        PinItemRequest request = mLauncherApps.getPinItemRequest(resultIntent);
+        assertNotNull(request);
+        assertEquals(PinItemRequest.REQUEST_TYPE_SHORTCUT, request.getRequestType());
+        return request;
+    }
+
+    public void testCreateShortcutResult_noDefaultLauncher() {
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+            assertNull(mManager.createShortcutResultIntent(s1));
+        });
+    }
+
+    public void testCreateShortcutResult_validResult() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+            Intent intent = mManager.createShortcutResultIntent(s1);
+            mRequest = verifyAndGetCreateShortcutResult(intent);
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertTrue(mRequest.isValid());
+            assertTrue(mRequest.accept());
+        });
+    }
+
+    public void testCreateShortcutResult_alreadyPinned() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+            Intent intent = mManager.createShortcutResultIntent(s1);
+            mRequest = verifyAndGetCreateShortcutResult(intent);
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertTrue(mRequest.isValid());
+            assertTrue(mRequest.getShortcutInfo().isPinned());
+            assertTrue(mRequest.accept());
+        });
+    }
+
+    public void testCreateShortcutResult_alreadyPinnedByAnother() {
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
+        });
+
+        // Initially all launchers have the shortcut permission, until we call setDefaultLauncher().
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
+        });
+
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+            Intent intent = mManager.createShortcutResultIntent(s1);
+            mRequest = verifyAndGetCreateShortcutResult(intent);
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertTrue(mRequest.isValid());
+            assertFalse(mRequest.getShortcutInfo().isPinned());
+            assertTrue(mRequest.accept());
+        });
+    }
+
+    public void testCreateShortcutResult_defaultLauncherChanges() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+            Intent intent = mManager.createShortcutResultIntent(s1);
+            mRequest = verifyAndGetCreateShortcutResult(intent);
+        });
+
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_2, USER_0));
+        // Verify that other launcher can't use this request
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            assertFalse(mRequest.isValid());
+            assertExpectException(SecurityException.class, "Calling uid mismatch",
+                    mRequest::accept);
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Set some random caller UID.
+            mInjectedCallingUid = 12345;
+
+            assertFalse(mRequest.isValid());
+            assertExpectException(SecurityException.class, "Calling uid mismatch",
+                    mRequest::accept);
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertTrue(mRequest.isValid());
+            assertTrue(mRequest.accept());
+        });
+    }
+
+    private LauncherActivityInfo setupMockActivityInfo() {
+        doReturn(getTestContext().getPackageName()).when(mServiceContext).getPackageName();
+        doReturn(getTestContext().getContentResolver()).when(mServiceContext).getContentResolver();
+
+        LauncherActivityInfo info = mock(LauncherActivityInfo.class);
+        when(info.getComponentName()).thenReturn(
+                new ComponentName(getTestContext(), "a.ShortcutConfigActivity"));
+        when(info.getUser()).thenReturn(Process.myUserHandle());
+        return info;
+    }
+
+    public void testStartConfigActivity_defaultLauncher() {
+        LauncherActivityInfo info = setupMockActivityInfo();
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+        runWithCaller(LAUNCHER_1, USER_0, () ->
+            assertNotNull(mLauncherApps.getShortcutConfigActivityIntent(info))
+        );
+    }
+
+    public void testStartConfigActivity_nonDefaultLauncher() {
+        LauncherActivityInfo info = setupMockActivityInfo();
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+        runWithCaller(LAUNCHER_2, USER_0, () ->
+            assertExpectException(SecurityException.class, null, () ->
+                    mLauncherApps.getShortcutConfigActivityIntent(info))
+        );
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
index bcd72fc..df275d2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
@@ -1421,6 +1421,35 @@
         });
     }
 
+    public void testRequestPinShortcut_wrongLauncherCannotAccept() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+            assertTrue(mManager.requestPinShortcut(s1, null));
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+        });
+
+        final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+        verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+        final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+        // Verify that other launcher can't use this request
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Set some random caller UID.
+            mInjectedCallingUid = 12345;
+
+            assertFalse(request.isValid());
+            assertExpectException(SecurityException.class, "Calling uid mismatch", request::accept);
+        });
+
+        // The default launcher can still use this request
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            assertTrue(request.isValid());
+            assertTrue(request.accept());
+        });
+    }
+
     // TODO More tests:
 
     // Cancel previous pending request and release memory?
diff --git a/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java b/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
new file mode 100644
index 0000000..e2aff16
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.provider.Settings;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.test.mock.MockContentResolver;
+import android.text.TextUtils;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+import android.widget.Toast;
+import com.android.internal.R;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.policy.AccessibilityShortcutController.FrameworkObjectProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.internal.util.reflection.Whitebox;
+
+import java.util.Collections;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityShortcutControllerTest {
+    private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name";
+
+    private @Mock Context mContext;
+    private @Mock FrameworkObjectProvider mFrameworkObjectProvider;
+    private @Mock IAccessibilityManager mAccessibilityManagerService;
+    private @Mock Handler mHandler;
+    private @Mock AlertDialog.Builder mAlertDialogBuilder;
+    private @Mock AlertDialog mAlertDialog;
+    private @Mock AccessibilityServiceInfo mServiceInfo;
+    private @Mock Resources mResources;
+    private @Mock Toast mToast;
+
+    private MockContentResolver mContentResolver;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mContentResolver = new MockContentResolver(mContext);
+        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+        when(mContext.getResources()).thenReturn(mResources);
+
+        when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(anyInt()))
+                .thenReturn(Collections.singletonList(mServiceInfo));
+
+        // Use the extra level of indirection in the object to mock framework objects
+        AccessibilityManager accessibilityManager =
+                new AccessibilityManager(mHandler, mAccessibilityManagerService, 0);
+        when(mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext))
+                .thenReturn(accessibilityManager);
+        when(mFrameworkObjectProvider.getAlertDialogBuilder(mContext))
+                .thenReturn(mAlertDialogBuilder);
+        when(mFrameworkObjectProvider.makeToastFromText(eq(mContext), anyObject(), anyInt()))
+                .thenReturn(mToast);
+
+        when(mResources.getString(anyInt())).thenReturn("Howdy %s");
+        ResolveInfo resolveInfo = mock(ResolveInfo.class);
+        when(resolveInfo.loadLabel(anyObject())).thenReturn("Service name");
+        when(mServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
+        when(mServiceInfo.getComponentName())
+                .thenReturn(ComponentName.unflattenFromString(SERVICE_NAME_STRING));
+
+        when(mAlertDialogBuilder.setTitle(anyInt())).thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.setCancelable(anyBoolean())).thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.setMessage(anyObject())).thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.setPositiveButton(anyInt(), anyObject()))
+                .thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.setNegativeButton(anyInt(), anyObject()))
+                .thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.setOnCancelListener(anyObject())).thenReturn(mAlertDialogBuilder);
+        when(mAlertDialogBuilder.create()).thenReturn(mAlertDialog);
+
+        Window window = mock(Window.class);
+        Whitebox.setInternalState(window, "mWindowAttributes", new WindowManager.LayoutParams());
+        when(mAlertDialog.getWindow()).thenReturn(window);
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void testShortcutAvailable_withNullServiceIdWhenCreated_shouldReturnFalse() {
+        configureShortcutDisabled();
+        assertFalse(getController().isAccessibilityShortcutAvailable());
+    }
+
+    @Test
+    public void testShortcutAvailable_withNonNullServiceIdWhenCreated_shouldReturnTrue() {
+        configureShortcutEnabled();
+        assertTrue(getController().isAccessibilityShortcutAvailable());
+    }
+
+    @Test
+    public void testShortcutAvailable_whenServiceIdBecomesNull_shouldReturnFalse() {
+        configureShortcutEnabled();
+        AccessibilityShortcutController accessibilityShortcutController = getController();
+        Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
+        accessibilityShortcutController.onSettingsChanged();
+        assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable());
+    }
+
+    @Test
+    public void testShortcutAvailable_whenServiceIdBecomesNonNull_shouldReturnTrue() {
+        configureShortcutDisabled();
+        AccessibilityShortcutController accessibilityShortcutController = getController();
+        configureShortcutEnabled();
+        accessibilityShortcutController.onSettingsChanged();
+        assertTrue(accessibilityShortcutController.isAccessibilityShortcutAvailable());
+    }
+
+    @Test
+    public void testOnAccessibilityShortcut_firstTime_showsWarningDialog()
+            throws Exception {
+        configureShortcutEnabled();
+        AccessibilityShortcutController accessibilityShortcutController = getController();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+        accessibilityShortcutController.performAccessibilityShortcut();
+
+        assertEquals(1, Settings.Secure.getInt(
+                mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0));
+        verify(mResources).getString(R.string.accessibility_shortcut_toogle_warning);
+        verify(mAlertDialog).show();
+        verify(mAccessibilityManagerService).getInstalledAccessibilityServiceList(anyInt());
+        verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut();
+    }
+
+    @Test
+    public void testOnAccessibilityShortcut_withDialogShowing_callsServer()
+        throws Exception {
+        configureShortcutEnabled();
+        AccessibilityShortcutController accessibilityShortcutController = getController();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+        accessibilityShortcutController.performAccessibilityShortcut();
+        accessibilityShortcutController.performAccessibilityShortcut();
+        verify(mToast).show();
+        verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut();
+    }
+
+    @Test
+    public void testOnAccessibilityShortcut_ifCanceledFirstTime_showsWarningDialog()
+        throws Exception {
+        configureShortcutEnabled();
+        AccessibilityShortcutController accessibilityShortcutController = getController();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+        accessibilityShortcutController.performAccessibilityShortcut();
+        ArgumentCaptor<AlertDialog.OnCancelListener> cancelListenerCaptor =
+                ArgumentCaptor.forClass(AlertDialog.OnCancelListener.class);
+        verify(mAlertDialogBuilder).setOnCancelListener(cancelListenerCaptor.capture());
+        // Call the cancel callback
+        cancelListenerCaptor.getValue().onCancel(null);
+
+        accessibilityShortcutController.performAccessibilityShortcut();
+        verify(mAlertDialog, times(2)).show();
+    }
+
+    @Test
+    public void testClickingDisableButtonInDialog_shouldClearShortcutId() {
+        configureShortcutEnabled();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+        getController().performAccessibilityShortcut();
+
+        ArgumentCaptor<DialogInterface.OnClickListener> captor =
+                ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
+        verify(mAlertDialogBuilder).setNegativeButton(eq(R.string.disable_accessibility_shortcut),
+                captor.capture());
+        // Call the button callback
+        captor.getValue().onClick(null, 0);
+        assertTrue(TextUtils.isEmpty(
+                Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)));
+    }
+
+    @Test
+    public void testClickingLeaveOnButtonInDialog_shouldLeaveShortcutReady() throws Exception {
+        configureShortcutEnabled();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+        getController().performAccessibilityShortcut();
+
+        ArgumentCaptor<DialogInterface.OnClickListener> captor =
+            ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
+        verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.leave_accessibility_shortcut_on),
+            captor.capture());
+        // Call the button callback, if one exists
+        if (captor.getValue() != null) {
+            captor.getValue().onClick(null, 0);
+        }
+        assertEquals(SERVICE_NAME_STRING,
+                Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE));
+        assertEquals(1, Settings.Secure.getInt(
+            mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN));
+    }
+
+    @Test
+    public void testOnAccessibilityShortcut_afterDialogShown_shouldCallServer() throws Exception {
+        configureShortcutEnabled();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
+        getController().performAccessibilityShortcut();
+
+        verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog);
+        verify(mToast).show();
+        verify(mAccessibilityManagerService).performAccessibilityShortcut();
+    }
+
+    private void configureShortcutDisabled() {
+        Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
+    }
+
+    private void configureShortcutEnabled() {
+        Settings.Secure.putString(
+                mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, SERVICE_NAME_STRING);
+    }
+
+    private AccessibilityShortcutController getController() {
+        AccessibilityShortcutController accessibilityShortcutController =
+                new AccessibilityShortcutController(mContext, mHandler);
+        accessibilityShortcutController.mFrameworkObjectProvider = mFrameworkObjectProvider;
+        return accessibilityShortcutController;
+    }
+}