Merge "TIF: Drop the requirement for ID tuple for non-broadcast channels" into nyc-dev
diff --git a/Android.mk b/Android.mk
index ffc0421..08f3468 100644
--- a/Android.mk
+++ b/Android.mk
@@ -149,6 +149,7 @@
 	core/java/android/content/pm/IPackageMoveObserver.aidl \
 	core/java/android/content/pm/IPackageStatsObserver.aidl \
 	core/java/android/content/pm/IOnPermissionsChangeListener.aidl \
+	core/java/android/content/pm/IShortcutService.aidl \
 	core/java/android/database/IContentObserver.aidl \
 	../av/camera/aidl/android/hardware/ICameraService.aidl \
 	../av/camera/aidl/android/hardware/ICameraServiceListener.aidl \
@@ -652,6 +653,7 @@
 	frameworks/base/core/java/android/content/pm/ProviderInfo.aidl \
 	frameworks/base/core/java/android/content/pm/PackageStats.aidl \
 	frameworks/base/core/java/android/content/pm/PermissionGroupInfo.aidl \
+	frameworks/base/core/java/android/content/pm/ShortcutInfo.aidl \
 	frameworks/base/core/java/android/content/pm/LabeledIntent.aidl \
 	frameworks/base/core/java/android/content/ComponentName.aidl \
 	frameworks/base/core/java/android/content/SyncStats.aidl \
diff --git a/api/current.txt b/api/current.txt
index 3fdcf0b..87d9c14 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8140,6 +8140,7 @@
     field public static final java.lang.String RESTRICTIONS_SERVICE = "restrictions";
     field public static final java.lang.String SEARCH_SERVICE = "search";
     field public static final java.lang.String SENSOR_SERVICE = "sensor";
+    field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
     field public static final java.lang.String STORAGE_SERVICE = "storage";
     field public static final java.lang.String TELECOM_SERVICE = "telecom";
     field public static final java.lang.String TELEPHONY_SERVICE = "phone";
@@ -9445,13 +9446,19 @@
   public class LauncherApps {
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
     method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
+    method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle);
+    method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
+    method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
+    method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
     method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle);
+    method public void pinShortcuts(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
     method public void registerCallback(android.content.pm.LauncherApps.Callback);
     method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
     method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
     method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+    method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
     method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
   }
 
@@ -9464,6 +9471,18 @@
     method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle);
     method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
     method public void onPackagesUnsuspended(java.lang.String[], android.os.UserHandle);
+    method public void onShortcutsChanged(java.lang.String, java.util.List<android.content.pm.ShortcutInfo>, android.os.UserHandle);
+  }
+
+  public static class LauncherApps.ShortcutQuery {
+    ctor public LauncherApps.ShortcutQuery();
+    method public void setActivity(android.content.ComponentName);
+    method public void setChangedSince(long);
+    method public void setPackage(java.lang.String);
+    method public void setQueryFlags(int);
+    field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
+    field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+    field public static final int FLAG_GET_PINNED = 2; // 0x2
   }
 
   public class PackageInfo implements android.os.Parcelable {
@@ -9963,6 +9982,56 @@
     field public java.lang.String permission;
   }
 
+  public class ShortcutInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.content.ComponentName getActivityComponent();
+    method public android.os.PersistableBundle getExtras();
+    method public java.lang.String getId();
+    method public android.content.Intent getIntent();
+    method public long getLastChangedTimestamp();
+    method public java.lang.String getPackageName();
+    method public java.lang.String getTitle();
+    method public int getWeight();
+    method public boolean hasIconFile();
+    method public boolean hasIconResource();
+    method public boolean isDynamic();
+    method public boolean isPinned();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
+    field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3
+    field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4
+    field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
+    field public static final int FLAG_DYNAMIC = 1; // 0x1
+    field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
+    field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
+    field public static final int FLAG_PINNED = 2; // 0x2
+  }
+
+  public static class ShortcutInfo.Builder {
+    ctor public ShortcutInfo.Builder(android.content.Context);
+    method public android.content.pm.ShortcutInfo build();
+    method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+    method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
+    method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
+    method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
+    method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setWeight(int);
+  }
+
+  public class ShortcutManager {
+    method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
+    method public void deleteAllDynamicShortcuts();
+    method public void deleteDynamicShortcut(java.lang.String);
+    method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
+    method public int getMaxDynamicShortcutCount();
+    method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
+    method public long getRateLimitResetTime();
+    method public int getRemainingCallCount();
+    method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+    method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+  }
+
   public class Signature implements android.os.Parcelable {
     ctor public Signature(byte[]);
     ctor public Signature(java.lang.String);
@@ -19722,7 +19791,7 @@
     method public void adjustVolume(int, int);
     method public void dispatchMediaKeyEvent(android.view.KeyEvent);
     method public int generateAudioSessionId();
-    method public android.media.AudioRecordConfiguration[] getActiveRecordConfigurations();
+    method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
     method public android.media.AudioDeviceInfo[] getDevices(int);
     method public int getMode();
     method public java.lang.String getParameters(java.lang.String);
@@ -19868,7 +19937,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordConfigChanged(android.media.AudioRecordConfiguration[]);
+    method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -19942,7 +20011,7 @@
     method public abstract void onRoutingChanged(android.media.AudioRecord);
   }
 
-  public final class AudioRecordConfiguration implements android.os.Parcelable {
+  public final class AudioRecordingConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method public android.media.AudioDeviceInfo getAudioDevice();
     method public int getClientAudioSessionId();
@@ -19950,7 +20019,7 @@
     method public android.media.AudioFormat getClientFormat();
     method public android.media.AudioFormat getFormat();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
+    field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
   }
 
   public abstract interface AudioRouting {
@@ -23023,6 +23092,7 @@
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public boolean onTrackballEvent(android.view.MotionEvent);
     method public abstract boolean onTune(android.net.Uri);
+    method public boolean onTune(android.net.Uri, android.os.Bundle);
     method public void onUnblockContent(android.media.tv.TvContentRating);
     method public void setOverlayViewEnabled(boolean);
   }
@@ -23096,12 +23166,15 @@
     method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
     method public void setStreamVolume(float);
     method public void setTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
+    method public void setZOrderMediaOverlay(boolean);
+    method public void setZOrderOnTop(boolean);
     method public void timeShiftPause();
     method public void timeShiftPlay(java.lang.String, android.net.Uri);
     method public void timeShiftResume();
     method public void timeShiftSeekTo(long);
     method public void timeShiftSetPlaybackParams(android.media.PlaybackParams);
     method public void tune(java.lang.String, android.net.Uri);
+    method public void tune(java.lang.String, android.net.Uri, android.os.Bundle);
   }
 
   public static abstract interface TvView.OnUnhandledInputEventListener {
@@ -29007,6 +29080,7 @@
     ctor public PersistableBundle();
     ctor public PersistableBundle(int);
     ctor public PersistableBundle(android.os.PersistableBundle);
+    ctor public PersistableBundle(android.os.Bundle);
     method public java.lang.Object clone();
     method public int describeContents();
     method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
@@ -34426,8 +34500,9 @@
 package android.service.notification {
 
   public class Condition implements android.os.Parcelable {
-    ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int);
+    ctor public Condition(android.net.Uri, java.lang.String, int);
     ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
+    ctor public Condition(android.os.Parcel);
     method public android.service.notification.Condition copy();
     method public int describeContents();
     method public static boolean isValidId(android.net.Uri, java.lang.String);
@@ -34458,6 +34533,7 @@
     method public final void notifyConditions(android.service.notification.Condition...);
     method public android.os.IBinder onBind(android.content.Intent);
     method public abstract void onConnected();
+    method public void onRequestConditions(int);
     method public abstract void onSubscribe(android.net.Uri);
     method public abstract void onUnsubscribe(android.net.Uri);
     field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
@@ -57404,7 +57480,23 @@
 
   public abstract interface Comparator {
     method public abstract int compare(T, T);
+    method public static java.util.Comparator<T> comparing(java.util.function.Function<? super T, ? extends U>, java.util.Comparator<? super U>);
+    method public static java.util.Comparator<T> comparing(java.util.function.Function<? super T, ? extends U>);
+    method public static java.util.Comparator<T> comparingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static java.util.Comparator<T> comparingInt(java.util.function.ToIntFunction<? super T>);
+    method public static java.util.Comparator<T> comparingLong(java.util.function.ToLongFunction<? super T>);
     method public abstract boolean equals(java.lang.Object);
+    method public static java.util.Comparator<T> naturalOrder();
+    method public static java.util.Comparator<T> nullsFirst(java.util.Comparator<? super T>);
+    method public static java.util.Comparator<T> nullsLast(java.util.Comparator<? super T>);
+    method public static java.util.Comparator<T> reverseOrder();
+    method public default java.util.Comparator<T> reversed();
+    method public default java.util.Comparator<T> thenComparing(java.util.Comparator<? super T>);
+    method public default java.util.Comparator<T> thenComparing(java.util.function.Function<? super T, ? extends U>, java.util.Comparator<? super U>);
+    method public default java.util.Comparator<T> thenComparing(java.util.function.Function<? super T, ? extends U>);
+    method public default java.util.Comparator<T> thenComparingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public default java.util.Comparator<T> thenComparingInt(java.util.function.ToIntFunction<? super T>);
+    method public default java.util.Comparator<T> thenComparingLong(java.util.function.ToLongFunction<? super T>);
   }
 
   public class ConcurrentModificationException extends java.lang.RuntimeException {
diff --git a/api/removed.txt b/api/removed.txt
index 115224c..2f55373 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -43,6 +43,14 @@
 
 }
 
+package android.media.tv {
+
+  public class TvView extends android.view.ViewGroup {
+    method public void requestUnblockContent(android.media.tv.TvContentRating);
+  }
+
+}
+
 package android.net {
 
   public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
@@ -201,14 +209,6 @@
 
 }
 
-package android.service.notification {
-
-  public abstract class ConditionProviderService extends android.app.Service {
-    method public void onRequestConditions(int);
-  }
-
-}
-
 package android.test.mock {
 
   public deprecated class MockPackageManager extends android.content.pm.PackageManager {
diff --git a/api/system-current.txt b/api/system-current.txt
index 59d53e0..545a3e0 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8446,6 +8446,7 @@
     field public static final java.lang.String RESTRICTIONS_SERVICE = "restrictions";
     field public static final java.lang.String SEARCH_SERVICE = "search";
     field public static final java.lang.String SENSOR_SERVICE = "sensor";
+    field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
     field public static final java.lang.String STORAGE_SERVICE = "storage";
     field public static final java.lang.String TELECOM_SERVICE = "telecom";
     field public static final java.lang.String TELEPHONY_SERVICE = "phone";
@@ -9779,13 +9780,19 @@
   public class LauncherApps {
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
     method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
+    method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle);
+    method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
+    method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
+    method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
     method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle);
+    method public void pinShortcuts(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
     method public void registerCallback(android.content.pm.LauncherApps.Callback);
     method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
     method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
     method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+    method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
     method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
   }
 
@@ -9798,6 +9805,18 @@
     method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle);
     method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
     method public void onPackagesUnsuspended(java.lang.String[], android.os.UserHandle);
+    method public void onShortcutsChanged(java.lang.String, java.util.List<android.content.pm.ShortcutInfo>, android.os.UserHandle);
+  }
+
+  public static class LauncherApps.ShortcutQuery {
+    ctor public LauncherApps.ShortcutQuery();
+    method public void setActivity(android.content.ComponentName);
+    method public void setChangedSince(long);
+    method public void setPackage(java.lang.String);
+    method public void setQueryFlags(int);
+    field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
+    field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+    field public static final int FLAG_GET_PINNED = 2; // 0x2
   }
 
   public class PackageInfo implements android.os.Parcelable {
@@ -10357,6 +10376,56 @@
     field public java.lang.String permission;
   }
 
+  public class ShortcutInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.content.ComponentName getActivityComponent();
+    method public android.os.PersistableBundle getExtras();
+    method public java.lang.String getId();
+    method public android.content.Intent getIntent();
+    method public long getLastChangedTimestamp();
+    method public java.lang.String getPackageName();
+    method public java.lang.String getTitle();
+    method public int getWeight();
+    method public boolean hasIconFile();
+    method public boolean hasIconResource();
+    method public boolean isDynamic();
+    method public boolean isPinned();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
+    field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3
+    field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4
+    field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
+    field public static final int FLAG_DYNAMIC = 1; // 0x1
+    field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
+    field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
+    field public static final int FLAG_PINNED = 2; // 0x2
+  }
+
+  public static class ShortcutInfo.Builder {
+    ctor public ShortcutInfo.Builder(android.content.Context);
+    method public android.content.pm.ShortcutInfo build();
+    method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+    method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
+    method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
+    method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
+    method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setWeight(int);
+  }
+
+  public class ShortcutManager {
+    method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
+    method public void deleteAllDynamicShortcuts();
+    method public void deleteDynamicShortcut(java.lang.String);
+    method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
+    method public int getMaxDynamicShortcutCount();
+    method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
+    method public long getRateLimitResetTime();
+    method public int getRemainingCallCount();
+    method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+    method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+  }
+
   public class Signature implements android.os.Parcelable {
     ctor public Signature(byte[]);
     ctor public Signature(java.lang.String);
@@ -21203,7 +21272,7 @@
     method public void adjustVolume(int, int);
     method public void dispatchMediaKeyEvent(android.view.KeyEvent);
     method public int generateAudioSessionId();
-    method public android.media.AudioRecordConfiguration[] getActiveRecordConfigurations();
+    method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
     method public android.media.AudioDeviceInfo[] getDevices(int);
     method public int getMode();
     method public java.lang.String getParameters(java.lang.String);
@@ -21357,7 +21426,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordConfigChanged(android.media.AudioRecordConfiguration[]);
+    method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -21434,7 +21503,7 @@
     method public abstract void onRoutingChanged(android.media.AudioRecord);
   }
 
-  public final class AudioRecordConfiguration implements android.os.Parcelable {
+  public final class AudioRecordingConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method public android.media.AudioDeviceInfo getAudioDevice();
     method public int getClientAudioSessionId();
@@ -21442,7 +21511,7 @@
     method public android.media.AudioFormat getClientFormat();
     method public android.media.AudioFormat getFormat();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
+    field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
   }
 
   public abstract interface AudioRouting {
@@ -24872,7 +24941,6 @@
     method public java.util.List<android.media.tv.TvTrackInfo> getTracks(int);
     method protected void onLayout(boolean, int, int, int, int);
     method public boolean onUnhandledInputEvent(android.view.InputEvent);
-    method public deprecated void requestUnblockContent(android.media.tv.TvContentRating);
     method public void reset();
     method public void selectTrack(int, java.lang.String);
     method public void sendAppPrivateCommand(java.lang.String, android.os.Bundle);
@@ -31292,6 +31360,7 @@
     ctor public PersistableBundle();
     ctor public PersistableBundle(int);
     ctor public PersistableBundle(android.os.PersistableBundle);
+    ctor public PersistableBundle(android.os.Bundle);
     method public java.lang.Object clone();
     method public int describeContents();
     method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
@@ -36916,8 +36985,9 @@
 package android.service.notification {
 
   public class Condition implements android.os.Parcelable {
-    ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int);
+    ctor public Condition(android.net.Uri, java.lang.String, int);
     ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
+    ctor public Condition(android.os.Parcel);
     method public android.service.notification.Condition copy();
     method public int describeContents();
     method public static boolean isValidId(android.net.Uri, java.lang.String);
@@ -36958,37 +37028,6 @@
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
   }
 
-  public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
-    ctor public NotificationAssistantService();
-    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
-    method public final android.os.IBinder onBind(android.content.Intent);
-    method public void onNotificationActionClick(java.lang.String, long, int);
-    method public void onNotificationClick(java.lang.String, long);
-    method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
-    method public void onNotificationRemoved(java.lang.String, long, int);
-    method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
-    field public static final int REASON_APP_CANCEL = 8; // 0x8
-    field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
-    field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
-    field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3
-    field public static final int REASON_DELEGATE_CLICK = 1; // 0x1
-    field public static final int REASON_DELEGATE_ERROR = 4; // 0x4
-    field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd
-    field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc
-    field public static final int REASON_LISTENER_CANCEL = 10; // 0xa
-    field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
-    field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
-    field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
-    field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe
-    field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
-    field public static final int REASON_USER_STOPPED = 6; // 0x6
-    field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
-  }
-
-  public class NotificationAssistantService.Adjustment {
-    ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
-  }
-
   public abstract class NotificationListenerService extends android.app.Service {
     ctor public NotificationListenerService();
     method public final void cancelAllNotifications();
@@ -37058,6 +37097,37 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.NotificationListenerService.RankingMap> CREATOR;
   }
 
+  public abstract class NotificationRankerService extends android.service.notification.NotificationListenerService {
+    ctor public NotificationRankerService();
+    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationRankerService.Adjustment);
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public void onNotificationActionClick(java.lang.String, long, int);
+    method public void onNotificationClick(java.lang.String, long);
+    method public abstract android.service.notification.NotificationRankerService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+    method public void onNotificationRemoved(java.lang.String, long, int);
+    method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
+    field public static final int REASON_APP_CANCEL = 8; // 0x8
+    field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
+    field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
+    field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3
+    field public static final int REASON_DELEGATE_CLICK = 1; // 0x1
+    field public static final int REASON_DELEGATE_ERROR = 4; // 0x4
+    field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd
+    field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc
+    field public static final int REASON_LISTENER_CANCEL = 10; // 0xa
+    field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
+    field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
+    field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+    field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe
+    field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
+    field public static final int REASON_USER_STOPPED = 6; // 0x6
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationRankerService";
+  }
+
+  public class NotificationRankerService.Adjustment {
+    ctor public NotificationRankerService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+  }
+
   public class StatusBarNotification implements android.os.Parcelable {
     ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long);
     ctor public StatusBarNotification(android.os.Parcel);
@@ -60508,7 +60578,23 @@
 
   public abstract interface Comparator {
     method public abstract int compare(T, T);
+    method public static java.util.Comparator<T> comparing(java.util.function.Function<? super T, ? extends U>, java.util.Comparator<? super U>);
+    method public static java.util.Comparator<T> comparing(java.util.function.Function<? super T, ? extends U>);
+    method public static java.util.Comparator<T> comparingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static java.util.Comparator<T> comparingInt(java.util.function.ToIntFunction<? super T>);
+    method public static java.util.Comparator<T> comparingLong(java.util.function.ToLongFunction<? super T>);
     method public abstract boolean equals(java.lang.Object);
+    method public static java.util.Comparator<T> naturalOrder();
+    method public static java.util.Comparator<T> nullsFirst(java.util.Comparator<? super T>);
+    method public static java.util.Comparator<T> nullsLast(java.util.Comparator<? super T>);
+    method public static java.util.Comparator<T> reverseOrder();
+    method public default java.util.Comparator<T> reversed();
+    method public default java.util.Comparator<T> thenComparing(java.util.Comparator<? super T>);
+    method public default java.util.Comparator<T> thenComparing(java.util.function.Function<? super T, ? extends U>, java.util.Comparator<? super U>);
+    method public default java.util.Comparator<T> thenComparing(java.util.function.Function<? super T, ? extends U>);
+    method public default java.util.Comparator<T> thenComparingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public default java.util.Comparator<T> thenComparingInt(java.util.function.ToIntFunction<? super T>);
+    method public default java.util.Comparator<T> thenComparingLong(java.util.function.ToLongFunction<? super T>);
   }
 
   public class ConcurrentModificationException extends java.lang.RuntimeException {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 9ebc484..79f7297 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -41,6 +41,14 @@
 
 }
 
+package android.media.tv {
+
+  public class TvView extends android.view.ViewGroup {
+    method public void requestUnblockContent(android.media.tv.TvContentRating);
+  }
+
+}
+
 package android.net {
 
   public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
diff --git a/api/test-current.txt b/api/test-current.txt
index f298dc4..35b63a2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -8146,6 +8146,7 @@
     field public static final java.lang.String RESTRICTIONS_SERVICE = "restrictions";
     field public static final java.lang.String SEARCH_SERVICE = "search";
     field public static final java.lang.String SENSOR_SERVICE = "sensor";
+    field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
     field public static final java.lang.String STORAGE_SERVICE = "storage";
     field public static final java.lang.String TELECOM_SERVICE = "telecom";
     field public static final java.lang.String TELEPHONY_SERVICE = "phone";
@@ -9454,13 +9455,19 @@
   public class LauncherApps {
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
     method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
+    method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle);
+    method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
+    method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
+    method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
     method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
     method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle);
+    method public void pinShortcuts(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
     method public void registerCallback(android.content.pm.LauncherApps.Callback);
     method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
     method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
     method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
     method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+    method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
     method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
   }
 
@@ -9473,6 +9480,18 @@
     method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle);
     method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
     method public void onPackagesUnsuspended(java.lang.String[], android.os.UserHandle);
+    method public void onShortcutsChanged(java.lang.String, java.util.List<android.content.pm.ShortcutInfo>, android.os.UserHandle);
+  }
+
+  public static class LauncherApps.ShortcutQuery {
+    ctor public LauncherApps.ShortcutQuery();
+    method public void setActivity(android.content.ComponentName);
+    method public void setChangedSince(long);
+    method public void setPackage(java.lang.String);
+    method public void setQueryFlags(int);
+    field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
+    field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+    field public static final int FLAG_GET_PINNED = 2; // 0x2
   }
 
   public class PackageInfo implements android.os.Parcelable {
@@ -9973,6 +9992,56 @@
     field public java.lang.String permission;
   }
 
+  public class ShortcutInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.content.ComponentName getActivityComponent();
+    method public android.os.PersistableBundle getExtras();
+    method public java.lang.String getId();
+    method public android.content.Intent getIntent();
+    method public long getLastChangedTimestamp();
+    method public java.lang.String getPackageName();
+    method public java.lang.String getTitle();
+    method public int getWeight();
+    method public boolean hasIconFile();
+    method public boolean hasIconResource();
+    method public boolean isDynamic();
+    method public boolean isPinned();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
+    field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3
+    field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4
+    field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
+    field public static final int FLAG_DYNAMIC = 1; // 0x1
+    field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
+    field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
+    field public static final int FLAG_PINNED = 2; // 0x2
+  }
+
+  public static class ShortcutInfo.Builder {
+    ctor public ShortcutInfo.Builder(android.content.Context);
+    method public android.content.pm.ShortcutInfo build();
+    method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+    method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
+    method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
+    method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
+    method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setWeight(int);
+  }
+
+  public class ShortcutManager {
+    method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
+    method public void deleteAllDynamicShortcuts();
+    method public void deleteDynamicShortcut(java.lang.String);
+    method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
+    method public int getMaxDynamicShortcutCount();
+    method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
+    method public long getRateLimitResetTime();
+    method public int getRemainingCallCount();
+    method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+    method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
+  }
+
   public class Signature implements android.os.Parcelable {
     ctor public Signature(byte[]);
     ctor public Signature(java.lang.String);
@@ -19733,7 +19802,7 @@
     method public void adjustVolume(int, int);
     method public void dispatchMediaKeyEvent(android.view.KeyEvent);
     method public int generateAudioSessionId();
-    method public android.media.AudioRecordConfiguration[] getActiveRecordConfigurations();
+    method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
     method public android.media.AudioDeviceInfo[] getDevices(int);
     method public int getMode();
     method public java.lang.String getParameters(java.lang.String);
@@ -19879,7 +19948,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordConfigChanged(android.media.AudioRecordConfiguration[]);
+    method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -19953,7 +20022,7 @@
     method public abstract void onRoutingChanged(android.media.AudioRecord);
   }
 
-  public final class AudioRecordConfiguration implements android.os.Parcelable {
+  public final class AudioRecordingConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method public android.media.AudioDeviceInfo getAudioDevice();
     method public int getClientAudioSessionId();
@@ -19961,7 +20030,7 @@
     method public android.media.AudioFormat getClientFormat();
     method public android.media.AudioFormat getFormat();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
+    field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
   }
 
   public abstract interface AudioRouting {
@@ -23034,6 +23103,7 @@
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public boolean onTrackballEvent(android.view.MotionEvent);
     method public abstract boolean onTune(android.net.Uri);
+    method public boolean onTune(android.net.Uri, android.os.Bundle);
     method public void onUnblockContent(android.media.tv.TvContentRating);
     method public void setOverlayViewEnabled(boolean);
   }
@@ -23107,12 +23177,15 @@
     method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
     method public void setStreamVolume(float);
     method public void setTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
+    method public void setZOrderMediaOverlay(boolean);
+    method public void setZOrderOnTop(boolean);
     method public void timeShiftPause();
     method public void timeShiftPlay(java.lang.String, android.net.Uri);
     method public void timeShiftResume();
     method public void timeShiftSeekTo(long);
     method public void timeShiftSetPlaybackParams(android.media.PlaybackParams);
     method public void tune(java.lang.String, android.net.Uri);
+    method public void tune(java.lang.String, android.net.Uri, android.os.Bundle);
   }
 
   public static abstract interface TvView.OnUnhandledInputEventListener {
@@ -29018,6 +29091,7 @@
     ctor public PersistableBundle();
     ctor public PersistableBundle(int);
     ctor public PersistableBundle(android.os.PersistableBundle);
+    ctor public PersistableBundle(android.os.Bundle);
     method public java.lang.Object clone();
     method public int describeContents();
     method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
@@ -34443,8 +34517,9 @@
 package android.service.notification {
 
   public class Condition implements android.os.Parcelable {
-    ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int);
+    ctor public Condition(android.net.Uri, java.lang.String, int);
     ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
+    ctor public Condition(android.os.Parcel);
     method public android.service.notification.Condition copy();
     method public int describeContents();
     method public static boolean isValidId(android.net.Uri, java.lang.String);
@@ -34475,6 +34550,7 @@
     method public final void notifyConditions(android.service.notification.Condition...);
     method public android.os.IBinder onBind(android.content.Intent);
     method public abstract void onConnected();
+    method public void onRequestConditions(int);
     method public abstract void onSubscribe(android.net.Uri);
     method public abstract void onUnsubscribe(android.net.Uri);
     field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
@@ -57423,7 +57499,23 @@
 
   public abstract interface Comparator {
     method public abstract int compare(T, T);
+    method public static java.util.Comparator<T> comparing(java.util.function.Function<? super T, ? extends U>, java.util.Comparator<? super U>);
+    method public static java.util.Comparator<T> comparing(java.util.function.Function<? super T, ? extends U>);
+    method public static java.util.Comparator<T> comparingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public static java.util.Comparator<T> comparingInt(java.util.function.ToIntFunction<? super T>);
+    method public static java.util.Comparator<T> comparingLong(java.util.function.ToLongFunction<? super T>);
     method public abstract boolean equals(java.lang.Object);
+    method public static java.util.Comparator<T> naturalOrder();
+    method public static java.util.Comparator<T> nullsFirst(java.util.Comparator<? super T>);
+    method public static java.util.Comparator<T> nullsLast(java.util.Comparator<? super T>);
+    method public static java.util.Comparator<T> reverseOrder();
+    method public default java.util.Comparator<T> reversed();
+    method public default java.util.Comparator<T> thenComparing(java.util.Comparator<? super T>);
+    method public default java.util.Comparator<T> thenComparing(java.util.function.Function<? super T, ? extends U>, java.util.Comparator<? super U>);
+    method public default java.util.Comparator<T> thenComparing(java.util.function.Function<? super T, ? extends U>);
+    method public default java.util.Comparator<T> thenComparingDouble(java.util.function.ToDoubleFunction<? super T>);
+    method public default java.util.Comparator<T> thenComparingInt(java.util.function.ToIntFunction<? super T>);
+    method public default java.util.Comparator<T> thenComparingLong(java.util.function.ToLongFunction<? super T>);
   }
 
   public class ConcurrentModificationException extends java.lang.RuntimeException {
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 115224c..2f55373 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -43,6 +43,14 @@
 
 }
 
+package android.media.tv {
+
+  public class TvView extends android.view.ViewGroup {
+    method public void requestUnblockContent(android.media.tv.TvContentRating);
+  }
+
+}
+
 package android.net {
 
   public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
@@ -201,14 +209,6 @@
 
 }
 
-package android.service.notification {
-
-  public abstract class ConditionProviderService extends android.app.Service {
-    method public void onRequestConditions(int);
-  }
-
-}
-
 package android.test.mock {
 
   public deprecated class MockPackageManager extends android.content.pm.PackageManager {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f64bf1d..2f6907e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1575,6 +1575,7 @@
                     Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
             dest.writeInt(numActivities);
             dest.writeInt(numRunning);
+            dest.writeInt(isDockable ? 1 : 0);
         }
 
         public void readFromParcel(Parcel source) {
@@ -1590,6 +1591,7 @@
             description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
             numActivities = source.readInt();
             numRunning = source.readInt();
+            isDockable = source.readInt() != 0;
         }
 
         public static final Creator<RunningTaskInfo> CREATOR = new Creator<RunningTaskInfo>() {
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 1f1f318..cd4ace6 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -42,7 +42,7 @@
      * @param name The name of the rule.
      * @param owner The Condition Provider service that owns this rule.
      * @param conditionId A representation of the state that should cause the Condition Provider
-     *                    service to apply the interruption filter.
+     *                    service to apply the given interruption filter.
      * @param interruptionFilter The interruption filter defines which notifications are allowed to
      *                           interrupt the user (e.g. via sound &amp; vibration) while this rule
      *                           is active.
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 5697924..2c270fc 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -61,7 +61,7 @@
     StatusBarNotification[] getActiveNotifications(String callingPkg);
     StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count);
 
-    void registerListener(in INotificationListener listener, in ComponentName component, int userid);
+    void registerListener(in INotificationListener listener, in ComponentName component, int userid, boolean asRanker);
     void unregisterListener(in INotificationListener listener, int userid);
 
     void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
@@ -80,7 +80,7 @@
     void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
     void setInterruptionFilter(String pkg, int interruptionFilter);
 
-    void setImportanceFromAssistant(in INotificationListener token, String key, int importance, CharSequence explanation);
+    void setImportanceFromRankerService(in INotificationListener token, String key, int importance, CharSequence explanation);
 
     ComponentName getEffectsSuppressor();
     boolean matchesCallFilter(in Bundle extras);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d8f0ac5..a2cc6b1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -47,6 +47,7 @@
 import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.style.AbsoluteSizeSpan;
+import android.text.style.CharacterStyle;
 import android.text.style.RelativeSizeSpan;
 import android.text.style.TextAppearanceSpan;
 import android.util.Log;
@@ -1664,17 +1665,22 @@
             SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
             for (Object span : spans) {
                 Object resultSpan = span;
-                if (span instanceof TextAppearanceSpan) {
-                    TextAppearanceSpan originalSpan = (TextAppearanceSpan) span;
+                if (resultSpan instanceof CharacterStyle) {
+                    resultSpan = ((CharacterStyle) span).getUnderlying();
+                }
+                if (resultSpan instanceof TextAppearanceSpan) {
+                    TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
                     resultSpan = new TextAppearanceSpan(
                             originalSpan.getFamily(),
                             originalSpan.getTextStyle(),
                             -1,
                             originalSpan.getTextColor(),
                             originalSpan.getLinkTextColor());
-                } else if (span instanceof RelativeSizeSpan
-                        || span instanceof AbsoluteSizeSpan) {
+                } else if (resultSpan instanceof RelativeSizeSpan
+                        || resultSpan instanceof AbsoluteSizeSpan) {
                     continue;
+                } else {
+                    resultSpan = span;
                 }
                 builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
                         ss.getSpanFlags(span));
@@ -3591,37 +3597,53 @@
         }
 
         /**
+         * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
+         * change.
+         *
+         * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
+         *
          * @hide
          */
-        public static void stripForDelivery(Notification n) {
+        public static Notification maybeCloneStrippedForDelivery(Notification n) {
             String templateClass = n.extras.getString(EXTRA_TEMPLATE);
-            if (TextUtils.isEmpty(templateClass)) {
-                return;
-            }
+
             // Only strip views for known Styles because we won't know how to
             // re-create them otherwise.
-            if (getNotificationStyleClass(templateClass) == null) {
-                return;
+            if (!TextUtils.isEmpty(templateClass)
+                    && getNotificationStyleClass(templateClass) == null) {
+                return n;
             }
-            // Get rid of unmodified BuilderRemoteViews.
-            if (n.contentView instanceof BuilderRemoteViews &&
+
+            // Only strip unmodified BuilderRemoteViews.
+            boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
                     n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
-                            n.contentView.getSequenceNumber()) {
-                n.contentView = null;
-                n.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
-            }
-            if (n.bigContentView instanceof BuilderRemoteViews &&
+                            n.contentView.getSequenceNumber();
+            boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
                     n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
-                            n.bigContentView.getSequenceNumber()) {
-                n.bigContentView = null;
-                n.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
-            }
-            if (n.headsUpContentView instanceof BuilderRemoteViews &&
+                            n.bigContentView.getSequenceNumber();
+            boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
                     n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
-                            n.headsUpContentView.getSequenceNumber()) {
-                n.headsUpContentView = null;
-                n.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
+                            n.headsUpContentView.getSequenceNumber();
+
+            // Nothing to do here, no need to clone.
+            if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
+                return n;
             }
+
+            Notification clone = n.clone();
+            if (stripContentView) {
+                clone.contentView = null;
+                clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
+            }
+            if (stripBigContentView) {
+                clone.bigContentView = null;
+                clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
+            }
+            if (stripHeadsUpContentView) {
+                clone.headsUpContentView = null;
+                clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
+            }
+            return clone;
         }
 
         private int getBaseLayoutResource() {
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 16aee78..7cfa0cd 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -259,8 +259,7 @@
             }
         }
         if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
-        final Notification copy = notification.clone();
-        Builder.stripForDelivery(copy);
+        final Notification copy = Builder.maybeCloneStrippedForDelivery(notification);
         try {
             service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
                     copy, idOut, user.getIdentifier());
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index b1c5fd8..3a5dd30 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -37,7 +37,9 @@
 import android.content.IRestrictionsManager;
 import android.content.RestrictionsManager;
 import android.content.pm.ILauncherApps;
+import android.content.pm.IShortcutService;
 import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutManager;
 import android.content.res.Resources;
 import android.hardware.ConsumerIrManager;
 import android.hardware.ISerialManager;
@@ -748,6 +750,15 @@
                 Log.i(TAG, "Creating new instance of SoundTriggerManager object.");
                 return new SoundTriggerManager(ctx, ISoundTriggerService.Stub.asInterface(b));
             }});
+
+        registerService(Context.SHORTCUT_SERVICE, ShortcutManager.class,
+                new CachedServiceFetcher<ShortcutManager>() {
+                    @Override
+                    public ShortcutManager createService(ContextImpl ctx) {
+                        IBinder b = ServiceManager.getService(Context.SHORTCUT_SERVICE);
+                        return new ShortcutManager(ctx,
+                                IShortcutService.Stub.asInterface(b));
+                    }});
     }
 
     /**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b935b25..f96ddf0 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2680,6 +2680,7 @@
             RADIO_SERVICE,
             HARDWARE_PROPERTIES_SERVICE,
             //@hide: SOUND_TRIGGER_SERVICE,
+            SHORTCUT_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceName {}
@@ -3576,6 +3577,14 @@
     public static final String HARDWARE_PROPERTIES_SERVICE = "hardwareproperties";
 
     /**
+     * TODO Javadoc
+     *
+     * @see #getSystemService
+     * @see android.content.pm.ShortcutManager
+     */
+    public static final String SHORTCUT_SERVICE = "shortcut";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index cc266c5..da3c873 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -22,6 +22,7 @@
 import android.content.pm.IOnAppsChangedListener;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -42,4 +43,13 @@
     boolean isPackageEnabled(String packageName, in UserHandle user);
     boolean isActivityEnabled(in ComponentName component, in UserHandle user);
     ApplicationInfo getApplicationInfo(String packageName, int flags, in UserHandle user);
+
+    ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName,
+            in ComponentName componentName, int flags, in UserHandle user);
+    ParceledListSlice getShortcutInfo(String callingPackage, String packageName, in List<String> ids,
+            in UserHandle user);
+    void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
+            in UserHandle user);
+    void startShortcut(String callingPackage, in ShortcutInfo shortcut, in Rect sourceBounds,
+            in Bundle startActivityOptions, in UserHandle user);
 }
diff --git a/core/java/android/content/pm/IOnAppsChangedListener.aidl b/core/java/android/content/pm/IOnAppsChangedListener.aidl
index 1303696..e6525af 100644
--- a/core/java/android/content/pm/IOnAppsChangedListener.aidl
+++ b/core/java/android/content/pm/IOnAppsChangedListener.aidl
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.content.pm.ParceledListSlice;
 import android.os.UserHandle;
 
 /**
@@ -29,4 +30,5 @@
     void onPackagesUnavailable(in UserHandle user, in String[] packageNames, boolean replacing);
     void onPackagesSuspended(in UserHandle user, in String[] packageNames);
     void onPackagesUnsuspended(in UserHandle user, in String[] packageNames);
+    void onShortcutChanged(in UserHandle user, String packageName, in ParceledListSlice shortcuts);
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index b4e9f60..c684447 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -120,6 +120,8 @@
 
     int checkUidSignatures(int uid1, int uid2);
 
+    List<String> getAllPackages();
+
     String[] getPackagesForUid(int uid);
 
     String getNameForUid(int uid);
@@ -380,6 +382,13 @@
      */
     void clearApplicationUserData(in String packageName, IPackageDataObserver observer, int userId);
 
+    /**
+     * Clear the profile data of an application.
+     * @param packageName The package name of the application whose profile data
+     * need to be deleted
+     */
+    void clearApplicationProfileData(in String packageName);
+
    /**
      * Get package statistics including the code, data and cache size for
      * an already installed package
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
new file mode 100644
index 0000000..23e671d
--- /dev/null
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ShortcutInfo;
+
+/**
+ * {@hide}
+ */
+interface IShortcutService {
+
+    boolean setDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
+            int userId);
+
+    ParceledListSlice getDynamicShortcuts(String packageName, int userId);
+
+    boolean addDynamicShortcut(String packageName, in ShortcutInfo shortcutInfo, int userId);
+
+    void deleteDynamicShortcut(String packageName, in String shortcutId, int userId);
+
+    void deleteAllDynamicShortcuts(String packageName, int userId);
+
+    ParceledListSlice getPinnedShortcuts(String packageName, int userId);
+
+    boolean updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId);
+
+    int getMaxDynamicShortcutCount(String packageName, int userId);
+
+    int getRemainingCallCount(String packageName, int userId);
+
+    long getRateLimitResetTime(String packageName, int userId);
+
+    void resetThrottling(); // system only API for developer opsions
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index e443d50..f05ecd3 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -16,23 +16,29 @@
 
 package android.content.pm;
 
+import android.Manifest.permission;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ILauncherApps;
-import android.content.pm.IOnAppsChangedListener;
 import android.content.pm.PackageManager.ApplicationInfoFlags;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -148,6 +154,91 @@
          */
         public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
         }
+
+        /**
+         * Indicates that one or more shortcuts (which may be dynamic and/or pinned)
+         * have been added, updated or removed.
+         *
+         * @param packageName The name of the package that has the shortcuts.
+         * @param shortcuts all shortcuts from the package (dynamic and/or pinned).
+         * @param user The UserHandle of the profile that generated the change.
+         */
+        public void onShortcutsChanged(@NonNull String packageName,
+                @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
+        }
+    }
+
+    /**
+     * Represents a query passed to {@link #getShortcuts(ShortcutQuery, UserHandle)}.
+     */
+    public static class ShortcutQuery {
+        /**
+         * Include dynamic shortcuts in the result.
+         */
+        public static final int FLAG_GET_DYNAMIC = 1 << 0;
+
+        /**
+         * Include pinned shortcuts in the result.
+         */
+        public static final int FLAG_GET_PINNED = 1 << 1;
+
+        /**
+         * Requests "key" fields only.
+         */
+        public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
+
+        /** @hide */
+        @IntDef(flag = true,
+                value = {
+                        FLAG_GET_DYNAMIC,
+                        FLAG_GET_PINNED,
+                        FLAG_GET_KEY_FIELDS_ONLY,
+                })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface QueryFlags {}
+
+        long mChangedSince;
+
+        @Nullable
+        String mPackage;
+
+        @Nullable
+        ComponentName mActivity;
+
+        @QueryFlags
+        int mQueryFlags;
+
+        public ShortcutQuery() {
+        }
+
+        /**
+         * If non-zero, returns only shortcuts that have been added or updated since the timestamp,
+         * which is a milliseconds since the Epoch.
+         */
+        public void setChangedSince(long changedSince) {
+            mChangedSince = changedSince;
+        }
+
+        /**
+         * If non-null, returns only shortcuts from the package.
+         */
+        public void setPackage(@Nullable String packageName) {
+            mPackage = packageName;
+        }
+
+        /**
+         * If non-null, returns only shortcuts associated with the activity.
+         */
+        public void setActivity(@Nullable ComponentName activity) {
+            mActivity = activity;
+        }
+
+        /**
+         * Set query options.
+         */
+        public void setQueryFlags(@QueryFlags int queryFlags) {
+            mQueryFlags = queryFlags;
+        }
     }
 
     /** @hide */
@@ -302,6 +393,125 @@
         }
     }
 
+    /**
+     * Returns the IDs of {@link ShortcutInfo}s that match {@code query}.
+     *
+     * <p>Callers mut have the {@link permission#BIND_APPWIDGET} permission.
+     *
+     * @param query result includes shortcuts matching this query.
+     * @param user The UserHandle of the profile.
+     *
+     * @return the IDs of {@link ShortcutInfo}s that match the query.
+     */
+    @RequiresPermission(permission.BIND_APPWIDGET)
+    @Nullable
+    public List<ShortcutInfo> getShortcuts(@NonNull ShortcutQuery query,
+            @NonNull UserHandle user) {
+        try {
+            return mService.getShortcuts(mContext.getPackageName(),
+                    query.mChangedSince, query.mPackage, query.mActivity, query.mQueryFlags, user)
+                    .getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns {@link ShortcutInfo}s with the given IDs from a package.
+     *
+     * <p>Callers mut have the {@link permission#BIND_APPWIDGET} permission.
+     *
+     * @param packageName The target package.
+     * @param ids IDs of the shortcuts to retrieve.
+     * @param user The UserHandle of the profile.
+     *
+     * @return list of {@link ShortcutInfo} associated with the package.
+     */
+    @RequiresPermission(permission.BIND_APPWIDGET)
+    @Nullable
+    public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName,
+            @NonNull List<String> ids, @NonNull UserHandle user) {
+        try {
+            return mService.getShortcutInfo(mContext.getPackageName(), packageName, ids, user)
+                    .getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
+     * Pin shortcuts on a package.
+     *
+     * <p>This API is <b>NOT</b> cumulative; this will replace all pinned shortcuts for the package.
+     * However, different launchers may have different set of pinned shortcuts.
+     *
+     * <p>Callers must have the {@link permission#BIND_APPWIDGET} permission.
+     *
+     * @param packageName The target package name.
+     * @param shortcutIds The IDs of the shortcut to be pinned.
+     * @param user The UserHandle of the profile.
+     */
+    @RequiresPermission(permission.BIND_APPWIDGET)
+    public void pinShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
+            @NonNull UserHandle user) {
+        try {
+            mService.pinShortcuts(mContext.getPackageName(), packageName, shortcutIds, user);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the icon resource ID, if {@code shortcut} has one
+     * (i.e. when {@link ShortcutInfo#hasIconResource()} returns {@code true}).
+     *
+     * <p>Callers mut have the {@link permission#BIND_APPWIDGET} permission.
+     *
+     * @param shortcut The target shortcut.
+     * @param user The UserHandle of the profile.
+     */
+    @RequiresPermission(permission.BIND_APPWIDGET)
+    public int getShortcutIconResId(@NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
+        throw new RuntimeException("not implemented yet");
+    }
+
+    /**
+     * Return the icon as {@link ParcelFileDescriptor}, when it's stored as a file
+     * (i.e. when {@link ShortcutInfo#hasIconFile()} returns {@code true}).
+     *
+     * <p>Callers mut have the {@link permission#BIND_APPWIDGET} permission.
+     *
+     * @param shortcut The target shortcut.
+     * @param user The UserHandle of the profile.
+     */
+    @RequiresPermission(permission.BIND_APPWIDGET)
+    public ParcelFileDescriptor getShortcutIconFd(
+            @NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
+        throw new RuntimeException("not implemented yet");
+    }
+
+    /**
+     * Launches a shortcut.
+     *
+     * <p>Callers mut have the {@link permission#BIND_APPWIDGET} permission.
+     *
+     * @param shortcut The target shortcut.
+     * @param sourceBounds The Rect containing the source bounds of the clicked icon.
+     * @param startActivityOptions Options to pass to startActivity.
+     * @param user The UserHandle of the profile.
+     */
+    @RequiresPermission(permission.BIND_APPWIDGET)
+    public void startShortcut(@NonNull ShortcutInfo shortcut,
+            @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
+            @NonNull UserHandle user) {
+        try {
+            mService.startShortcut(mContext.getPackageName(), shortcut, sourceBounds,
+                    startActivityOptions, user);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 
     /**
      * Registers a callback for changes to packages in current and managed profiles.
@@ -474,6 +684,20 @@
                 }
             }
         }
+
+        @Override
+        public void onShortcutChanged(UserHandle user, String packageName,
+                ParceledListSlice shortcuts) {
+            if (DEBUG) {
+                Log.d(TAG, "onShortcutChanged " + user.getIdentifier() + "," + packageName);
+            }
+            final List<ShortcutInfo> list = shortcuts.getList();
+            synchronized (LauncherApps.this) {
+                for (CallbackMessageHandler callback : mCallbacks) {
+                    callback.postOnShortcutChanged(packageName, user, list);
+                }
+            }
+        }
     };
 
     private static class CallbackMessageHandler extends Handler {
@@ -484,6 +708,7 @@
         private static final int MSG_UNAVAILABLE = 5;
         private static final int MSG_SUSPENDED = 6;
         private static final int MSG_UNSUSPENDED = 7;
+        private static final int MSG_SHORTCUT_CHANGED = 8;
 
         private LauncherApps.Callback mCallback;
 
@@ -492,6 +717,7 @@
             String packageName;
             boolean replacing;
             UserHandle user;
+            List<ShortcutInfo> shortcuts;
         }
 
         public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
@@ -527,6 +753,9 @@
                 case MSG_UNSUSPENDED:
                     mCallback.onPackagesUnsuspended(info.packageNames, info.user);
                     break;
+                case MSG_SHORTCUT_CHANGED:
+                    mCallback.onShortcutsChanged(info.packageName, info.shortcuts, info.user);
+                    break;
             }
         }
 
@@ -582,5 +811,14 @@
             info.user = user;
             obtainMessage(MSG_UNSUSPENDED, info).sendToTarget();
         }
+
+        public void postOnShortcutChanged(String packageName, UserHandle user,
+                List<ShortcutInfo> shortcuts) {
+            CallbackInfo info = new CallbackInfo();
+            info.packageName = packageName;
+            info.user = user;
+            info.shortcuts = shortcuts;
+            obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget();
+        }
     }
 }
diff --git a/core/java/android/content/pm/ShortcutInfo.aidl b/core/java/android/content/pm/ShortcutInfo.aidl
new file mode 100644
index 0000000..08e1873
--- /dev/null
+++ b/core/java/android/content/pm/ShortcutInfo.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+parcelable ShortcutInfo;
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
new file mode 100644
index 0000000..6520563
--- /dev/null
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -0,0 +1,680 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * TODO Enhance javadoc
+ *
+ * Represents a shortcut form an application.
+ *
+ * Notes...
+ * - If an {@link Icon} is of a resource, then we'll just persist the package name and resource ID.
+ *
+ *   Otherwise, the bitmap will be fetched when it's registered to ShortcutManager, then *shrunk*
+ *   if necessary, and persisted.
+ *
+ *   We will disallow byte[] icons, because they can easily go over binder size limit.
+ *
+ * TODO Move save/load to this class
+ */
+public class ShortcutInfo implements Parcelable {
+    /* @hide */
+    public static final int FLAG_DYNAMIC = 1 << 0;
+
+    /* @hide */
+    public static final int FLAG_PINNED = 1 << 1;
+
+    /* @hide */
+    public static final int FLAG_HAS_ICON_RES = 1 << 2;
+
+    /* @hide */
+    public static final int FLAG_HAS_ICON_FILE = 1 << 3;
+
+    /** @hide */
+    @IntDef(flag = true,
+            value = {
+            FLAG_DYNAMIC,
+            FLAG_PINNED,
+            FLAG_HAS_ICON_RES,
+            FLAG_HAS_ICON_FILE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ShortcutFlags {}
+
+    // Cloning options.
+
+    /* @hide */
+    private static final int CLONE_REMOVE_ICON = 1 << 0;
+
+    /* @hide */
+    private static final int CLONE_REMOVE_INTENT = 1 << 1;
+
+    /* @hide */
+    public static final int CLONE_REMOVE_NON_KEY_INFO = 1 << 2;
+
+    /* @hide */
+    public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON;
+
+    /* @hide */
+    public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT;
+
+    /** @hide */
+    @IntDef(flag = true,
+            value = {
+                    CLONE_REMOVE_ICON,
+                    CLONE_REMOVE_INTENT,
+                    CLONE_REMOVE_NON_KEY_INFO,
+                    CLONE_REMOVE_FOR_CREATOR,
+                    CLONE_REMOVE_FOR_LAUNCHER
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CloneFlags {}
+
+    private final String mId;
+
+    @NonNull
+    private final String mPackageName;
+
+    @Nullable
+    private ComponentName mActivityComponent;
+
+    @Nullable
+    private Icon mIcon;
+
+    @NonNull
+    private String mTitle;
+
+    @NonNull
+    private Intent mIntent;
+
+    // Internal use only.
+    @NonNull
+    private PersistableBundle mIntentPersistableExtras;
+
+    private int mWeight;
+
+    @Nullable
+    private PersistableBundle mExtras;
+
+    private long mLastChangedTimestamp;
+
+    // Internal use only.
+    @ShortcutFlags
+    private int mFlags;
+
+    // Internal use only.
+    private int mIconResourceId;
+
+    // Internal use only.
+    @Nullable
+    private String mBitmapPath;
+
+    private ShortcutInfo(Builder b) {
+        mId = Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided");
+
+        // Note we can't do other null checks here because SM.updateShortcuts() takes partial
+        // information.
+        mPackageName = b.mContext.getPackageName();
+        mActivityComponent = b.mActivityComponent;
+        mIcon = b.mIcon;
+        mTitle = b.mTitle;
+        mIntent = b.mIntent;
+        mWeight = b.mWeight;
+        mExtras = b.mExtras;
+        updateTimestamp();
+    }
+
+    /**
+     * Throws if any of the mandatory fields is not set.
+     *
+     * @hide
+     */
+    public void enforceMandatoryFields() {
+        Preconditions.checkStringNotEmpty(mTitle, "Shortcut title must be provided");
+        Preconditions.checkNotNull(mIntent, "Shortcut Intent must be provided");
+    }
+
+    /**
+     * Copy constructor.
+     */
+    private ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags) {
+        mId = source.mId;
+        mPackageName = source.mPackageName;
+        mActivityComponent = source.mActivityComponent;
+        mFlags = source.mFlags;
+        mLastChangedTimestamp = source.mLastChangedTimestamp;
+
+        if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
+            if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
+                mIcon = source.mIcon;
+            }
+
+            mTitle = source.mTitle;
+            if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
+                mIntent = source.mIntent;
+                mIntentPersistableExtras = source.mIntentPersistableExtras;
+            }
+            mWeight = source.mWeight;
+            mExtras = source.mExtras;
+            mIconResourceId = source.mIconResourceId;
+            mBitmapPath = source.mBitmapPath;
+        }
+    }
+
+    /**
+     * Copy a {@link ShortcutInfo}, optionally removing fields.
+     * @hide
+     */
+    public ShortcutInfo clone(@CloneFlags int cloneFlags) {
+        return new ShortcutInfo(this, cloneFlags);
+    }
+
+    /**
+     * Copy non-null/zero fields from another {@link ShortcutInfo}.  Only "public" information
+     * will be overwritten.  The timestamp will be updated.
+     *
+     * - Flags will not change
+     * - mBitmapPath will not change
+     * - Current time will be set to timestamp
+     *
+     * @hide
+     */
+    public void copyNonNullFieldsFrom(ShortcutInfo source) {
+        Preconditions.checkState(mId == source.mId, "ID must match");
+        Preconditions.checkState(mPackageName.equals(source.mPackageName),
+                "Package namae must match");
+
+        if (source.mActivityComponent != null) {
+            mActivityComponent = source.mActivityComponent;
+        }
+
+        if (source.mIcon != null) {
+            mIcon = source.mIcon;
+        }
+        if (source.mTitle != null) {
+            mTitle = source.mTitle;
+        }
+        if (source.mIntent != null) {
+            mIntent = source.mIntent;
+            mIntentPersistableExtras = source.mIntentPersistableExtras;
+        }
+        if (source.mWeight != 0) {
+            mWeight = source.mWeight;
+        }
+        if (source.mExtras != null) {
+            mExtras = source.mExtras;
+        }
+
+        updateTimestamp();
+    }
+
+    /**
+     * Builder class for {@link ShortcutInfo} objects.
+     */
+    public static class Builder {
+        private final Context mContext;
+
+        private String mId;
+
+        private ComponentName mActivityComponent;
+
+        private Icon mIcon;
+
+        private String mTitle;
+
+        private Intent mIntent;
+
+        private int mWeight;
+
+        private PersistableBundle mExtras;
+
+        /** Constructor. */
+        public Builder(Context context) {
+            mContext = context;
+        }
+
+        /**
+         * Sets the ID of the shortcut.  This is a mandatory field.
+         */
+        @NonNull
+        public Builder setId(@NonNull String id) {
+            mId = Preconditions.checkStringNotEmpty(id, "id");
+            return this;
+        }
+
+        /**
+         * Optionally sets the target activity.
+         */
+        @NonNull
+        public Builder setActivityComponent(@NonNull ComponentName activityComponent) {
+            mActivityComponent = Preconditions.checkNotNull(activityComponent, "activityComponent");
+            return this;
+        }
+
+        /**
+         * Optionally sets an icon.
+         *
+         * - Tint is not supported TODO Either check and throw, or support it.
+         * - URI icons will be converted into Bitmap icons at the registration time.
+         *
+         * TODO Only allow Bitmap, Resource and URI types.  byte[] type can easily go over
+         * binder size limit.
+         */
+        @NonNull
+        public Builder setIcon(Icon icon) {
+            mIcon = icon;
+            return this;
+        }
+
+        /**
+         * Sets the title of a shortcut.  This is a mandatory field.
+         */
+        @NonNull
+        public Builder setTitle(@NonNull String title) {
+            mTitle = Preconditions.checkStringNotEmpty(title, "title");
+            return this;
+        }
+
+        /**
+         * Sets the intent of a shortcut.  This is a mandatory field.  The extras must only contain
+         * persistable information.  (See {@link PersistableBundle}).
+         */
+        @NonNull
+        public Builder setIntent(@NonNull Intent intent) {
+            mIntent = Preconditions.checkNotNull(intent, "intent");
+            return this;
+        }
+
+        /**
+         * Optionally sets the weight of a shortcut, which will be used by Launcher for sorting.
+         * The larger the weight, the more "important" a shortcut is.
+         */
+        @NonNull
+        public Builder setWeight(int weight) {
+            mWeight = weight;
+            return this;
+        }
+
+        /**
+         * Optional values that application can set.
+         * TODO: reserve keys starting with "android."
+         */
+        @NonNull
+        public Builder setExtras(@NonNull PersistableBundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Creates a {@link ShortcutInfo} instance.
+         */
+        @NonNull
+        public ShortcutInfo build() {
+            return new ShortcutInfo(this);
+        }
+    }
+
+    /**
+     * Return the ID of the shortcut.
+     */
+    @NonNull
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Return the ID of the shortcut.
+     */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Return the target activity, which may be null, in which case the shortcut is not associated
+     * with a specific activity.
+     */
+    @Nullable
+    public ComponentName getActivityComponent() {
+        return mActivityComponent;
+    }
+
+    /**
+     * Icon.
+     *
+     * For performance reasons, this will <b>NOT</b> be available when an instance is returned
+     * by {@link ShortcutManager} or {@link LauncherApps}.  A launcher application needs to use
+     * other APIs in LauncherApps to fetch the bitmap.  TODO Add a precondition for it.
+     *
+     * @hide
+     */
+    @Nullable
+    public Icon getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Return the shortcut title.
+     */
+    @NonNull
+    public String getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Return the intent.
+     * TODO Set mIntentPersistableExtras and before returning.
+     */
+    @NonNull
+    public Intent getIntent() {
+        return mIntent;
+    }
+
+    /** @hide */
+    @Nullable
+    public PersistableBundle getIntentPersistableExtras() {
+        return mIntentPersistableExtras;
+    }
+
+    /**
+     * Return the weight of a shortcut, which will be used by Launcher for sorting.
+     * The larger the weight, the more "important" a shortcut is.
+     */
+    public int getWeight() {
+        return mWeight;
+    }
+
+    /**
+     * Optional values that application can set.
+     */
+    @Nullable
+    public PersistableBundle getExtras() {
+        return mExtras;
+    }
+
+    /**
+     * Last time when any of the fields was updated.
+     */
+    public long getLastChangedTimestamp() {
+        return mLastChangedTimestamp;
+    }
+
+    /** @hide */
+    @ShortcutFlags
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /** @hide*/
+    public void setFlags(@ShortcutFlags int flags) {
+        mFlags = flags;
+    }
+
+    /** @hide*/
+    public void addFlags(@ShortcutFlags int flags) {
+        mFlags |= flags;
+    }
+
+    /** @hide*/
+    public void clearFlags(@ShortcutFlags int flags) {
+        mFlags &= ~flags;
+    }
+
+    /** @hide*/
+    public boolean hasFlags(@ShortcutFlags int flags) {
+        return (mFlags & flags) == flags;
+    }
+
+    /** Return whether a shortcut is dynamic. */
+    public boolean isDynamic() {
+        return hasFlags(FLAG_DYNAMIC);
+    }
+
+    /** Return whether a shortcut is pinned. */
+    public boolean isPinned() {
+        return hasFlags(FLAG_PINNED);
+    }
+
+    /**
+     * Return whether a shortcut's icon is a resource in the owning package.
+     *
+     * @see LauncherApps#getShortcutIconResId(ShortcutInfo, UserHandle)
+     */
+    public boolean hasIconResource() {
+        return hasFlags(FLAG_HAS_ICON_RES);
+    }
+
+    /**
+     * Return whether a shortcut's icon is stored as a file.
+     *
+     * @see LauncherApps#getShortcutIconFd(ShortcutInfo, UserHandle)
+     */
+    public boolean hasIconFile() {
+        return hasFlags(FLAG_HAS_ICON_FILE);
+    }
+
+    /** @hide */
+    public void updateTimestamp() {
+        mLastChangedTimestamp = System.currentTimeMillis();
+    }
+
+    /** @hide */
+    // VisibleForTesting
+    public void setTimestamp(long value) {
+        mLastChangedTimestamp = value;
+    }
+
+    /** @hide */
+    public void setIcon(Icon icon) {
+        mIcon = icon;
+    }
+
+    /** @hide */
+    public void setTitle(String title) {
+        mTitle = title;
+    }
+
+    /** @hide */
+    public void setIntent(Intent intent) {
+        mIntent = intent;
+    }
+
+    /** @hide */
+    public void setIntentPersistableExtras(PersistableBundle intentPersistableExtras) {
+        mIntentPersistableExtras = intentPersistableExtras;
+    }
+
+    /** @hide */
+    public void setWeight(int weight) {
+        mWeight = weight;
+    }
+
+    /** @hide */
+    public void setExtras(PersistableBundle extras) {
+        mExtras = extras;
+    }
+
+    /** @hide */
+    public int getIconResourceId() {
+        return mIconResourceId;
+    }
+
+    /** @hide */
+    public String getBitmapPath() {
+        return mBitmapPath;
+    }
+
+    /** @hide */
+    public void setBitmapPath(String bitmapPath) {
+        mBitmapPath = bitmapPath;
+    }
+
+    private ShortcutInfo(Parcel source) {
+        final ClassLoader cl = getClass().getClassLoader();
+
+        mId = source.readString();
+        mPackageName = source.readString();
+        mActivityComponent = source.readParcelable(cl);
+        mIcon = source.readParcelable(cl);
+        mTitle = source.readString();
+        mIntent = source.readParcelable(cl);
+        mIntentPersistableExtras = source.readParcelable(cl);
+        mWeight = source.readInt();
+        mExtras = source.readParcelable(cl);
+        mLastChangedTimestamp = source.readLong();
+        mFlags = source.readInt();
+        mIconResourceId = source.readInt();
+        mBitmapPath = source.readString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mId);
+        dest.writeString(mPackageName);
+        dest.writeParcelable(mActivityComponent, flags);
+        dest.writeParcelable(mIcon, flags);
+        dest.writeString(mTitle);
+        dest.writeParcelable(mIntent, flags);
+        dest.writeParcelable(mIntentPersistableExtras, flags);
+        dest.writeInt(mWeight);
+        dest.writeParcelable(mExtras, flags);
+        dest.writeLong(mLastChangedTimestamp);
+        dest.writeInt(mFlags);
+        dest.writeInt(mIconResourceId);
+        dest.writeString(mBitmapPath);
+    }
+
+    public static final Creator<ShortcutInfo> CREATOR =
+            new Creator<ShortcutInfo>() {
+                public ShortcutInfo createFromParcel(Parcel source) {
+                    return new ShortcutInfo(source);
+                }
+                public ShortcutInfo[] newArray(int size) {
+                    return new ShortcutInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Return a string representation, intended for logging.  Some fields will be retracted.
+     */
+    @Override
+    public String toString() {
+        return toStringInner(/* secure =*/ true, /* includeInternalData =*/ false);
+    }
+
+    /** @hide */
+    public String toInsecureString() {
+        return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true);
+    }
+
+    private String toStringInner(boolean secure, boolean includeInternalData) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("ShortcutInfo {");
+
+        sb.append("id=");
+        sb.append(secure ? "***" : mId);
+
+        sb.append(", packageName=");
+        sb.append(mPackageName);
+
+        if (isDynamic()) {
+            sb.append(", dynamic");
+        }
+        if (isPinned()) {
+            sb.append(", pinned");
+        }
+
+        sb.append(", activity=");
+        sb.append(mActivityComponent);
+
+        sb.append(", title=");
+        sb.append(secure ? "***" : mTitle);
+
+        sb.append(", icon=");
+        sb.append(mIcon);
+
+        sb.append(", weight=");
+        sb.append(mWeight);
+
+        sb.append(", timestamp=");
+        sb.append(mLastChangedTimestamp);
+
+        sb.append(", intent=");
+        sb.append(mIntent);
+
+        sb.append(", intentExtras=");
+        sb.append(secure ? "***" : mIntentPersistableExtras);
+
+        sb.append(", extras=");
+        sb.append(mExtras);
+
+        if (includeInternalData) {
+            sb.append(", flags=");
+            sb.append(mFlags);
+
+            sb.append(", iconRes=");
+            sb.append(mIconResourceId);
+
+            sb.append(", bitmapPath=");
+            sb.append(mBitmapPath);
+        }
+
+        sb.append("}");
+        return sb.toString();
+    }
+
+    /** @hide */
+    public ShortcutInfo(String id, String packageName, ComponentName activityComponent,
+            Icon icon, String title, Intent intent, PersistableBundle intentPersistableExtras,
+            int weight, PersistableBundle extras, long lastChangedTimestamp,
+            int flags, int iconResId, String bitmapPath) {
+        mId = id;
+        mPackageName = packageName;
+        mActivityComponent = activityComponent;
+        mIcon = icon;
+        mTitle = title;
+        mIntent = intent;
+        mIntentPersistableExtras = intentPersistableExtras;
+        mWeight = weight;
+        mExtras = extras;
+        mLastChangedTimestamp = lastChangedTimestamp;
+        mFlags = flags;
+        mIconResourceId = iconResId;
+        mBitmapPath = bitmapPath;
+    }
+}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
new file mode 100644
index 0000000..4c51d49
--- /dev/null
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * TODO Enhance javadoc
+ *
+ * {@link ShortcutManager} manages shortcuts created by applications.
+ *
+ * <h3>Dynamic shortcuts and pinned shortcuts</h3>
+ *
+ * An application can publish shortcuts with {@link #setDynamicShortcuts(List)} and
+ * {@link #addDynamicShortcut(ShortcutInfo)}.  There can be at most
+ * {@link #getMaxDynamicShortcutCount()} number of dynamic shortcuts at a time from the same
+ * application.
+ * A dynamic shortcut can be deleted with {@link #deleteDynamicShortcut(String)}, and apps
+ * can also use {@link #deleteAllDynamicShortcuts()} to delete all dynamic shortcuts.
+ *
+ * <p>The shortcuts that are currently published by the above APIs are called "dynamic", because
+ * they can be removed by the creator application at any time.  The user may "pin" dynamic shortcuts
+ * on Launcher to make "pinned" shortcuts.  Pinned shortcuts <b>cannot</b> be removed by the creator
+ * app.  An application can obtain all pinned shortcuts from itself with
+ * {@link #getPinnedShortcuts()}.  Applications should keep the pinned shortcut information
+ * up-to-date using {@link #updateShortcuts(List)}.
+ *
+ * <p>The number of pinned shortcuts does not affect the number of dynamic shortcuts that can be
+ * published by an application at a time.
+ * No matter how many pinned shortcuts that Launcher has for an application, the
+ * application can still always publish {@link #getMaxDynamicShortcutCount()} number of dynamic
+ * shortcuts.
+ *
+ * <h3>Shortcut IDs</h3>
+ *
+ * Each shortcut must have an ID, which must be unique within each application.  When a shortcut is
+ * published, existing shortcuts with the same ID will be updated.  Note this may include a
+ * pinned shortcut.
+ *
+ * <h3>Rate limiting</h3>
+ *
+ * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcut(ShortcutInfo)},
+ * and {@link #updateShortcuts(List)} will be
+ * rate-limited.  An application can call these methods at most
+ * {@link #getRemainingCallCount()} times until the rate-limiting counter is reset,
+ * which happens at a certain time every day.
+ *
+ * <p>An applications can use {@link #getRateLimitResetTime()} to get the next reset time.
+ *
+ * <h3>Backup and Restore</h3>
+ *
+ * Shortcuts will be backed up and restored across devices.  This means all information, including
+ * IDs, must be meaningful on a different device.
+ *
+ * TODO: Define a Broadcast to let apps update shortcuts on a restored device.
+ *
+ * <h3>APIs for launcher</h3>
+ *
+ * Launcher applications should use {@link LauncherApps} to get shortcuts that are published from
+ * applications.  Launcher applications can also pin shortcuts with
+ * {@link LauncherApps#pinShortcuts(String, List, UserHandle)}.
+ */
+public class ShortcutManager {
+    private static final String TAG = "ShortcutManager";
+
+    private final Context mContext;
+    private final IShortcutService mService;
+
+    /**
+     * @hide
+     */
+    public ShortcutManager(Context context, IShortcutService service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Publish a list of shortcuts.  All existing dynamic shortcuts from the caller application
+     * will be replaced.
+     *
+     * <p>This API will be rate-limited.
+     *
+     * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
+     *
+     * @throws IllegalArgumentException if {@code shortcutInfoList} contains more than
+     * {@link #getMaxDynamicShortcutCount()} shortcuts.
+     */
+    public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
+        try {
+            return mService.setDynamicShortcuts(mContext.getPackageName(),
+                    new ParceledListSlice(shortcutInfoList), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return all dynamic shortcuts from the caller application.  The number of result items
+     * will not exceed the value returned by {@link #getMaxDynamicShortcutCount()}.
+     */
+    @NonNull
+    public List<ShortcutInfo> getDynamicShortcuts() {
+        try {
+            return mService.getDynamicShortcuts(mContext.getPackageName(), injectMyUserId())
+                    .getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Publish a single dynamic shortcut.  If there's already dynamic or pinned shortcuts with
+     * the same ID, they will all be updated.
+     *
+     * <p>This API will be rate-limited.
+     *
+     * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
+     *
+     * @throws IllegalArgumentException if the caller application has already published the
+     * max number of dynamic shortcuts.
+     */
+    public boolean addDynamicShortcut(@NonNull ShortcutInfo shortcutInfo) {
+        try {
+            return mService.addDynamicShortcut(
+                    mContext.getPackageName(), shortcutInfo, injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Delete a single dynamic shortcut by ID.
+     */
+    public void deleteDynamicShortcut(@NonNull String shortcutId) {
+        try {
+            mService.deleteDynamicShortcut(mContext.getPackageName(), shortcutId, injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Delete all dynamic shortcuts from the caller application.
+     */
+    public void deleteAllDynamicShortcuts() {
+        try {
+            mService.deleteAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return all pinned shortcuts from the caller application.
+     */
+    @NonNull
+    public List<ShortcutInfo> getPinnedShortcuts() {
+        try {
+            return mService.getPinnedShortcuts(mContext.getPackageName(), injectMyUserId())
+                    .getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Update all existing shortcuts with the same IDs.  Shortcuts may be pinned and/or dynamic.
+     *
+     * <p>This API will be rate-limited.
+     *
+     * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
+     */
+    public boolean updateShortcuts(List<ShortcutInfo> shortcutInfoList) {
+        try {
+            return mService.updateShortcuts(mContext.getPackageName(),
+                    new ParceledListSlice(shortcutInfoList), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the max number of dynamic shortcuts that each application can have at a time.
+     */
+    public int getMaxDynamicShortcutCount() {
+        try {
+            return mService.getMaxDynamicShortcutCount(mContext.getPackageName(), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return the number of times the caller application can call the rate-limited APIs
+     * before the rate limit counter is reset.
+     *
+     * @see #getRateLimitResetTime()
+     */
+    public int getRemainingCallCount() {
+        try {
+            return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Return when the rate limit count will be reset next time, in milliseconds since the epoch.
+     *
+     * @see #getRemainingCallCount()
+     * @see System#currentTimeMillis()
+     */
+    public long getRateLimitResetTime() {
+        try {
+            return mService.getRateLimitResetTime(mContext.getPackageName(), injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide injection point */
+    @VisibleForTesting
+    protected int injectMyUserId() {
+        return UserHandle.myUserId();
+    }
+}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
new file mode 100644
index 0000000..8055dd9
--- /dev/null
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.LauncherApps.ShortcutQuery;
+
+import java.util.List;
+
+/**
+ * Entry points used by {@link LauncherApps}.
+ *
+ * <p>No permission / argument checks will be performed inside.
+ * Callers must check the calling app permission and the calling package name.
+ * @hide
+ */
+public abstract class ShortcutServiceInternal {
+    public interface ShortcutChangeListener {
+        void onShortcutChanged(@NonNull String packageName,
+                @NonNull List<ShortcutInfo> shortcuts, @UserIdInt int userId);
+    }
+
+    public abstract List<ShortcutInfo>
+            getShortcuts(@NonNull String callingPackage, long changedSince,
+            @Nullable String packageName, @Nullable ComponentName componentName,
+            @ShortcutQuery.QueryFlags int flags,
+            int userId);
+
+    public abstract List<ShortcutInfo>
+            getShortcutInfo(@NonNull String callingPackage,
+            @NonNull String packageName, @Nullable List<String> ids, int userId);
+
+    public abstract void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
+            @NonNull List<String> shortcutIds, int userId);
+
+    public abstract Intent createShortcutIntent(@NonNull String callingPackage,
+            @NonNull ShortcutInfo shortcut, int userId);
+
+    public abstract void addListener(@NonNull ShortcutChangeListener listener);
+}
diff --git a/core/java/android/content/res/GradientColor.java b/core/java/android/content/res/GradientColor.java
index 98ef2ea..cc46cbd 100644
--- a/core/java/android/content/res/GradientColor.java
+++ b/core/java/android/content/res/GradientColor.java
@@ -17,6 +17,7 @@
 package android.content.res;
 
 import android.annotation.ColorInt;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.Resources.Theme;
@@ -37,13 +38,48 @@
 import android.util.Xml;
 
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
-
+/**
+ * Lets you define a gradient color, which is used inside
+ * {@link android.graphics.drawable.VectorDrawable}.
+ *
+ * {@link android.content.res.GradientColor}s are created from XML resource files defined in the
+ * "color" subdirectory directory of an application's resource directory.  The XML file contains
+ * a single "gradient" element with a number of attributes and elements inside.  For example:
+ * <pre>
+ * &lt;gradient xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ *   &lt;android:startColor="?android:attr/colorPrimary"/&gt;
+ *   &lt;android:endColor="?android:attr/colorControlActivated"/&gt;
+ *   &lt;.../&gt;
+ *   &lt;android:type="linear"/&gt;
+ * &lt;/gradient&gt;
+ * </pre>
+ *
+ * This can describe either a {@link android.graphics.LinearGradient},
+ * {@link android.graphics.RadialGradient}, or {@link android.graphics.SweepGradient}.
+ *
+ * Note that different attributes are relevant for different types of gradient.
+ * For example, android:gradientRadius is only applied to RadialGradient.
+ * androd:centerX and android:centerY are only applied to SweepGradient or RadialGradient.
+ * android:startX, android:startY, android:endX and android:endY are only applied to LinearGradient.
+ *
+ * Also note if any color "item" element is defined, then startColor, centerColor and endColor will
+ * be ignored.
+ */
 public class GradientColor extends ComplexColor {
     private static final String TAG = "GradientColor";
 
     private static final boolean DBG_GRADIENT = false;
 
+    @IntDef({TILE_MODE_CLAMP, TILE_MODE_REPEAT, TILE_MODE_MIRROR})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface GradientTileMode {}
+    private static final int TILE_MODE_CLAMP = 0;
+    private static final int TILE_MODE_REPEAT = 1;
+    private static final int TILE_MODE_MIRROR = 2;
+
     /** Lazily-created factory for this GradientColor. */
     private GradientColorFactory mFactory;
 
@@ -54,7 +90,8 @@
     // all the XML information.
     private Shader mShader = null;
 
-    // Below are the attributes at the root element <gradient>
+    // Below are the attributes at the root element <gradient>.
+    // NOTE: they need to be copied in the copy constructor!
     private int mGradientType = GradientDrawable.LINEAR_GRADIENT;
 
     private float mCenterX = 0f;
@@ -70,6 +107,8 @@
     private int mEndColor = 0;
     private boolean mHasCenterColor = false;
 
+    private int mTileMode = 0; // Clamp mode.
+
     private float mGradientRadius = 0f;
 
     // Below are the attributes for the <item> element.
@@ -100,6 +139,7 @@
             mEndColor = copy.mEndColor;
             mHasCenterColor = copy.mHasCenterColor;
             mGradientRadius = copy.mGradientRadius;
+            mTileMode = copy.mTileMode;
 
             if (copy.mItemColors != null) {
                 mItemColors = copy.mItemColors.clone();
@@ -117,6 +157,20 @@
         }
     }
 
+    // Set the default to clamp mode.
+    private static Shader.TileMode parseTileMode(@GradientTileMode int tileMode) {
+        switch (tileMode) {
+            case TILE_MODE_CLAMP:
+                return Shader.TileMode.CLAMP;
+            case TILE_MODE_REPEAT:
+                return Shader.TileMode.REPEAT;
+            case TILE_MODE_MIRROR:
+                return Shader.TileMode.MIRROR;
+            default:
+                return Shader.TileMode.CLAMP;
+        }
+    }
+
     /**
      * Update the root level's attributes, either for inflate or applyTheme.
      */
@@ -150,6 +204,9 @@
         mEndColor = a.getColor(
                 R.styleable.GradientColor_endColor, mEndColor);
 
+        mTileMode = a.getInt(
+                R.styleable.GradientColor_tileMode, mTileMode);
+
         if (DBG_GRADIENT) {
             Log.v(TAG, "hasCenterColor is " + mHasCenterColor);
             if (mHasCenterColor) {
@@ -157,6 +214,7 @@
             }
             Log.v(TAG, "startColor: " + mStartColor);
             Log.v(TAG, "endColor: " + mEndColor);
+            Log.v(TAG, "tileMode: " + mTileMode);
         }
 
         mGradientRadius = a.getFloat(R.styleable.GradientColor_gradientRadius,
@@ -406,11 +464,11 @@
 
         if (mGradientType == GradientDrawable.LINEAR_GRADIENT) {
             mShader = new LinearGradient(mStartX, mStartY, mEndX, mEndY, tempColors, tempOffsets,
-                    Shader.TileMode.CLAMP);
+                    parseTileMode(mTileMode));
         } else {
             if (mGradientType == GradientDrawable.RADIAL_GRADIENT) {
                 mShader = new RadialGradient(mCenterX, mCenterY, mGradientRadius, tempColors,
-                        tempOffsets, Shader.TileMode.CLAMP);
+                        tempOffsets, parseTileMode(mTileMode));
             } else {
                 mShader = new SweepGradient(mCenterX, mCenterY, tempColors, tempOffsets);
             }
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index ee7bd9a..5c71373 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -235,6 +235,12 @@
         return mParcelledData != null;
     }
 
+    /** @hide */
+    ArrayMap<String, Object> getMap() {
+        unparcel();
+        return mMap;
+    }
+
     /**
      * Returns the number of mappings contained in this Bundle.
      *
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index ea180b2..f36bb29 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -24,9 +24,6 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
 
 /**
  * A mapping from String values to various types that can be saved to persistent and later
@@ -82,6 +79,18 @@
         super(b);
     }
 
+
+    /**
+     * Constructs a PersistableBundle from a Bundle.
+     *
+     * @param b a Bundle to be copied.
+     *
+     * @throws IllegalArgumentException if any element of {@code b} cannot be persisted.
+     */
+    public PersistableBundle(Bundle b) {
+        this(b.getMap());
+    }
+
     /**
      * Constructs a PersistableBundle containing the mappings passed in.
      *
@@ -101,6 +110,8 @@
             if (value instanceof ArrayMap) {
                 // Fix up any Maps by replacing them with PersistableBundles.
                 mMap.setValueAt(i, new PersistableBundle((ArrayMap<String, Object>) value));
+            } else if (value instanceof Bundle) {
+                mMap.setValueAt(i, new PersistableBundle(((Bundle) value)));
             } else if (!isValidType(value)) {
                 throw new IllegalArgumentException("Bad value in PersistableBundle key="
                         + mMap.keyAt(i) + " value=" + value);
diff --git a/core/java/android/preference/EditTextPreference.java b/core/java/android/preference/EditTextPreference.java
index ff37042..9467c22 100644
--- a/core/java/android/preference/EditTextPreference.java
+++ b/core/java/android/preference/EditTextPreference.java
@@ -49,6 +49,7 @@
     private EditText mEditText;
     
     private String mText;
+    private boolean mTextSet;
 
     public EditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
@@ -85,15 +86,16 @@
      * @param text The text to save
      */
     public void setText(String text) {
-        final boolean wasBlocking = shouldDisableDependents();
-        
-        mText = text;
-        
-        persistString(text);
-        
-        final boolean isBlocking = shouldDisableDependents(); 
-        if (isBlocking != wasBlocking) {
-            notifyDependencyChange(isBlocking);
+        // Always persist/notify the first time.
+        final boolean changed = !TextUtils.equals(mText, text);
+        if (changed || !mTextSet) {
+            mText = text;
+            mTextSet = true;
+            persistString(text);
+            if(changed) {
+                notifyDependencyChange(shouldDisableDependents());
+                notifyChanged();
+            }
         }
     }
     
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4f185e6..d43ff4e 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5867,15 +5867,6 @@
         public static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled";
 
         /**
-         * Names of the service component that the current user has explicitly allowed to
-         * see and change the importance of all of the user's notifications.
-         *
-         * @hide
-         */
-        public static final String ENABLED_NOTIFICATION_ASSISTANT
-                = "enabled_notification_assistant";
-
-        /**
          * Names of the service components that the current user has explicitly allowed to
          * see all of the user's notifications, separated by ':'.
          *
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 11737c6..0163b47 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -16,32 +16,56 @@
 
 package android.service.notification;
 
+import android.annotation.IntDef;
 import android.annotation.SystemApi;
+import android.app.AutomaticZenRule;
 import android.content.Context;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
- * Condition information from condition providers. Used to tell the system to enter Do Not Disturb
- * mode and request that the system exit Do Not Disturb mode.
+ * The current condition of an {@link android.app.AutomaticZenRule}, provided by the
+ * {@link ConditionProviderService} that owns the rule. Used to tell the system to enter Do Not
+ * Disturb mode and request that the system exit Do Not Disturb mode.
  */
 public class Condition implements Parcelable {
 
+    @SystemApi
     public static final String SCHEME = "condition";
 
+    /** @hide */
+    @IntDef({STATE_FALSE, STATE_TRUE, STATE_TRUE, STATE_ERROR})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {}
+
+    /**
+     * Indicates that Do Not Disturb should be turned off. Note that all Conditions from all
+     * {@link ConditionProviderService} providers must be off for Do Not Disturb to be turned off on
+     * the device.
+     */
     public static final int STATE_FALSE = 0;
+    /**
+     * Indicates that Do Not Disturb should be turned on.
+     */
     public static final int STATE_TRUE = 1;
+
+    @SystemApi
     public static final int STATE_UNKNOWN = 2;
+    @SystemApi
     public static final int STATE_ERROR = 3;
 
+    @SystemApi
     public static final int FLAG_RELEVANT_NOW = 1 << 0;
+    @SystemApi
     public static final int FLAG_RELEVANT_ALWAYS = 1 << 1;
 
     /**
-     * The URI representing the condition being updated.
+     * The URI representing the rule being updated.
      * See {@link android.app.AutomaticZenRule#getConditionId()}.
      */
     public final Uri id;
@@ -52,23 +76,17 @@
      */
     public final String summary;
 
-    /**
-     * Additional information about what the rule encoded in {@link #id} means when it is enabled.
-     * User visible if the state of the condition is {@link #STATE_TRUE}.
-     */
+    @SystemApi
     public final String line1;
-
-    /**
-     * Additional information about what the rule encoded in {@link #id} means when it is enabled.
-     * User visible if the state of the condition is {@link #STATE_TRUE}.
-     */
+    @SystemApi
     public final String line2;
 
     /**
-     * The state of this condition. {@link #STATE_TRUE} will enable Do Not Disturb mode. Any other
-     * state will turn Do Not Disturb off for this rule. Note that Do Not Disturb might still be
-     * enabled globally if other conditions are in a {@link #STATE_TRUE} state.
+     * The state of this condition. {@link #STATE_TRUE} will enable Do Not Disturb mode.
+     * {@link #STATE_FALSE} will turn Do Not Disturb off for this rule. Note that Do Not Disturb
+     * might still be enabled globally if other conditions are in a {@link #STATE_TRUE} state.
      */
+    @State
     public final int state;
 
     @SystemApi
@@ -76,8 +94,13 @@
     @SystemApi
     public final int icon;
 
-    public Condition(Uri id, String summary, String line1, String line2, int state) {
-        this(id, summary, line1, line2, -1, state, FLAG_RELEVANT_ALWAYS);
+    /**
+     * An object representing the current state of a {@link android.app.AutomaticZenRule}.
+     * @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule
+     * @param summary a user visible description of the rule state.
+     */
+    public Condition(Uri id, String summary, int state) {
+        this(id, summary, "", "", -1, state, FLAG_RELEVANT_ALWAYS);
     }
 
     @SystemApi
@@ -85,8 +108,6 @@
             int state, int flags) {
         if (id == null) throw new IllegalArgumentException("id is required");
         if (summary == null) throw new IllegalArgumentException("summary is required");
-        if (line1 == null) throw new IllegalArgumentException("line1 is required");
-        if (line2 == null) throw new IllegalArgumentException("line2 is required");
         if (!isValidState(state)) throw new IllegalArgumentException("state is invalid: " + state);
         this.id = id;
         this.summary = summary;
@@ -97,7 +118,7 @@
         this.flags = flags;
     }
 
-    private Condition(Parcel source) {
+    public Condition(Parcel source) {
         this((Uri)source.readParcelable(Condition.class.getClassLoader()),
                 source.readString(),
                 source.readString(),
@@ -135,6 +156,7 @@
             .append(']').toString();
     }
 
+    @SystemApi
     public static String stateToString(int state) {
         if (state == STATE_FALSE) return "STATE_FALSE";
         if (state == STATE_TRUE) return "STATE_TRUE";
@@ -143,6 +165,7 @@
         throw new IllegalArgumentException("state is invalid: " + state);
     }
 
+    @SystemApi
     public static String relevanceToString(int flags) {
         final boolean now = (flags & FLAG_RELEVANT_NOW) != 0;
         final boolean always = (flags & FLAG_RELEVANT_ALWAYS) != 0;
@@ -175,6 +198,7 @@
         return 0;
     }
 
+    @SystemApi
     public Condition copy() {
         final Parcel parcel = Parcel.obtain();
         try {
@@ -186,10 +210,14 @@
         }
     }
 
+    @SystemApi
     public static Uri.Builder newId(Context context) {
-        return new Uri.Builder().scheme(SCHEME).authority(context.getPackageName());
+        return new Uri.Builder()
+                .scheme(Condition.SCHEME)
+                .authority(context.getPackageName());
     }
 
+    @SystemApi
     public static boolean isValidId(Uri id, String pkg) {
         return id != null && SCHEME.equals(id.getScheme()) && pkg.equals(id.getAuthority());
     }
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index adcc9d6..44c3887 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -102,9 +102,6 @@
      */
     abstract public void onConnected();
 
-    /**
-     * @removed
-     */
     @SystemApi
     public void onRequestConditions(int relevance) {}
 
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index a0de17f..8c35901 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -23,7 +23,7 @@
 /** @hide */
 oneway interface INotificationListener
 {
-    // listeners and assistants
+    // listeners and rankers
     void onListenerConnected(in NotificationRankingUpdate update);
     void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
             in NotificationRankingUpdate update);
@@ -33,7 +33,7 @@
     void onListenerHintsChanged(int hints);
     void onInterruptionFilterChanged(int interruptionFilter);
 
-    // assistants only
+    // rankers only
     void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user);
     void onNotificationVisibilityChanged(String key, long time, boolean visible);
     void onNotificationClick(String key, long time);
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index b8f9812..a8b8da1 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -170,13 +170,18 @@
 
     private INotificationManager mNoMan;
 
-    /** Only valid after a successful call to (@link registerAsService}. */
-    private int mCurrentUser;
+    /**
+     * Only valid after a successful call to (@link registerAsService}.
+     * @hide
+     */
+    protected int mCurrentUser;
 
-
-    // This context is required for system services since NotificationListenerService isn't
-    // started as a real Service and hence no context is available.
-    private Context mSystemContext;
+    /**
+     * This context is required for system services since NotificationListenerService isn't
+     * started as a real Service and hence no context is available..
+     * @hide
+     */
+    protected Context mSystemContext;
 
     /**
      * The {@link Intent} that must be declared as handled by the service.
@@ -675,12 +680,18 @@
     @SystemApi
     public void registerAsSystemService(Context context, ComponentName componentName,
             int currentUser) throws RemoteException {
+        registerAsSystemServiceImpl(context, componentName, currentUser, false /* asRanker */);
+    }
+
+    /** @hide */
+    protected void registerAsSystemServiceImpl(Context context, ComponentName componentName,
+            int currentUser, boolean asRanker) throws RemoteException {
         mSystemContext = context;
         if (mWrapper == null) {
             mWrapper = new NotificationListenerWrapper();
         }
         INotificationManager noMan = getNotificationInterface();
-        noMan.registerListener(mWrapper, componentName, currentUser);
+        noMan.registerListener(mWrapper, componentName, currentUser, asRanker);
         mCurrentUser = currentUser;
         mHandler = new MyHandler(context.getMainLooper());
     }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationRankerService.java
similarity index 90%
rename from core/java/android/service/notification/NotificationAssistantService.java
rename to core/java/android/service/notification/NotificationRankerService.java
index 41af837..520b4c2 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationRankerService.java
@@ -31,31 +31,20 @@
 import com.android.internal.os.SomeArgs;
 
 /**
- * A service that helps the user manage notifications by modifying the
- * relative importance of notifications.
- * <p>To extend this class, you must declare the service in your manifest file with
- * the {@link android.Manifest.permission#BIND_NOTIFICATION_ASSISTANT_SERVICE} permission
- * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
- * <pre>
- * &lt;service android:name=".NotificationAssistant"
- *          android:label="&#64;string/service_name"
- *          android:permission="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE">
- *     &lt;intent-filter>
- *         &lt;action android:name="android.service.notification.NotificationAssistantService" />
- *     &lt;/intent-filter>
- * &lt;/service></pre>
+ * A service that helps the user manage notifications. This class is only used to
+ * extend the framework service and may not be implemented by non-framework components.
  * @hide
  */
 @SystemApi
-public abstract class NotificationAssistantService extends NotificationListenerService {
-    private static final String TAG = "NotificationAssistant";
+public abstract class NotificationRankerService extends NotificationListenerService {
+    private static final String TAG = "NotificationRanker";
 
     /**
      * The {@link Intent} that must be declared as handled by the service.
      */
     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     public static final String SERVICE_INTERFACE
-            = "android.service.notification.NotificationAssistantService";
+            = "android.service.notification.NotificationRankerService";
 
     /** Notification was canceled by the status bar reporting a click. */
     public static final int REASON_DELEGATE_CLICK = 1;
@@ -130,7 +119,7 @@
     @Override
     public void registerAsSystemService(Context context, ComponentName componentName,
             int currentUser) throws RemoteException {
-        super.registerAsSystemService(context, componentName, currentUser);
+        registerAsSystemServiceImpl(context, componentName, currentUser, true /* as Ranker */);
         mHandler = new MyHandler(getContext().getMainLooper());
     }
 
@@ -143,7 +132,7 @@
     @Override
     public final IBinder onBind(Intent intent) {
         if (mWrapper == null) {
-            mWrapper = new NotificationAssistantWrapper();
+            mWrapper = new NotificationRankingServiceWrapper();
         }
         return mWrapper;
     }
@@ -216,14 +205,14 @@
     public final void adjustImportance(String key, Adjustment adjustment) {
         if (!isBound()) return;
         try {
-            getNotificationInterface().setImportanceFromAssistant(mWrapper, key,
+            getNotificationInterface().setImportanceFromRankerService(mWrapper, key,
                     adjustment.mImportance, adjustment.mExplanation);
         } catch (android.os.RemoteException ex) {
             Log.v(TAG, "Unable to contact notification manager", ex);
         }
     }
 
-    private class NotificationAssistantWrapper extends NotificationListenerWrapper {
+    private class NotificationRankingServiceWrapper extends NotificationListenerWrapper {
         @Override
         public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder,
                 int importance, boolean user) {
diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java
index 9af65e4..2645f86 100644
--- a/core/java/android/transition/Slide.java
+++ b/core/java/android/transition/Slide.java
@@ -47,6 +47,7 @@
     private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition";
     private CalculateSlide mSlideCalculator = sCalculateBottom;
     private @GravityFlag int mSlideEdge = Gravity.BOTTOM;
+    private float mSlideFraction = 1;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -56,16 +57,16 @@
     private interface CalculateSlide {
 
         /** Returns the translation value for view when it goes out of the scene */
-        float getGoneX(ViewGroup sceneRoot, View view);
+        float getGoneX(ViewGroup sceneRoot, View view, float fraction);
 
         /** Returns the translation value for view when it goes out of the scene */
-        float getGoneY(ViewGroup sceneRoot, View view);
+        float getGoneY(ViewGroup sceneRoot, View view, float fraction);
     }
 
     private static abstract class CalculateSlideHorizontal implements CalculateSlide {
 
         @Override
-        public float getGoneY(ViewGroup sceneRoot, View view) {
+        public float getGoneY(ViewGroup sceneRoot, View view, float fraction) {
             return view.getTranslationY();
         }
     }
@@ -73,27 +74,27 @@
     private static abstract class CalculateSlideVertical implements CalculateSlide {
 
         @Override
-        public float getGoneX(ViewGroup sceneRoot, View view) {
+        public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
             return view.getTranslationX();
         }
     }
 
     private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() {
         @Override
-        public float getGoneX(ViewGroup sceneRoot, View view) {
-            return view.getTranslationX() - sceneRoot.getWidth();
+        public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
+            return view.getTranslationX() - sceneRoot.getWidth() * fraction;
         }
     };
 
     private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() {
         @Override
-        public float getGoneX(ViewGroup sceneRoot, View view) {
+        public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
             final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
             final float x;
             if (isRtl) {
-                x = view.getTranslationX() + sceneRoot.getWidth();
+                x = view.getTranslationX() + sceneRoot.getWidth() * fraction;
             } else {
-                x = view.getTranslationX() - sceneRoot.getWidth();
+                x = view.getTranslationX() - sceneRoot.getWidth() * fraction;
             }
             return x;
         }
@@ -101,27 +102,27 @@
 
     private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
         @Override
-        public float getGoneY(ViewGroup sceneRoot, View view) {
-            return view.getTranslationY() - sceneRoot.getHeight();
+        public float getGoneY(ViewGroup sceneRoot, View view, float fraction) {
+            return view.getTranslationY() - sceneRoot.getHeight() * fraction;
         }
     };
 
     private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() {
         @Override
-        public float getGoneX(ViewGroup sceneRoot, View view) {
-            return view.getTranslationX() + sceneRoot.getWidth();
+        public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
+            return view.getTranslationX() + sceneRoot.getWidth() * fraction;
         }
     };
 
     private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() {
         @Override
-        public float getGoneX(ViewGroup sceneRoot, View view) {
+        public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
             final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
             final float x;
             if (isRtl) {
-                x = view.getTranslationX() - sceneRoot.getWidth();
+                x = view.getTranslationX() - sceneRoot.getWidth() * fraction;
             } else {
-                x = view.getTranslationX() + sceneRoot.getWidth();
+                x = view.getTranslationX() + sceneRoot.getWidth() * fraction;
             }
             return x;
         }
@@ -129,8 +130,8 @@
 
     private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
         @Override
-        public float getGoneY(ViewGroup sceneRoot, View view) {
-            return view.getTranslationY() + sceneRoot.getHeight();
+        public float getGoneY(ViewGroup sceneRoot, View view, float fraction) {
+            return view.getTranslationY() + sceneRoot.getHeight() * fraction;
         }
     };
 
@@ -237,8 +238,8 @@
         int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION);
         float endX = view.getTranslationX();
         float endY = view.getTranslationY();
-        float startX = mSlideCalculator.getGoneX(sceneRoot, view);
-        float startY = mSlideCalculator.getGoneY(sceneRoot, view);
+        float startX = mSlideCalculator.getGoneX(sceneRoot, view, mSlideFraction);
+        float startY = mSlideCalculator.getGoneY(sceneRoot, view, mSlideFraction);
         return TranslationAnimationCreator
                 .createAnimation(view, endValues, position[0], position[1],
                         startX, startY, endX, endY, sDecelerate, this);
@@ -253,10 +254,15 @@
         int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION);
         float startX = view.getTranslationX();
         float startY = view.getTranslationY();
-        float endX = mSlideCalculator.getGoneX(sceneRoot, view);
-        float endY = mSlideCalculator.getGoneY(sceneRoot, view);
+        float endX = mSlideCalculator.getGoneX(sceneRoot, view, mSlideFraction);
+        float endY = mSlideCalculator.getGoneY(sceneRoot, view, mSlideFraction);
         return TranslationAnimationCreator
                 .createAnimation(view, startValues, position[0], position[1],
                         startX, startY, endX, endY, sAccelerate, this);
     }
+
+    /** @hide */
+    public void setSlideFraction(float slideFraction) {
+        mSlideFraction = slideFraction;
+    }
 }
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 4afa9fe..316c7e3 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -41,12 +41,12 @@
 import android.widget.ListView;
 import android.widget.Spinner;
 
+import com.android.internal.R;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.StringTokenizer;
 
-import com.android.internal.R;
-
 /**
  * A Transition holds information about animations that will be run on its
  * targets during a scene change. Subclasses of this abstract class may
@@ -192,7 +192,7 @@
     private TransitionValuesMaps mStartValues = new TransitionValuesMaps();
     private TransitionValuesMaps mEndValues = new TransitionValuesMaps();
     TransitionSet mParent = null;
-    private int[] mMatchOrder = DEFAULT_MATCH_ORDER;
+    int[] mMatchOrder = DEFAULT_MATCH_ORDER;
     ArrayList<TransitionValues> mStartValuesList; // only valid after playTransition starts
     ArrayList<TransitionValues> mEndValuesList; // only valid after playTransitions starts
 
@@ -246,7 +246,7 @@
 
     // The function used to interpolate along two-dimensional points. Typically used
     // for adding curves to x/y View motion.
-    private PathMotion mPathMotion = STRAIGHT_PATH_MOTION;
+    PathMotion mPathMotion = STRAIGHT_PATH_MOTION;
 
     /**
      * Constructs a Transition object with no target objects. A transition with
@@ -780,7 +780,7 @@
                 }
             }
         }
-        if (minStartDelay != 0) {
+        if (startDelays.size() != 0) {
             for (int i = 0; i < startDelays.size(); i++) {
                 int index = startDelays.keyAt(i);
                 Animator animator = mAnimators.get(index);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index afe2f10..f7405e2 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1078,8 +1078,7 @@
                 scheduleTraversals();
             } else {
                 if (mAttachInfo.mHardwareRenderer != null) {
-                    // TODO: Temporary to help track down b/27286867
-                    Log.d(mTag, "WindowStopped on " + getTitle());
+                    if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle());
                     mAttachInfo.mHardwareRenderer.updateSurface(null);
                     mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView);
                 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0e34067..406dc2b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -215,7 +215,7 @@
 
     boolean mInBatchEditControllers;
     boolean mShowSoftInputOnFocus = true;
-    private boolean mPreserveDetachedSelection;
+    private boolean mPreserveSelection;
     boolean mTemporaryDetach;
 
     boolean mIsBeingLongClicked;
@@ -351,9 +351,14 @@
     }
 
     void replace() {
+        if (mSuggestionsPopupWindow == null) {
+            mSuggestionsPopupWindow = new SuggestionsPopupWindow();
+        }
+        hideCursorAndSpanControllers();
+        mSuggestionsPopupWindow.show();
+
         int middle = (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2;
         Selection.setSelection((Spannable) mTextView.getText(), middle);
-        showSuggestions();
     }
 
     void onAttachedToWindow() {
@@ -376,13 +381,7 @@
         updateSpellCheckSpans(0, mTextView.getText().length(),
                 true /* create the spell checker if needed */);
 
-        if (mTextView.hasTransientState() &&
-                mTextView.getSelectionStart() != mTextView.getSelectionEnd()) {
-            // Since transient state is reference counted make sure it stays matched
-            // with our own calls to it for managing selection.
-            // The action mode callback will set this back again when/if the action mode starts.
-            mTextView.setHasTransientState(false);
-
+        if (mTextView.getSelectionStart() != mTextView.getSelectionEnd()) {
             // We had an active selection from before, start the selection mode.
             startSelectionActionMode();
         }
@@ -2092,7 +2091,7 @@
 
                     mShowSuggestionRunnable = new Runnable() {
                         public void run() {
-                            showSuggestions();
+                            replace();
                         }
                     };
                     // removeCallbacks is performed on every touch
@@ -2113,9 +2112,9 @@
     }
 
     private void stopTextActionModeWithPreservingSelection() {
-        mPreserveDetachedSelection = true;
+        mPreserveSelection = true;
         stopTextActionMode();
-        mPreserveDetachedSelection = false;
+        mPreserveSelection = false;
     }
 
     /**
@@ -2233,15 +2232,6 @@
         mCorrectionHighlighter.highlight(info);
     }
 
-    void showSuggestions() {
-        if (mSuggestionsPopupWindow == null) {
-            mSuggestionsPopupWindow = new SuggestionsPopupWindow();
-        }
-        hideCursorAndSpanControllers();
-        stopTextActionMode();
-        mSuggestionsPopupWindow.show();
-    }
-
     void onScrollChanged() {
         if (mPositionListener != null) {
             mPositionListener.onScrollChanged();
@@ -2515,7 +2505,7 @@
                 .setEnabled(mTextView.canSelectAllText())
                 .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
 
-        mPreserveDetachedSelection = true;
+        mPreserveSelection = true;
     }
 
     private void replaceWithSuggestion(final SuggestionInfo suggestionInfo) {
@@ -3426,7 +3416,7 @@
 
         @Override
         protected int getTextOffset() {
-            return mTextView.getSelectionStart();
+            return (mTextView.getSelectionStart() + mTextView.getSelectionStart()) / 2;
         }
 
         @Override
@@ -3590,7 +3580,6 @@
             }
 
             if (menu.hasVisibleItems() || mode.getCustomView() != null) {
-                mTextView.setHasTransientState(true);
                 return true;
             } else {
                 return false;
@@ -3690,15 +3679,14 @@
                 customCallback.onDestroyActionMode(mode);
             }
 
-            /*
-             * If we're ending this mode because we're detaching from a window,
-             * we still have selection state to preserve. Don't clear it, we'll
-             * bring back the selection mode when (if) we get reattached.
-             */
-            if (!mPreserveDetachedSelection) {
+            if (!mPreserveSelection) {
+                /*
+                 * Leave current selection when we tentatively destroy action mode for the
+                 * selection. If we're detaching from a window, we'll bring back the selection
+                 * mode when (if) we get reattached.
+                 */
                 Selection.setSelection((Spannable) mTextView.getText(),
                         mTextView.getSelectionEnd());
-                mTextView.setHasTransientState(false);
             }
 
             if (mSelectionModifierCursorController != null) {
@@ -6037,7 +6025,7 @@
         private boolean fireIntent(Intent intent) {
             if (intent != null && Intent.ACTION_PROCESS_TEXT.equals(intent.getAction())) {
                 intent.putExtra(Intent.EXTRA_PROCESS_TEXT, mTextView.getSelectedText());
-                mEditor.mPreserveDetachedSelection = true;
+                mEditor.mPreserveSelection = true;
                 mTextView.startActivityForResult(intent, TextView.PROCESS_TEXT_REQUEST_CODE);
                 return true;
             }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 73f8fdc..ac3eaf7 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8286,6 +8286,13 @@
                 if (newSelEnd < 0) {
                     newSelEnd = Selection.getSelectionEnd(buf);
                 }
+
+                if (newSelStart == newSelEnd && hasTransientState()) {
+                    setHasTransientState(false);
+                } else if (newSelStart != newSelEnd && !hasTransientState()) {
+                    setHasTransientState(true);
+                }
+
                 if (mEditor != null) {
                     mEditor.refreshTextActionMode();
                 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 451669b..fbc96c2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2774,12 +2774,11 @@
         android:protectionLevel="signature" />
 
     <!-- Must be required by an {@link
-         android.service.notification.NotificationAssistantService},
-         to ensure that only the system can bind to it.
+         android.service.notification.NotificationRankerService         to ensure that only the system can bind to it.
          <p>Protection level: signature
          @hide This is not a third-party API (intended for system apps). -->
     -->
-    <permission android:name="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"
+    <permission android:name="android.permission.BIND_NOTIFICATION_RANKER_SERVICE"
         android:protectionLevel="signature" />
 
     <!-- Must be required by a {@link
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 90a573b..9ccd7f0 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8170,6 +8170,8 @@
              Defined in same coordinates as the path itself -->
         <attr name="endY" format="float" />
 
+        <!-- Defines the tile mode of the gradient. SweepGradient don't support tiling. -->
+        <attr name="tileMode"/>
     </declare-styleable>
 
     <!-- Describes an item of a GradientColor. Minimally need 2 items to define the gradient
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a99ee92..17afd92 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3138,7 +3138,7 @@
     <string name="condition_provider_service_binding_label">Condition provider</string>
     <!-- Label to show for a service that is running because it is observing and modifying the
          importance of the user's notifications. -->
-    <string name="notification_assistant_binding_label">Notification assistant</string>
+    <string name="notification_ranker_binding_label">Notification ranker service</string>
 
     <!-- Do Not Translate: Alternate eri.xml -->
     <string name="alternate_eri_file">/data/eri.xml</string>
@@ -4228,4 +4228,9 @@
     <!-- The representation of a time duration when negative. An example is -1:14. This can be used with a countdown timer for example.-->
     <string name="negative_duration">\u2212<xliff:g id="time" example="1:14">%1$s</xliff:g></string>
 
+    <!-- Title of notification shown when device has been forced to safe mode after a security compromise. -->
+    <string name="audit_safemode_notification">Factory reset to use this device normally</string>
+    <!-- Description of notification shown when device has been forced to safe mode after a security compromise. -->
+    <string name="audit_safemode_notification_details">Touch to learn more.</string>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0934c3b..cad0e7b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1844,7 +1844,7 @@
   <java-symbol type="string" name="notification_listener_binding_label" />
   <java-symbol type="string" name="vr_listener_binding_label" />
   <java-symbol type="string" name="condition_provider_service_binding_label" />
-  <java-symbol type="string" name="notification_assistant_binding_label" />
+  <java-symbol type="string" name="notification_ranker_binding_label" />
   <java-symbol type="string" name="report" />
   <java-symbol type="string" name="select_input_method" />
   <java-symbol type="string" name="select_keyboard_layout_notification_title" />
@@ -1895,6 +1895,8 @@
   <java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
   <java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
   <java-symbol type="string" name="config_persistentDataPackageName" />
+  <java-symbol type="string" name="audit_safemode_notification" />
+  <java-symbol type="string" name="audit_safemode_notification_details" />
 
   <java-symbol type="layout" name="resolver_list" />
   <java-symbol type="id" name="resolver_list" />
diff --git a/core/tests/coretests/src/android/transition/SlideTransitionTest.java b/core/tests/coretests/src/android/transition/SlideTransitionTest.java
new file mode 100644
index 0000000..8b9ec74
--- /dev/null
+++ b/core/tests/coretests/src/android/transition/SlideTransitionTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.transition;
+
+import android.animation.AnimatorSetActivity;
+import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import com.android.frameworks.coretests.R;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+
+public class SlideTransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
+
+    Activity mActivity;
+
+    public SlideTransitionTest() {
+        super(AnimatorSetActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mActivity = getActivity();
+    }
+
+    @SmallTest
+    public void testShortSlide() throws Throwable {
+        final float slideFraction = 0.5f;
+        final View square1 = mActivity.findViewById(R.id.square1);
+        final View sceneRoot = mActivity.findViewById(R.id.container);
+        final SlideTranslationValueRatchet ratchet = new SlideTranslationValueRatchet(square1);
+        square1.getViewTreeObserver().addOnPreDrawListener(ratchet);
+
+        final Slide slideOut = new Slide(Gravity.BOTTOM);
+        final float finalOffsetOut = sceneRoot.getHeight() * slideFraction;
+        slideOut.setSlideFraction(slideFraction);
+        TransitionLatch latch = setVisibilityInTransition(slideOut, R.id.square1, View.INVISIBLE);
+        assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+        assertEquals(0f, square1.getTranslationY(), 0.1f);
+        assertEquals(View.VISIBLE, square1.getVisibility());
+        Thread.sleep(100);
+        assertFalse(square1.getTranslationY() < 0.1
+                || square1.getTranslationY() > finalOffsetOut - 0.1);
+        assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+        // Give this 20% slop in case some frames get dropped.
+        assertTrue(finalOffsetOut * 0.8 < ratchet.maxY);
+        assertTrue(finalOffsetOut + 0.1 > ratchet.maxY);
+        assertEquals(View.INVISIBLE, square1.getVisibility());
+
+        ratchet.reset();
+        final Slide slideIn = new Slide(Gravity.BOTTOM);
+        final float initialOffsetIn = sceneRoot.getHeight() * slideFraction;
+        slideIn.setSlideFraction(slideFraction);
+        latch = setVisibilityInTransition(slideIn, R.id.square1, View.VISIBLE);
+        assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+        assertEquals(initialOffsetIn, square1.getTranslationY(), 0.1f);
+        assertEquals(View.VISIBLE, square1.getVisibility());
+        Thread.sleep(100);
+        assertFalse(square1.getTranslationY() < 0.1
+                || square1.getTranslationY() > initialOffsetIn - 0.1);
+        assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+        assertEquals(0f, ratchet.minY, 0.1);
+        assertEquals(0f, square1.getTranslationY(), 0.1);
+        assertEquals(View.VISIBLE, square1.getVisibility());
+
+        square1.getViewTreeObserver().removeOnPreDrawListener(ratchet);
+    }
+
+    public TransitionLatch setVisibilityInTransition(final Transition transition, int viewId,
+            final int visibility) throws Throwable {
+        final ViewGroup sceneRoot = (ViewGroup) mActivity.findViewById(R.id.container);
+        final View view = sceneRoot.findViewById(viewId);
+        TransitionLatch latch = new TransitionLatch();
+        transition.addListener(latch);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.beginDelayedTransition(sceneRoot, transition);
+                view.setVisibility(visibility);
+            }
+        });
+        return latch;
+    }
+
+    public static class TransitionLatch implements Transition.TransitionListener {
+        public CountDownLatch startLatch = new CountDownLatch(1);
+        public CountDownLatch endLatch = new CountDownLatch(1);
+        public CountDownLatch cancelLatch = new CountDownLatch(1);
+        public CountDownLatch pauseLatch = new CountDownLatch(1);
+        public CountDownLatch resumeLatch = new CountDownLatch(1);
+
+        @Override
+        public void onTransitionStart(Transition transition) {
+            startLatch.countDown();
+        }
+
+        @Override
+        public void onTransitionEnd(Transition transition) {
+            endLatch.countDown();
+            transition.removeListener(this);
+        }
+
+        @Override
+        public void onTransitionCancel(Transition transition) {
+            cancelLatch.countDown();
+        }
+
+        @Override
+        public void onTransitionPause(Transition transition) {
+            pauseLatch.countDown();
+        }
+
+        @Override
+        public void onTransitionResume(Transition transition) {
+            resumeLatch.countDown();
+        }
+    }
+
+    private static class SlideTranslationValueRatchet
+            implements ViewTreeObserver.OnPreDrawListener {
+
+        private final View mView;
+        private boolean mInitialized;
+        public float minX = Float.NaN;
+        public float minY = Float.NaN;
+        public float maxX = Float.NaN;
+        public float maxY = Float.NaN;
+
+        public SlideTranslationValueRatchet(View view) {
+            mView = view;
+        }
+
+        public void reset() {
+            minX = minY = maxX = maxY = Float.NaN;
+            mInitialized = false;
+        }
+
+        @Override
+        public boolean onPreDraw() {
+            if (!mInitialized) {
+                minX = maxX = mView.getTranslationX();
+                minY = maxY = mView.getTranslationY();
+                mInitialized = true;
+            } else {
+                minX = Math.min(minX, mView.getTranslationX());
+                minY = Math.min(minY, mView.getTranslationY());
+                maxX = Math.max(maxX, mView.getTranslationX());
+                maxY = Math.max(maxY, mView.getTranslationY());
+            }
+            return true;
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/transition/TransitionTest.java b/core/tests/coretests/src/android/transition/TransitionTest.java
new file mode 100644
index 0000000..7e72e25
--- /dev/null
+++ b/core/tests/coretests/src/android/transition/TransitionTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.transition;
+
+import android.animation.AnimatorSetActivity;
+import android.app.Activity;
+import android.graphics.Rect;
+import android.test.ActivityInstrumentationTestCase2;
+import android.transition.Transition.EpicenterCallback;
+import android.util.ArrayMap;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.TextView;
+
+import com.android.frameworks.coretests.R;
+
+public class TransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
+    Activity mActivity;
+    public TransitionTest() {
+        super(AnimatorSetActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mActivity = getActivity();
+    }
+
+    public void testClone() throws Throwable {
+        View square1 = mActivity.findViewById(R.id.square1);
+        View square2 = mActivity.findViewById(R.id.square2);
+        View square3 = mActivity.findViewById(R.id.square3);
+        Fade fade = new Fade();
+        fade.setStartDelay(1000);
+        fade.setDuration(1001);
+
+        fade.addTarget(square1);
+        fade.excludeTarget(square2, true);
+        fade.excludeChildren(square3, true);
+
+        fade.addTarget(R.id.square4);
+        fade.excludeTarget(R.id.square3, true);
+        fade.excludeChildren(R.id.square2, true);
+
+        fade.addTarget("hello");
+        fade.excludeTarget("world", true);
+
+        fade.addTarget(View.class);
+        fade.excludeTarget(TextView.class, true);
+
+        fade.setMatchOrder(Transition.MATCH_ID);
+        fade.setPropagation(new CircularPropagation());
+        fade.setPathMotion(new ArcMotion());
+        fade.setInterpolator(new AccelerateInterpolator());
+        fade.setNameOverrides(new ArrayMap<>());
+
+        EpicenterCallback epicenterCallback = new EpicenterCallback() {
+            @Override
+            public Rect onGetEpicenter(Transition transition) {
+                return null;
+            }
+        };
+
+        fade.setEpicenterCallback(epicenterCallback);
+
+        Fade clone = (Fade) fade.clone();
+        assertEquals(fade.mStartDelay, clone.mStartDelay);
+        assertEquals(fade.mDuration, clone.mDuration);
+        assertEquals(fade.mInterpolator, clone.mInterpolator);
+        assertEquals(fade.mPropagation, clone.mPropagation);
+        assertEquals(fade.getPathMotion(), clone.getPathMotion());
+        assertEquals(fade.getEpicenterCallback(), clone.getEpicenterCallback());
+        assertEquals(fade.mNameOverrides, clone.mNameOverrides);
+        assertEquals(fade.mMatchOrder, clone.mMatchOrder);
+
+        assertEquals(fade.mTargets, clone.mTargets);
+        assertEquals(fade.mTargetExcludes, clone.mTargetExcludes);
+        assertEquals(fade.mTargetChildExcludes, clone.mTargetChildExcludes);
+
+        assertEquals(fade.mTargetIds, clone.mTargetIds);
+        assertEquals(fade.mTargetIdExcludes, clone.mTargetIdExcludes);
+        assertEquals(fade.mTargetIdChildExcludes, clone.mTargetIdChildExcludes);
+
+        assertEquals(fade.mTargetNames, clone.mTargetNames);
+        assertEquals(fade.mTargetNameExcludes, clone.mTargetNameExcludes);
+
+        assertEquals(fade.mTargetTypes, clone.mTargetTypes);
+        assertEquals(fade.mTargetTypeExcludes, clone.mTargetTypeExcludes);
+    }
+}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 70995ac..9a9a89b 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -227,15 +227,16 @@
 LOCAL_MODULE := hwui_unit_tests
 LOCAL_MODULE_TAGS := tests
 LOCAL_STATIC_LIBRARIES := libhwui_static_null_gpu
+LOCAL_SHARED_LIBRARIES := libmemunreachable
 LOCAL_CFLAGS := \
         $(hwui_cflags) \
         -DHWUI_NULL_GPU
 
 LOCAL_SRC_FILES += \
     $(hwui_test_common_src_files) \
+    tests/unit/main.cpp \
     tests/unit/CanvasStateTests.cpp \
     tests/unit/ClipAreaTests.cpp \
-    tests/unit/CrashHandlerInjector.cpp \
     tests/unit/DamageAccumulatorTests.cpp \
     tests/unit/DeviceInfoTests.cpp \
     tests/unit/FatVectorTests.cpp \
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 9595a85..c809ff4 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -19,9 +19,6 @@
 #include "DeferredLayerUpdater.h"
 #include "LayerRenderer.h"
 
-#include <unistd.h>
-#include <signal.h>
-
 namespace android {
 namespace uirenderer {
 
@@ -136,27 +133,7 @@
     canvas->drawTextOnPath(glyphs.data(), glyphs.size(), path, 0, 0, paint);
 }
 
-static void defaultCrashHandler() {
-    fprintf(stderr, "RenderThread crashed!");
-}
-
-static std::function<void()> gCrashHandler = defaultCrashHandler;
-static sighandler_t gPreviousSignalHandler;
-
-static void signalHandler(int sig) {
-    gCrashHandler();
-    if (gPreviousSignalHandler) {
-        gPreviousSignalHandler(sig);
-    }
-}
-
-void TestUtils::setRenderThreadCrashHandler(std::function<void()> crashHandler) {
-    gCrashHandler = crashHandler;
-}
-
 void TestUtils::TestTask::run() {
-    gPreviousSignalHandler = signal(SIGABRT, signalHandler);
-
     // RenderState only valid once RenderThread is running, so queried here
     RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
 
@@ -164,9 +141,6 @@
     rtCallback(renderthread::RenderThread::getInstance());
     renderState.flush(Caches::FlushMode::Full);
     renderState.onGLContextDestroyed();
-
-    // Restore the previous signal handler
-    signal(SIGABRT, gPreviousSignalHandler);
 }
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 6f23705..28ac116 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -180,8 +180,6 @@
 
     typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
 
-    static void setRenderThreadCrashHandler(std::function<void()> crashHandler);
-
     class TestTask : public renderthread::RenderTask {
     public:
         TestTask(RtCallback rtCallback)
diff --git a/libs/hwui/tests/unit/CrashHandlerInjector.cpp b/libs/hwui/tests/unit/CrashHandlerInjector.cpp
deleted file mode 100644
index b1c678d..0000000
--- a/libs/hwui/tests/unit/CrashHandlerInjector.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-#include "tests/common/TestUtils.h"
-
-#include <gtest/gtest.h>
-#include <cstdio>
-
-using namespace android::uirenderer;
-
-static void gunitCrashHandler() {
-    auto testinfo = ::testing::UnitTest::GetInstance()->current_test_info();
-    printf("[  FAILED  ] %s.%s\n", testinfo->test_case_name(),
-            testinfo->name());
-    printf("[  FATAL!  ] RenderThread crashed, aborting tests!\n");
-    fflush(stdout);
-}
-
-static void hookError() {
-    TestUtils::setRenderThreadCrashHandler(gunitCrashHandler);
-}
-
-class HookErrorInit {
-public:
-    HookErrorInit() { hookError(); }
-};
-
-static HookErrorInit sInit;
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
new file mode 100644
index 0000000..409a12d
--- /dev/null
+++ b/libs/hwui/tests/unit/main.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+#include "gtest/gtest.h"
+
+#include "Caches.h"
+#include "thread/TaskManager.h"
+#include "tests/common/TestUtils.h"
+
+#include <memunreachable/memunreachable.h>
+
+#include <cstdio>
+#include <iostream>
+#include <map>
+#include <unordered_set>
+#include <signal.h>
+#include <unistd.h>
+
+using namespace std;
+using namespace android;
+using namespace android::uirenderer;
+
+static auto CRASH_SIGNALS = {
+        SIGABRT,
+        SIGSEGV,
+        SIGBUS,
+};
+
+static map<int, struct sigaction> gSigChain;
+
+static void gtestSigHandler(int sig, siginfo_t* siginfo, void* context) {
+    auto testinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+    printf("[  FAILED  ] %s.%s\n", testinfo->test_case_name(),
+            testinfo->name());
+    printf("[  FATAL!  ] Process crashed, aborting tests!\n");
+    fflush(stdout);
+
+    // restore the default sighandler and re-raise
+    struct sigaction sa = gSigChain[sig];
+    sigaction(sig, &sa, nullptr);
+    raise(sig);
+}
+
+static void logUnreachable(initializer_list<UnreachableMemoryInfo> infolist) {
+    // merge them all
+    UnreachableMemoryInfo merged;
+    unordered_set<uintptr_t> addrs;
+    merged.allocation_bytes = 0;
+    merged.leak_bytes = 0;
+    merged.num_allocations = 0;
+    merged.num_leaks = 0;
+    for (auto& info : infolist) {
+        // We'll be a little hazzy about these ones and just hope the biggest
+        // is the most accurate
+        merged.allocation_bytes = max(merged.allocation_bytes, info.allocation_bytes);
+        merged.num_allocations = max(merged.num_allocations, info.num_allocations);
+        for (auto& leak : info.leaks) {
+             if (addrs.find(leak.begin) == addrs.end()) {
+                 merged.leaks.push_back(leak);
+                 merged.num_leaks++;
+                 merged.leak_bytes += leak.size;
+                 addrs.insert(leak.begin);
+             }
+        }
+    }
+
+    // Now log the result
+    if (merged.num_leaks) {
+        cout << endl << "Leaked memory!" << endl;
+        if (!merged.leaks[0].backtrace.num_frames) {
+            cout << "Re-run with 'setprop libc.debug.malloc.program hwui_unit_test'"
+                    << endl << "and 'setprop libc.debug.malloc.options backtrace=8'"
+                    << " to get backtraces" << endl;
+        }
+        cout << merged.ToString(false);
+    }
+}
+
+static void checkForLeaks() {
+    // TODO: Until we can shutdown the RT thread we need to do this in
+    // two passes as GetUnreachableMemory has limited insight into
+    // thread-local caches so some leaks will not be properly tagged as leaks
+    nsecs_t before = systemTime();
+    UnreachableMemoryInfo rtMemInfo;
+    TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) {
+        if (Caches::hasInstance()) {
+            Caches::getInstance().tasks.stop();
+        }
+        // Check for leaks
+        if (!GetUnreachableMemory(rtMemInfo)) {
+            cerr << "Failed to get unreachable memory!" << endl;
+            return;
+        }
+    });
+    UnreachableMemoryInfo uiMemInfo;
+    if (!GetUnreachableMemory(uiMemInfo)) {
+        cerr << "Failed to get unreachable memory!" << endl;
+        return;
+    }
+    logUnreachable({rtMemInfo, uiMemInfo});
+    nsecs_t after = systemTime();
+    cout << "Leak check took " << ns2ms(after - before) << "ms" << endl;
+}
+
+int main(int argc, char* argv[]) {
+    // Register a crash handler
+    struct sigaction sa;
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_sigaction = &gtestSigHandler;
+    sa.sa_flags = SA_SIGINFO;
+    for (auto sig : CRASH_SIGNALS) {
+        struct sigaction old_sa;
+        sigaction(sig, &sa, &old_sa);
+        gSigChain.insert(pair<int, struct sigaction>(sig, old_sa));
+    }
+
+    // Run the tests
+    testing::InitGoogleTest(&argc, argv);
+    int ret = RUN_ALL_TESTS();
+    checkForLeaks();
+    return ret;
+}
+
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 8206d23..69d4487 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2730,15 +2730,16 @@
      * this abstract class and register it with
      * {@link AudioManager#registerAudioRecordingCallback(AudioRecordingCallback, Handler)}
      * to be notified.
-     * Use {@link AudioManager#getActiveRecordConfigurations()} to query the current configuration.
+     * Use {@link AudioManager#getActiveRecordingConfigurations()} to query the current
+     * configuration.
      */
     public static abstract class AudioRecordingCallback {
         /**
          * Called whenever the device recording configuration has changed.
          * @param configs array containing the results of
-         *      {@link AudioManager#getActiveRecordConfigurations()}.
+         *      {@link AudioManager#getActiveRecordingConfigurations()}.
          */
-        public void onRecordConfigChanged(AudioRecordConfiguration[] configs) {}
+        public void onRecordConfigChanged(AudioRecordingConfiguration[] configs) {}
     }
 
     private static class AudioRecordingCallbackInfo {
@@ -2752,10 +2753,10 @@
 
     private final static class RecordConfigChangeCallbackData {
         final AudioRecordingCallback mCb;
-        final AudioRecordConfiguration[] mConfigs;
+        final AudioRecordingConfiguration[] mConfigs;
 
         RecordConfigChangeCallbackData(AudioRecordingCallback cb,
-                AudioRecordConfiguration[] configs) {
+                AudioRecordingConfiguration[] configs) {
             mCb = cb;
             mConfigs = configs;
         }
@@ -2838,10 +2839,10 @@
      * @return a non-null array of recording configurations. An array of length 0 indicates there is
      *     no recording active when queried.
      */
-    public @NonNull AudioRecordConfiguration[] getActiveRecordConfigurations() {
+    public @NonNull AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
         final IAudioService service = getService();
         try {
-            return service.getActiveRecordConfigurations();
+            return service.getActiveRecordingConfigurations();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2896,7 +2897,7 @@
 
     private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
 
-        public void dispatchRecordingConfigChange(AudioRecordConfiguration[] configs) {
+        public void dispatchRecordingConfigChange(AudioRecordingConfiguration[] configs) {
             synchronized(mRecordCallbackLock) {
                 if (mRecordCallbackList != null) {
                     for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
diff --git a/media/java/android/media/AudioRecordConfiguration.aidl b/media/java/android/media/AudioRecordingConfiguration.aidl
similarity index 93%
rename from media/java/android/media/AudioRecordConfiguration.aidl
rename to media/java/android/media/AudioRecordingConfiguration.aidl
index afe912b..c63d30b 100644
--- a/media/java/android/media/AudioRecordConfiguration.aidl
+++ b/media/java/android/media/AudioRecordingConfiguration.aidl
@@ -15,4 +15,4 @@
 
 package android.media;
 
-parcelable AudioRecordConfiguration;
+parcelable AudioRecordingConfiguration;
diff --git a/media/java/android/media/AudioRecordConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
similarity index 82%
rename from media/java/android/media/AudioRecordConfiguration.java
rename to media/java/android/media/AudioRecordingConfiguration.java
index de78a5a..cd6f95a 100644
--- a/media/java/android/media/AudioRecordConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -27,13 +27,13 @@
 import java.util.Objects;
 
 /**
- * The AudioRecordConfiguration class collects the information describing an audio recording
+ * The AudioRecordingConfiguration class collects the information describing an audio recording
  * session. This information is returned through the
- * {@link AudioManager#getActiveRecordConfigurations()} method.
+ * {@link AudioManager#getActiveRecordingConfigurations()} method.
  *
  */
-public final class AudioRecordConfiguration implements Parcelable {
-    private final static String TAG = new String("AudioRecordConfiguration");
+public final class AudioRecordingConfiguration implements Parcelable {
+    private final static String TAG = new String("AudioRecordingConfiguration");
 
     private final int mSessionId;
 
@@ -47,7 +47,7 @@
     /**
      * @hide
      */
-    public AudioRecordConfiguration(int session, int source, AudioFormat devFormat,
+    public AudioRecordingConfiguration(int session, int source, AudioFormat devFormat,
             AudioFormat clientFormat, int patchHandle) {
         mSessionId = session;
         mClientSource = source;
@@ -136,18 +136,18 @@
         return null;
     }
 
-    public static final Parcelable.Creator<AudioRecordConfiguration> CREATOR
-            = new Parcelable.Creator<AudioRecordConfiguration>() {
+    public static final Parcelable.Creator<AudioRecordingConfiguration> CREATOR
+            = new Parcelable.Creator<AudioRecordingConfiguration>() {
         /**
-         * Rebuilds an AudioRecordConfiguration previously stored with writeToParcel().
-         * @param p Parcel object to read the AudioRecordConfiguration from
-         * @return a new AudioRecordConfiguration created from the data in the parcel
+         * Rebuilds an AudioRecordingConfiguration previously stored with writeToParcel().
+         * @param p Parcel object to read the AudioRecordingConfiguration from
+         * @return a new AudioRecordingConfiguration created from the data in the parcel
          */
-        public AudioRecordConfiguration createFromParcel(Parcel p) {
-            return new AudioRecordConfiguration(p);
+        public AudioRecordingConfiguration createFromParcel(Parcel p) {
+            return new AudioRecordingConfiguration(p);
         }
-        public AudioRecordConfiguration[] newArray(int size) {
-            return new AudioRecordConfiguration[size];
+        public AudioRecordingConfiguration[] newArray(int size) {
+            return new AudioRecordingConfiguration[size];
         }
     };
 
@@ -170,7 +170,7 @@
         dest.writeInt(mPatchHandle);
     }
 
-    private AudioRecordConfiguration(Parcel in) {
+    private AudioRecordingConfiguration(Parcel in) {
         mSessionId = in.readInt();
         mClientSource = in.readInt();
         mClientFormat = AudioFormat.CREATOR.createFromParcel(in);
@@ -181,9 +181,9 @@
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
-        if (o == null || !(o instanceof AudioRecordConfiguration)) return false;
+        if (o == null || !(o instanceof AudioRecordingConfiguration)) return false;
 
-        AudioRecordConfiguration that = (AudioRecordConfiguration) o;
+        AudioRecordingConfiguration that = (AudioRecordingConfiguration) o;
 
         return ((mSessionId == that.mSessionId)
                 && (mClientSource == that.mClientSource)
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 987a8b6..97f670b 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -20,7 +20,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.content.ComponentName;
 import android.media.AudioAttributes;
-import android.media.AudioRecordConfiguration;
+import android.media.AudioRecordingConfiguration;
 import android.media.AudioRoutesInfo;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioRoutesObserver;
@@ -164,5 +164,5 @@
 
     oneway void unregisterRecordingCallback(in IRecordingConfigDispatcher rcdb);
 
-    AudioRecordConfiguration[] getActiveRecordConfigurations();
+    AudioRecordingConfiguration[] getActiveRecordingConfigurations();
 }
diff --git a/media/java/android/media/IRecordingConfigDispatcher.aidl b/media/java/android/media/IRecordingConfigDispatcher.aidl
index eaa92ca..e803283 100644
--- a/media/java/android/media/IRecordingConfigDispatcher.aidl
+++ b/media/java/android/media/IRecordingConfigDispatcher.aidl
@@ -16,7 +16,7 @@
 
 package android.media;
 
-import android.media.AudioRecordConfiguration;
+import android.media.AudioRecordingConfiguration;
 
 /**
  * AIDL for the RecordingActivity monitor in AudioService to signal audio recording updates.
@@ -25,6 +25,6 @@
  */
 oneway interface IRecordingConfigDispatcher {
 
-    void dispatchRecordingConfigChange(in AudioRecordConfiguration[] configs);
+    void dispatchRecordingConfigChange(in AudioRecordingConfiguration[] configs);
 
 }
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 63e3edc..7c9591d 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -349,12 +349,12 @@
     /**
      * Returns the number of tuners this TV input has.
      *
-     * <p>This method is valid only for the input of type {@link #TYPE_TUNER}.
+     * <p>This method is valid only for inputs of type {@link #TYPE_TUNER}. For inputs of other
+     * types, it returns 0.
      *
      * <p>Tuners correspond to physical/logical resources that allow reception of TV signal. Having
      * <i>N</i> tuners means that the TV input is capable of receiving <i>N</i> different channels
      * concurrently.
-     *
      */
     public int getTunerCount() {
         return mTunerCount;
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 8fb58b5..bc20c17 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -491,7 +491,7 @@
          * until this method is called.
          *
          * <p>The TV input service must call this method as soon as the content rendered onto its
-         * surface is ready for viewing. This method must be called each time {@link #onTune(Uri)}
+         * surface is ready for viewing. This method must be called each time {@link #onTune}
          * is called.
          *
          * @see #notifyVideoUnavailable
@@ -837,14 +837,15 @@
         public abstract boolean onTune(Uri channelUri);
 
         /**
-         * Calls {@link #onTune(Uri)}. Override this method in order to handle {@code params}.
+         * Calls {@link #onTune(Uri)}. Override this method in order to handle domain-specific
+         * features that are only known between certain TV inputs and their clients.
          *
          * @param channelUri The URI of the channel.
-         * @param params The extra parameters from other applications.
+         * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
+         *            name, i.e. prefixed with a package name you own, so that different developers
+         *            will not create conflicting keys.
          * @return {@code true} if the tuning was successful, {@code false} otherwise.
-         * @hide
          */
-        @SystemApi
         public boolean onTune(Uri channelUri, Bundle params) {
             return onTune(channelUri);
         }
@@ -1209,7 +1210,7 @@
         }
 
         /**
-         * Calls {@link #onTune}.
+         * Calls {@link #onTune(Uri, Bundle)}.
          */
         void tune(Uri channelUri, Bundle params) {
             mCurrentPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
@@ -1836,7 +1837,7 @@
      * a hardware TV Input (e.g. HDMI 1) and forward the application's surface to the session so
      * that the user can see the screen of the hardware TV Input when she tunes to a channel from
      * this TV input. The implementation of this class is expected to change the channel of the
-     * external set-top box via a proprietary protocol when {@link HardwareSession#onTune(Uri)} is
+     * external set-top box via a proprietary protocol when {@link HardwareSession#onTune} is
      * requested by the application.
      *
      * <p>Note that this class is not for inputs for internal hardware like built-in tuner and HDMI
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index 6a44b1e..e623353 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -121,6 +121,8 @@
 
     /**
      * Returns the audio channel count. Valid only for {@link #TYPE_AUDIO} tracks.
+     *
+     * @throws IllegalStateException if not called on an audio track
      */
     public final int getAudioChannelCount() {
         if (mType != TYPE_AUDIO) {
@@ -131,6 +133,8 @@
 
     /**
      * Returns the audio sample rate, in the unit of Hz. Valid only for {@link #TYPE_AUDIO} tracks.
+     *
+     * @throws IllegalStateException if not called on an audio track
      */
     public final int getAudioSampleRate() {
         if (mType != TYPE_AUDIO) {
@@ -142,6 +146,8 @@
     /**
      * Returns the width of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO}
      * tracks.
+     *
+     * @throws IllegalStateException if not called on a video track
      */
     public final int getVideoWidth() {
         if (mType != TYPE_VIDEO) {
@@ -153,6 +159,8 @@
     /**
      * Returns the height of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO}
      * tracks.
+     *
+     * @throws IllegalStateException if not called on a video track
      */
     public final int getVideoHeight() {
         if (mType != TYPE_VIDEO) {
@@ -164,6 +172,8 @@
     /**
      * Returns the frame rate of the video, in the unit of fps (frames per second). Valid only for
      * {@link #TYPE_VIDEO} tracks.
+     *
+     * @throws IllegalStateException if not called on a video track
      */
     public final float getVideoFrameRate() {
         if (mType != TYPE_VIDEO) {
@@ -175,6 +185,8 @@
     /**
      * Returns the pixel aspect ratio (the ratio of a pixel's width to its height) of the video.
      * Valid only for {@link #TYPE_VIDEO} tracks.
+     *
+     * @throws IllegalStateException if not called on a video track
      */
     public final float getVideoPixelAspectRatio() {
         if (mType != TYPE_VIDEO) {
@@ -189,6 +201,8 @@
      *
      * <p>The complete list of values are defined in ETSI TS 101 154 V1.7.1 Annex B, ATSC A/53 Part
      * 4 and SMPTE 2016-1-2007.
+     *
+     * @throws IllegalStateException if not called on a video track
      */
     public final byte getVideoActiveFormatDescription() {
         if (mType != TYPE_VIDEO) {
@@ -268,6 +282,8 @@
          * @param type The type of the track.
          * @param id The ID of the track that uniquely identifies the current track among all the
          *            other tracks in the same TV program.
+         * @throws IllegalArgumentException if the type is not any of {@link #TYPE_AUDIO},
+         *                                  {@link #TYPE_VIDEO} and {@link #TYPE_SUBTITLE}
          */
         public Builder(int type, @NonNull String id) {
             if (type != TYPE_AUDIO
@@ -304,6 +320,7 @@
          * Sets the audio channel count. Valid only for {@link #TYPE_AUDIO} tracks.
          *
          * @param audioChannelCount The audio channel count.
+         * @throws IllegalStateException if not called on an audio track
          */
         public final Builder setAudioChannelCount(int audioChannelCount) {
             if (mType != TYPE_AUDIO) {
@@ -318,6 +335,7 @@
          * tracks.
          *
          * @param audioSampleRate The audio sample rate.
+         * @throws IllegalStateException if not called on an audio track
          */
         public final Builder setAudioSampleRate(int audioSampleRate) {
             if (mType != TYPE_AUDIO) {
@@ -332,6 +350,7 @@
          * tracks.
          *
          * @param videoWidth The width of the video.
+         * @throws IllegalStateException if not called on a video track
          */
         public final Builder setVideoWidth(int videoWidth) {
             if (mType != TYPE_VIDEO) {
@@ -346,6 +365,7 @@
          * tracks.
          *
          * @param videoHeight The height of the video.
+         * @throws IllegalStateException if not called on a video track
          */
         public final Builder setVideoHeight(int videoHeight) {
             if (mType != TYPE_VIDEO) {
@@ -360,6 +380,7 @@
          * {@link #TYPE_VIDEO} tracks.
          *
          * @param videoFrameRate The frame rate of the video.
+         * @throws IllegalStateException if not called on a video track
          */
         public final Builder setVideoFrameRate(float videoFrameRate) {
             if (mType != TYPE_VIDEO) {
@@ -379,6 +400,7 @@
          * pixel aspect ratio for most video formats.
          *
          * @param videoPixelAspectRatio The pixel aspect ratio of the video.
+         * @throws IllegalStateException if not called on a video track
          */
         public final Builder setVideoPixelAspectRatio(float videoPixelAspectRatio) {
             if (mType != TYPE_VIDEO) {
@@ -398,6 +420,7 @@
          * 4 and SMPTE 2016-1-2007.
          *
          * @param videoActiveFormatDescription The AFD code of the video.
+         * @throws IllegalStateException if not called on a video track
          */
         public final Builder setVideoActiveFormatDescription(byte videoActiveFormatDescription) {
             if (mType != TYPE_VIDEO) {
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 5c4b528..9623076 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -56,7 +57,7 @@
  * TV inputs available on the system can be obtained by calling
  * {@link TvInputManager#getTvInputList() TvInputManager.getTvInputList()}.)
  *
- * <p>Once the application supplies the URI for a specific TV channel to {@link #tune(String, Uri)}
+ * <p>Once the application supplies the URI for a specific TV channel to {@link #tune}
  * method, it takes care of underlying service binding (and unbinding if the current TvView is
  * already bound to a service) and automatically allocates/deallocates resources needed. In addition
  * to a few essential methods to control how the contents are presented, it also provides a way to
@@ -206,13 +207,18 @@
     }
 
     /**
-     * Sets the Z order of a window owning the surface of this TvView above the normal TvView
-     * but below an application.
+     * Controls whether the TvView's surface is placed on top of another regular surface view in the
+     * window (but still behind the window itself).
+     * This is typically used to place overlays on top of an underlying TvView.
      *
-     * @see SurfaceView#setZOrderMediaOverlay
-     * @hide
+     * <p>Note that this must be set before the TvView's containing window is attached to the
+     * window manager.
+     *
+     * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
+     *
+     * @param isMediaOverlay {@code true} to be on top of another regular surface, {@code false}
+     *            otherwise.
      */
-    @SystemApi
     public void setZOrderMediaOverlay(boolean isMediaOverlay) {
         if (isMediaOverlay) {
             mWindowZOrder = ZORDER_MEDIA_OVERLAY;
@@ -230,12 +236,18 @@
     }
 
     /**
-     * Sets the Z order of a window owning the surface of this TvView on top of an application.
+     * Controls whether the TvView's surface is placed on top of its window. Normally it is placed
+     * behind the window, to allow it to (for the most part) appear to composite with the views in
+     * the hierarchy.  By setting this, you cause it to be placed above the window. This means that
+     * none of the contents of the window this TvView is in will be visible on top of its surface.
      *
-     * @see SurfaceView#setZOrderOnTop
-     * @hide
+     * <p>Note that this must be set before the TvView's containing window is attached to the window
+     * manager.
+     *
+     * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
+     *
+     * @param onTop {@code true} to be on top of its window, {@code false} otherwise.
      */
-    @SystemApi
     public void setZOrderOnTop(boolean onTop) {
         if (onTop) {
             mWindowZOrder = ZORDER_ON_TOP;
@@ -280,14 +292,15 @@
     }
 
     /**
-     * Tunes to a given channel.
+     * Tunes to a given channel. This can be used to provide domain-specific features that are only
+     * known between certain TvView applications and their TV inputs.
      *
      * @param inputId The ID of TV input for the given channel.
      * @param channelUri The URI of a channel.
-     * @param params Extra parameters.
-     * @hide
+     * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
+     *            name, i.e. prefixed with a package name you own, so that different developers will
+     *            not create conflicting keys.
      */
-    @SystemApi
     public void tune(String inputId, Uri channelUri, Bundle params) {
         if (DEBUG) Log.d(TAG, "tune(" + channelUri + ")");
         if (TextUtils.isEmpty(inputId)) {
@@ -360,11 +373,8 @@
      *
      * @param unblockedRating A TvContentRating to unblock.
      * @see TvInputService.Session#notifyContentBlocked(TvContentRating)
-     * @hide
-     * @deprecated Use {@link #unblockContent} instead.
+     * @removed
      */
-    @Deprecated
-    @SystemApi
     public void requestUnblockContent(TvContentRating unblockedRating) {
         unblockContent(unblockedRating);
     }
@@ -379,6 +389,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
     public void unblockContent(TvContentRating unblockedRating) {
         if (mSession != null) {
             mSession.unblockContent(unblockedRating);
@@ -539,7 +550,7 @@
     }
 
     /**
-     * Calls {@link TvInputService.Session#appPrivateCommand(String, Bundle)} for the current
+     * Calls {@link TvInputService.Session#onAppPrivateCommand(String, Bundle)} for the current
      * session.
      *
      * @param action The name of the private command to send. This <em>must</em> be a scoped name,
@@ -893,7 +904,7 @@
 
         /**
          * This is invoked when the channel of this TvView is changed by the underlying TV input
-         * without any {@link TvView#tune(String, Uri)} request.
+         * without any {@link TvView#tune} request.
          *
          * @param inputId The ID of the TV input bound to this view.
          * @param channelUri The URI of a channel.
diff --git a/packages/DocumentsUI/res/layout/dialog_delete_confirmation.xml b/packages/DocumentsUI/res/layout/dialog_delete_confirmation.xml
index 990ce0b..a1c2910 100644
--- a/packages/DocumentsUI/res/layout/dialog_delete_confirmation.xml
+++ b/packages/DocumentsUI/res/layout/dialog_delete_confirmation.xml
@@ -18,8 +18,9 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:paddingTop="30dp"
-    android:gravity="center"
+    android:paddingTop="24dp"
+    android:paddingStart="24dp"
+    android:paddingEnd="24dp"
     android:textAppearance="@android:style/TextAppearance.Material.Subhead"
     android:textColor="@*android:color/primary_text_default_material_light">
 </TextView>
diff --git a/packages/DocumentsUI/res/values-sw720dp-land/dimens.xml b/packages/DocumentsUI/res/values-sw720dp-land/dimens.xml
index fa11244..1b67ee5 100644
--- a/packages/DocumentsUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/DocumentsUI/res/values-sw720dp-land/dimens.xml
@@ -20,4 +20,5 @@
 
     <dimen name="list_divider_inset">80dp</dimen>
 
+    <dimen name="max_drawer_width">320dp</dimen>
 </resources>
diff --git a/packages/DocumentsUI/res/values-sw720dp/dimens.xml b/packages/DocumentsUI/res/values-sw720dp/dimens.xml
index b5d1150..982b204 100644
--- a/packages/DocumentsUI/res/values-sw720dp/dimens.xml
+++ b/packages/DocumentsUI/res/values-sw720dp/dimens.xml
@@ -20,4 +20,5 @@
 
     <dimen name="list_item_padding">24dp</dimen>
 
+    <dimen name="max_drawer_width">320dp</dimen>
 </resources>
diff --git a/packages/DocumentsUI/res/values/dimens.xml b/packages/DocumentsUI/res/values/dimens.xml
index 9fc8a73..5af7da3 100644
--- a/packages/DocumentsUI/res/values/dimens.xml
+++ b/packages/DocumentsUI/res/values/dimens.xml
@@ -37,4 +37,5 @@
     <dimen name="dir_elevation">8dp</dimen>
     <dimen name="drag_shadow_size">120dp</dimen>
     <dimen name="grid_item_elevation">2dp</dimen>
+    <dimen name="max_drawer_width">280dp</dimen>
 </resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Display.java b/packages/DocumentsUI/src/com/android/documentsui/Display.java
new file mode 100644
index 0000000..bae2d58
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/Display.java
@@ -0,0 +1,56 @@
+/*
+ * 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.documentsui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Point;
+import android.util.TypedValue;
+
+/*
+ * Convenience class for getting display related attributes
+ */
+public final class Display {
+    /*
+     * Returns the screen width in pixels.
+     */
+    public static float screenWidth(Activity activity) {
+        Point size = new Point();
+        activity.getWindowManager().getDefaultDisplay().getSize(size);
+        return size.x;
+    }
+
+    /*
+     * Returns logical density of the display.
+     */
+    public static float density(Context context) {
+        return context.getResources().getDisplayMetrics().density;
+    }
+
+    /*
+     * Returns action bar height in pixels.
+     */
+    public static float actionBarHeight(Context context) {
+        int actionBarHeight = 0;
+        TypedValue tv = new TypedValue();
+        if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
+            actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,
+                    context.getResources().getDisplayMetrics());
+        }
+        return actionBarHeight;
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
index 9fceff5..020f2c0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
@@ -16,10 +16,14 @@
 
 package com.android.documentsui;
 
+import static com.android.documentsui.Shared.DEBUG;
+
 import android.app.Activity;
+import android.content.Context;
 import android.support.v4.app.ActionBarDrawerToggle;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v4.widget.DrawerLayout.DrawerListener;
+import android.util.Log;
 import android.view.View;
 import android.widget.Toolbar;
 
@@ -30,6 +34,8 @@
  */
 abstract class DrawerController implements DrawerListener {
 
+    public static final String TAG = "DrawerController";
+
     abstract void setOpen(boolean open);
     abstract boolean isPresent();
     abstract boolean isOpen();
@@ -50,6 +56,8 @@
         View drawer = activity.findViewById(R.id.drawer_roots);
         Toolbar toolbar = (Toolbar) activity.findViewById(R.id.roots_toolbar);
 
+        drawer.getLayoutParams().width = calculateDrawerWidth(activity);
+
         ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                 activity,
                 layout,
@@ -67,6 +75,19 @@
         return new DummyDrawerController();
     }
 
+    private static int calculateDrawerWidth(Activity activity) {
+        // Material design specification for navigation drawer:
+        // https://www.google.com/design/spec/patterns/navigation-drawer.html
+        float width = Display.screenWidth(activity) - Display.actionBarHeight(activity);
+        float maxWidth = activity.getResources().getDimension(R.dimen.max_drawer_width);
+        int finalWidth = (int) ((width > maxWidth ? maxWidth : width));
+
+        if (DEBUG)
+            Log.d(TAG, "Calculated drawer width:" + (finalWidth / Display.density(activity)));
+
+        return finalWidth;
+    }
+
     /**
      * Runtime controller that manages a real drawer.
      */
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 3920c62..ad4823e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -2634,6 +2634,10 @@
                 }
 
                 mPrinterAvailabilityDetector.updatePrinter(currentPrinter);
+
+                // Force a reload of the enabled print services to update
+                // mAdvancedPrintOptionsActivity in onLoadFinished();
+                getLoaderManager().getLoader(LOADER_ID_ENABLED_PRINT_SERVICES).forceLoad();
             } else if (spinner == mMediaSizeSpinner) {
                 SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position);
                 PrintAttributes attributes = mPrintJob.getAttributes();
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index ae2c6e7..ef397f6 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -677,7 +677,7 @@
     <!-- Services settings screen, setting option summary for the user to go to the screen to view running services  -->
     <string name="runningservices_settings_summary">View and control currently running services</string>
 
-    <!-- Developer settings: enable WebView multiprocess name [CHAR LIMIT=30] -->
+    <!-- Developer settings: enable WebView multiprocess name [CHAR LIMIT=50] -->
     <string name="enable_webview_multiprocess">Enable multiprocess WebView</string>
     <!-- Developer settings: enable WebView multiprocess summary [CHAR LIMIT=60] -->
     <string name="enable_webview_multiprocess_desc">Run WebView renderers in an isolated process.</string>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 51d8ca0..978ca94 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -155,6 +155,9 @@
     <!-- Default for Settings.Secure.LONG_PRESS_TIMEOUT_MILLIS -->
     <integer name="def_long_press_timeout_millis">500</integer>
 
+    <!-- Default for Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD -->
+    <bool name="def_show_ime_with_hard_keyboard">false</bool>
+
     <!-- Default for Settings.System.POINTER_SPEED -->
     <integer name="def_pointer_speed">0</integer>
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index a424d55..987b5ea 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1671,16 +1671,16 @@
 
         private void migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper,
                 SQLiteDatabase database, int userId) {
-            // Move over the global settings if owner.
-            if (userId == UserHandle.USER_SYSTEM) {
-                final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, userId);
-                ensureSettingsStateLocked(globalKey);
-                SettingsState globalSettings = mSettingsStates.get(globalKey);
-                migrateLegacySettingsLocked(globalSettings, database, TABLE_GLOBAL);
-                globalSettings.persistSyncLocked();
-            }
+            // Move over the system settings.
+            final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);
+            ensureSettingsStateLocked(systemKey);
+            SettingsState systemSettings = mSettingsStates.get(systemKey);
+            migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM);
+            systemSettings.persistSyncLocked();
 
             // Move over the secure settings.
+            // Do this after System settings, since this is the first thing we check when deciding
+            // to skip over migration from db to xml for a secondary user.
             final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId);
             ensureSettingsStateLocked(secureKey);
             SettingsState secureSettings = mSettingsStates.get(secureKey);
@@ -1688,12 +1688,16 @@
             ensureSecureSettingAndroidIdSetLocked(secureSettings);
             secureSettings.persistSyncLocked();
 
-            // Move over the system settings.
-            final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);
-            ensureSettingsStateLocked(systemKey);
-            SettingsState systemSettings = mSettingsStates.get(systemKey);
-            migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM);
-            systemSettings.persistSyncLocked();
+            // Move over the global settings if owner.
+            // Do this last, since this is the first thing we check when deciding
+            // to skip over migration from db to xml for owner user.
+            if (userId == UserHandle.USER_SYSTEM) {
+                final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, userId);
+                ensureSettingsStateLocked(globalKey);
+                SettingsState globalSettings = mSettingsStates.get(globalKey);
+                migrateLegacySettingsLocked(globalSettings, database, TABLE_GLOBAL);
+                globalSettings.persistSyncLocked();
+            }
 
             // Drop the database as now all is moved and persisted.
             if (DROP_DATABASE_ON_MIGRATION) {
@@ -1936,7 +1940,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 124;
+            private static final int SETTINGS_VERSION = 125;
 
             private final int mUserId;
 
@@ -2116,6 +2120,22 @@
                     currentVersion = 124;
                 }
 
+                if (currentVersion == 124) {
+                    // Version 124: allow OEMs to set a default value for whether IME should be
+                    // shown when a physical keyboard is connected.
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    Setting currentSetting = secureSettings.getSettingLocked(
+                            Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
+                    if (currentSetting == null) {
+                        secureSettings.insertSettingLocked(
+                                Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+                                getContext().getResources().getBoolean(
+                                        R.bool.def_show_ime_with_hard_keyboard) ? "1" : "0",
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    currentVersion = 125;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 // Return the current version.
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 0fad113..1c6a071 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -56,7 +56,6 @@
 import android.app.Service;
 import android.content.ClipData;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.res.Configuration;
@@ -206,7 +205,7 @@
         mMainHandler = new ServiceHandler("BugreportProgressServiceMainThread");
         mScreenshotHandler = new ScreenshotHandler("BugreportProgressServiceScreenshotThread");
 
-        mScreenshotsDir = new File(new ContextWrapper(mContext).getFilesDir(), SCREENSHOT_DIR);
+        mScreenshotsDir = new File(getFilesDir(), SCREENSHOT_DIR);
         if (!mScreenshotsDir.exists()) {
             Log.i(TAG, "Creating directory " + mScreenshotsDir + " to store temporary screenshots");
             if (!mScreenshotsDir.mkdir()) {
@@ -505,9 +504,9 @@
             Log.d(TAG, "Removing ID " + id);
             mProcesses.remove(id);
         }
-        stopSelfWhenDone();
         Log.v(TAG, "stopProgress(" + id + "): cancel notification");
         NotificationManager.from(mContext).cancel(TAG, id);
+        stopSelfWhenDone();
     }
 
     /**
@@ -877,6 +876,8 @@
             info = sharedInfo;
             Log.d(TAG, "shareBugreport(): no info for ID " + id + " on managed processes ("
                     + mProcesses + "), using info from intent instead (" + info + ")");
+        } else {
+            Log.v(TAG, "shareBugReport(): id " + id + " info = " + info);
         }
 
         addDetailsToZipFile(mContext, info);
@@ -1532,6 +1533,7 @@
                 final File newFile;
                 if (!newName.equals(oldName)) {
                     final File renamedFile = new File(screenshotDir, newName);
+                    Log.d(TAG, "Renaming screenshot file " + oldFile + " to " + renamedFile);
                     newFile = oldFile.renameTo(renamedFile) ? renamedFile : oldFile;
                 } else {
                     Log.w(TAG, "Name didn't change: " + oldName); // Shouldn't happen.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 6137349..c8c71cb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,11 +14,11 @@
 
 package com.android.systemui.qs;
 
+import android.graphics.Path;
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnLayoutChangeListener;
-import android.view.animation.PathInterpolator;
 import android.widget.TextView;
 import com.android.systemui.qs.PagedTileLayout.PageListener;
 import com.android.systemui.qs.QSPanel.QSTileLayout;
@@ -40,9 +40,6 @@
     private static final String ALLOW_FANCY_ANIMATION = "sysui_qs_fancy_anim";
     private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows";
 
-    public static final PathInterpolator TRANSLATION_Y_INTERPOLATOR =
-            new PathInterpolator(.1f, .3f, 1, 1);
-
     public static final float EXPANDED_TILE_DELAY = .7f;
 
     private final ArrayList<View> mAllViews = new ArrayList<>();
@@ -56,7 +53,7 @@
     private boolean mOnFirstPage = true;
     private TouchAnimator mFirstPageAnimator;
     private TouchAnimator mFirstPageDelayedAnimator;
-    private TouchAnimator mTranslationYAnimator;
+    private TouchAnimator mTranslationAnimator;
     private TouchAnimator mNonfirstPageAnimator;
 
     private boolean mOnKeyguard;
@@ -110,7 +107,7 @@
                 clearAnimationState();
             }
         } else if (MOVE_FULL_ROWS.equals(key)) {
-            mFullRows = newValue != null && Integer.parseInt(newValue) != 0;
+            mFullRows = newValue == null || Integer.parseInt(newValue) != 0;
         } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
             mNumQuickTiles = QuickQSPanel.getNumQuickTiles(mQsContainer.getContext());
             clearAnimationState();
@@ -129,6 +126,7 @@
 
     private void updateAnimators() {
         TouchAnimator.Builder firstPageBuilder = new Builder();
+        TouchAnimator.Builder translationXBuilder = new Builder();
         TouchAnimator.Builder translationYBuilder = new Builder();
         TouchAnimator.Builder firstPageDelayedBuilder = new Builder();
         Collection<QSTile<?>> tiles = mQsPanel.getHost().getTiles();
@@ -138,7 +136,6 @@
         int lastYDiff = 0;
         firstPageDelayedBuilder.setStartDelay(EXPANDED_TILE_DELAY);
         firstPageBuilder.setListener(this);
-        translationYBuilder.setInterpolator(TRANSLATION_Y_INTERPOLATOR);
         // Fade in the tiles/labels as we reach the final position.
         firstPageDelayedBuilder.addFloat(mQsPanel.getTileLayout(), "alpha", 0, 1);
         mAllViews.clear();
@@ -158,7 +155,7 @@
                 final int yDiff = loc2[1] - loc1[1];
                 lastYDiff = yDiff;
                 // Move the quick tile right from its location to the new one.
-                firstPageBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
+                translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
                 translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
 
                 // Counteract the parent translation on the tile. So we have a static base to
@@ -167,7 +164,7 @@
 
                 // Move the real tile's label from the quick tile position to its final
                 // location.
-                firstPageBuilder.addFloat(label, "translationX", -xDiff, 0);
+                translationXBuilder.addFloat(label, "translationX", -xDiff, 0);
                 translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
 
                 mTopFiveQs.add(tileIcon);
@@ -188,7 +185,13 @@
         if (mAllowFancy) {
             mFirstPageAnimator = firstPageBuilder.build();
             mFirstPageDelayedAnimator = firstPageDelayedBuilder.build();
-            mTranslationYAnimator = translationYBuilder.build();
+            Path path = new Path();
+            path.moveTo(0, 0);
+            path.cubicTo(0, 0, 0, 1, 1, 1);
+            mTranslationAnimator = new TouchAnimator.Builder()
+                    .addPath(translationXBuilder.build(), translationYBuilder.build(),
+                            "position", "position", path)
+                    .build();
         }
         mNonfirstPageAnimator = new TouchAnimator.Builder()
                 .addFloat(mQuickQsPanel, "alpha", 1, 0)
@@ -226,7 +229,7 @@
             mQuickQsPanel.setAlpha(1);
             mFirstPageAnimator.setPosition(position);
             mFirstPageDelayedAnimator.setPosition(position);
-            mTranslationYAnimator.setPosition(position);
+            mTranslationAnimator.setPosition(position);
         } else {
             mNonfirstPageAnimator.setPosition(position);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index 35ade58..db17245 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -14,7 +14,11 @@
 
 package com.android.systemui.qs;
 
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.util.FloatProperty;
 import android.util.MathUtils;
+import android.util.Pair;
 import android.util.Property;
 import android.view.View;
 import android.view.animation.Interpolator;
@@ -74,6 +78,19 @@
         }
     }
 
+    private static final FloatProperty<TouchAnimator> POSITION =
+            new FloatProperty<TouchAnimator>("position") {
+        @Override
+        public void setValue(TouchAnimator touchAnimator, float value) {
+            touchAnimator.setPosition(value);
+        }
+
+        @Override
+        public Float get(TouchAnimator touchAnimator) {
+            return touchAnimator.mLastT;
+        }
+    };
+
     public static class ListenerAdapter implements Listener {
         @Override
         public void onAnimationAtStart() { }
@@ -124,6 +141,19 @@
             return this;
         }
 
+        public Builder addPath(Object target, String xProp, String yProp,
+                Path path) {
+            return addPath(target, target, xProp, yProp, path);
+        }
+
+        public Builder addPath(Object xTarget, Object yTarget, String xProp, String yProp,
+                Path path) {
+            add(new Pair<>(xTarget, yTarget),
+                    KeyframeSet.ofPath(getProperty(xTarget, xProp, float.class),
+                    getProperty(yTarget, yProp, float.class), path));
+            return this;
+        }
+
         private void add(Object target, KeyframeSet keyframeSet) {
             mTargets.add(target);
             mValues.add(keyframeSet);
@@ -152,6 +182,9 @@
                         return View.SCALE_Y;
                 }
             }
+            if (target instanceof TouchAnimator && "position".equals(property)) {
+                return POSITION;
+            }
             return Property.of(target.getClass(), cls, property);
         }
 
@@ -208,6 +241,10 @@
         public static KeyframeSet ofFloat(Property property, float... values) {
             return new FloatKeyframeSet((Property<?, Float>) property, values);
         }
+
+        public static KeyframeSet ofPath(Property xProp, Property yProp, Path path) {
+            return new PathKeyframeSet<>(xProp, yProp, path);
+        }
     }
 
     private static class FloatKeyframeSet<T> extends KeyframeSet {
@@ -245,4 +282,31 @@
             mProperty.set((T) target, (int) (firstFloat + (secondFloat - firstFloat) * amount));
         }
     }
+
+    private static class PathKeyframeSet<T> extends KeyframeSet {
+        private final Property<T, Float> mXProp;
+        private final Property<T, Float> mYProp;
+        private final Path mPath;
+        private final PathMeasure mPathMeasure;
+        private final float mLength;
+        private final float[] mPos;
+
+        public PathKeyframeSet(Property<T, Float> xProp, Property<T, Float> yProp, Path path) {
+            super(2);
+            mXProp = xProp;
+            mYProp = yProp;
+            mPath = path;
+            mPathMeasure = new PathMeasure(mPath, false);
+            mLength = mPathMeasure.getLength();
+            mPos = new float[2];
+        }
+
+        @Override
+        protected void interpolate(int index, float amount, Object target) {
+            Pair<Object, Object> targets = (Pair<Object, Object>) target;
+            mPathMeasure.getPosTan(amount * mLength, mPos, null);
+            mXProp.set((T) targets.first, mPos[0]);
+            mYProp.set((T) targets.second, mPos[1]);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 0709992..db686a8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -58,6 +58,7 @@
     private final IBinder mToken = new Binder();
     private final IQSTileService mService;
     private final TileServiceManager mServiceManager;
+    private final int mUser;
 
     private boolean mListening;
     private boolean mBound;
@@ -71,6 +72,7 @@
         mServiceManager = host.getTileServices().getTileWrapper(this);
         mService = mServiceManager.getTileService();
         mTile = new Tile(mComponent);
+        mUser = ActivityManager.getCurrentUser();
         try {
             PackageManager pm = mContext.getPackageManager();
             ServiceInfo info = pm.getServiceInfo(mComponent, 0);
@@ -86,6 +88,10 @@
         }
     }
 
+    public int getUser() {
+        return mUser;
+    }
+
     public ComponentName getComponent() {
         return mComponent;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index c4436f4..2aad161 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -85,6 +85,7 @@
         mHandler = handler;
         mIntent = intent;
         mUser = user;
+        if (DEBUG) Log.d(TAG, "Creating " + mIntent + " " + mUser);
     }
 
     public ComponentName getComponent() {
@@ -116,13 +117,13 @@
             if (!checkComponentState()) {
                 return;
             }
-            if (DEBUG) Log.d(TAG, "Binding service " + mIntent);
+            if (DEBUG) Log.d(TAG, "Binding service " + mIntent + " " + mUser);
             mBindTryCount++;
             mContext.bindServiceAsUser(mIntent, this,
                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                     mUser);
         } else {
-            if (DEBUG) Log.d(TAG, "Unbinding service " + mIntent);
+            if (DEBUG) Log.d(TAG, "Unbinding service " + mIntent + " " + mUser);
             // Give it another chance next time it needs to be bound, out of kindness.
             mBindTryCount = 0;
             mWrapper = null;
@@ -350,7 +351,7 @@
 
     @Override
     public void onClick(IBinder iBinder) {
-        if (DEBUG) Log.d(TAG, "onClick " + iBinder);
+        if (DEBUG) Log.d(TAG, "onClick " + iBinder + " " + mUser);
         if (mWrapper == null || !mWrapper.onClick(iBinder)) {
             mClickBinder = iBinder;
             queueMessage(MSG_ON_CLICK);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 54bf68b..efac0fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -35,9 +35,11 @@
 import android.util.Log;
 import android.view.Display;
 import android.view.View;
+import android.widget.Toast;
 
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
+import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SystemUI;
 import com.android.systemui.recents.events.EventBus;
@@ -393,28 +395,35 @@
         boolean screenPinningActive = ssp.isScreenPinningActive();
         boolean isTopTaskHome = topTask != null && SystemServicesProxy.isHomeStack(topTask.stackId);
         if (topTask != null && !isTopTaskHome && !screenPinningActive) {
-            if (sSystemServicesProxy.isSystemUser(currentUser)) {
-                mImpl.dockTopTask(topTask.id, dragMode, stackCreateMode, initialBounds);
-            } else {
-                if (mSystemToUserCallbacks != null) {
-                    IRecentsNonSystemUserCallbacks callbacks =
-                            mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
-                    if (callbacks != null) {
-                        try {
-                            callbacks.dockTopTask(topTask.id, dragMode, stackCreateMode,
-                                    initialBounds);
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "Callback failed", e);
+            if (topTask.isDockable) {
+                if (sSystemServicesProxy.isSystemUser(currentUser)) {
+                    mImpl.dockTopTask(topTask.id, dragMode, stackCreateMode, initialBounds);
+                } else {
+                    if (mSystemToUserCallbacks != null) {
+                        IRecentsNonSystemUserCallbacks callbacks =
+                                mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                        if (callbacks != null) {
+                            try {
+                                callbacks.dockTopTask(topTask.id, dragMode, stackCreateMode,
+                                        initialBounds);
+                            } catch (RemoteException e) {
+                                Log.e(TAG, "Callback failed", e);
+                            }
+                        } else {
+                            Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
                         }
-                    } else {
-                        Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
                     }
                 }
+                mDraggingInRecentsCurrentUser = currentUser;
+                return true;
+            } else {
+                Toast.makeText(mContext, R.string.recents_drag_non_dockable_task_message,
+                        Toast.LENGTH_SHORT).show();
+                return false;
             }
-            mDraggingInRecentsCurrentUser = currentUser;
-            return true;
+        } else {
+            return false;
         }
-        return false;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index ac23f44..3ad2cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -645,7 +645,8 @@
         if (stack != null) {
             stackLayout.initialize(taskStackBounds,
                     TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
-            mDummyStackView.setTasks(stack, false /* notifyStackChanges */);
+            mDummyStackView.setTasks(stack, false /* notifyStackChanges */,
+                    false /* relayoutTaskStack */);
         }
         Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
         if (!taskViewBounds.equals(mLastTaskViewBounds)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 1af5a55..8342de5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -184,7 +184,8 @@
 
         // Update the stack
         mTaskStackView.onResume(isResumingFromVisible);
-        mTaskStackView.setTasks(stack, isResumingFromVisible /* notifyStackChanges */);
+        mTaskStackView.setTasks(stack, isResumingFromVisible /* notifyStackChanges */,
+                true /* relayoutTaskStack */);
 
         if (isResumingFromVisible) {
             // If we are already visible, then restore the background scrim
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 9da8fee..2601cd3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -298,7 +298,7 @@
     /**
      * Sets the stack tasks of this TaskStackView from the given TaskStack.
      */
-    public void setTasks(TaskStack stack, boolean notifyStackChanges) {
+    public void setTasks(TaskStack stack, boolean notifyStackChanges, boolean relayoutTaskStack) {
         boolean isInitialized = mLayoutAlgorithm.isInitialized();
         mStack.setTasks(getContext(), stack.computeAllTasksList(),
                 notifyStackChanges && isInitialized);
@@ -307,15 +307,18 @@
             // measure/layout pass
             updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
             updateToInitialState();
-            relayoutTaskViews(AnimationProps.IMMEDIATE);
 
-            // Rebind all the task views.  This will not trigger new resources to be loaded unless
-            // they have actually changed
-            List<TaskView> taskViews = getTaskViews();
-            int taskViewCount = taskViews.size();
-            for (int i = 0; i < taskViewCount; i++) {
-                TaskView tv = taskViews.get(i);
-                bindTaskView(tv, tv.getTask());
+            if (relayoutTaskStack) {
+                relayoutTaskViews(AnimationProps.IMMEDIATE);
+
+                // Rebind all the task views.  This will not trigger new resources to be loaded
+                // unless they have actually changed
+                List<TaskView> taskViews = getTaskViews();
+                int taskViewCount = taskViews.size();
+                for (int i = 0; i < taskViewCount; i++) {
+                    TaskView tv = taskViews.get(i);
+                    bindTaskView(tv, tv.getTask());
+                }
             }
         }
     }
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 9c6f2c5..d50e67a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -92,6 +92,7 @@
     private KeyguardUserSwitcher mKeyguardUserSwitcher;
     private KeyguardStatusBarView mKeyguardStatusBar;
     protected QSContainer mQsContainer;
+    private DensityContainer mQsDensityContainer;
     private KeyguardStatusView mKeyguardStatusView;
     private TextView mClockView;
     private View mReserveNotificationSpace;
@@ -217,8 +218,8 @@
         super.onFinishInflate();
         mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header);
         mKeyguardStatusView = (KeyguardStatusView) findViewById(R.id.keyguard_status_view);
-        DensityContainer container = (DensityContainer) findViewById(R.id.qs_density_container);
-        container.addInflateListener(new InflateListener() {
+        mQsDensityContainer = (DensityContainer) findViewById(R.id.qs_density_container);
+        mQsDensityContainer.addInflateListener(new InflateListener() {
             @Override
             public void onInflated(View v) {
                 mQsContainer = (QSContainer) v.findViewById(R.id.quick_settings_container);
@@ -2209,7 +2210,7 @@
 
     protected void setVerticalPanelTranslation(float translation) {
         mNotificationStackScroller.setTranslationX(translation);
-        mQsContainer.setTranslationX(translation);
+        mQsDensityContainer.setTranslationX(translation);
     }
 
     protected void updateStackHeight(float stackHeight) {
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 f894a22..5dcd393 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -114,6 +114,7 @@
     private final ManagedProfileController mProfileController;
     private final NextAlarmController mNextAlarmController;
     private View mHeader;
+    private int mCurrentUser;
 
     public QSTileHost(Context context, PhoneStatusBar statusBar,
             BluetoothController bluetooth, LocationController location,
@@ -320,7 +321,8 @@
         }
         if (DEBUG) Log.d(TAG, "Recreating tiles");
         final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
-        if (tileSpecs.equals(mTileSpecs)) return;
+        int currentUser = ActivityManager.getCurrentUser();
+        if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
         for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) {
             if (!tileSpecs.contains(tile.getKey())) {
                 if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey());
@@ -329,15 +331,16 @@
         }
         final LinkedHashMap<String, QSTile<?>> newTiles = new LinkedHashMap<>();
         for (String tileSpec : tileSpecs) {
-            if (mTiles.containsKey(tileSpec)) {
-                QSTile<?> tile = mTiles.get(tileSpec);
+            QSTile<?> tile = mTiles.get(tileSpec);
+            if (tile != null && (!(tile instanceof CustomTile)
+                    || ((CustomTile) tile).getUser() == currentUser)) {
                 if (DEBUG) Log.d(TAG, "Adding " + tile);
                 tile.removeCallbacks();
                 newTiles.put(tileSpec, tile);
             } else {
                 if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
                 try {
-                    QSTile<?> tile = createTile(tileSpec);
+                    tile = createTile(tileSpec);
                     if (tile != null && tile.isAvailable()) {
                         tile.setTileSpec(tileSpec);
                         newTiles.put(tileSpec, tile);
@@ -347,6 +350,7 @@
                 }
             }
         }
+        mCurrentUser = currentUser;
         mTileSpecs.clear();
         mTileSpecs.addAll(tileSpecs);
         mTiles.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 1a557e4..64d8ab2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -45,7 +45,6 @@
     public static final long ANIMATION_DURATION = 220;
     public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR
             = new PathInterpolator(0f, 0, 0.7f, 1f);
-
     private static final float SCRIM_BEHIND_ALPHA = 0.62f;
     private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.45f;
     private static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f;
@@ -60,6 +59,10 @@
     private final UnlockMethodCache mUnlockMethodCache;
     private final View mHeadsUpScrim;
 
+    private float mScrimBehindAlpha = SCRIM_BEHIND_ALPHA;
+    private float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
+    private float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING;
+
     protected boolean mKeyguardShowing;
     private float mFraction;
 
@@ -101,6 +104,19 @@
         scheduleUpdate();
     }
 
+    public void setShowScrimBehind(boolean show) {
+        if (show) {
+            mScrimBehindAlpha = SCRIM_BEHIND_ALPHA;
+            mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
+            mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING;
+        } else {
+            mScrimBehindAlpha = 0;
+            mScrimBehindAlphaKeyguard = 0;
+            mScrimBehindAlphaUnlocking = 0;
+        }
+        scheduleUpdate();
+    }
+
     public void onTrackingStarted() {
         mExpanding = true;
         mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
@@ -229,7 +245,7 @@
             fraction = (float) Math.pow(fraction, 0.8f);
             behindFraction = (float) Math.pow(behindFraction, 0.8f);
             setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA);
-            setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD);
+            setScrimBehindColor(behindFraction * mScrimBehindAlphaKeyguard);
         } else if (mBouncerShowing) {
             setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA);
             setScrimBehindColor(0f);
@@ -237,8 +253,8 @@
             float fraction = Math.max(0, Math.min(mFraction, 1));
             setScrimInFrontColor(0f);
             setScrimBehindColor(fraction
-                    * (SCRIM_BEHIND_ALPHA_KEYGUARD - SCRIM_BEHIND_ALPHA_UNLOCKING)
-                    + SCRIM_BEHIND_ALPHA_UNLOCKING);
+                    * (mScrimBehindAlphaKeyguard - mScrimBehindAlphaUnlocking)
+                    + mScrimBehindAlphaUnlocking);
         }
     }
 
@@ -251,7 +267,7 @@
         } else {
             // woo, special effects
             final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
-            setScrimBehindColor(k * SCRIM_BEHIND_ALPHA);
+            setScrimBehindColor(k * mScrimBehindAlpha);
         }
     }
 
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 4877a378..4e667c6 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -1062,7 +1062,7 @@
               type, kind, norm, size);
     }
     return (jlong)(uintptr_t)rsElementCreate((RsContext)con, (RsDataType)type, (RsDataKind)kind,
-                                             norm, size, true);
+                                             norm, size);
 }
 
 static jlong
@@ -1101,7 +1101,7 @@
     jlong id = (jlong)(uintptr_t)rsElementCreate2((RsContext)con,
                                      (const RsElement *)ids, fieldCount,
                                      nameArray, fieldCount * sizeof(size_t),  sizeArray,
-                                     (const uint32_t *)arraySizes, fieldCount, true);
+                                     (const uint32_t *)arraySizes, fieldCount);
 
     free(ids);
     free(arraySizes);
@@ -1175,7 +1175,7 @@
     }
 
     return (jlong)(uintptr_t)rsTypeCreate((RsContext)con, (RsElement)eid, dimx, dimy, dimz, mips,
-                                          faces, yuv, true);
+                                          faces, yuv);
 }
 
 static void
@@ -1211,7 +1211,7 @@
     }
     return (jlong)(uintptr_t) rsAllocationCreateTyped((RsContext)con, (RsType)type,
                                                       (RsAllocationMipmapControl)mips,
-                                                      (uint32_t)usage, (uintptr_t)pointer, true);
+                                                      (uint32_t)usage, (uintptr_t)pointer);
 }
 
 static void
@@ -1316,7 +1316,7 @@
     const void* ptr = bitmap.getPixels();
     jlong id = (jlong)(uintptr_t)rsAllocationCreateFromBitmap((RsContext)con,
                                                   (RsType)type, (RsAllocationMipmapControl)mip,
-                                                  ptr, bitmap.getSize(), usage, true);
+                                                  ptr, bitmap.getSize(), usage);
     bitmap.unlockPixels();
     return id;
 }
@@ -1332,7 +1332,7 @@
     const void* ptr = bitmap.getPixels();
     jlong id = (jlong)(uintptr_t)rsAllocationCreateTyped((RsContext)con,
                                             (RsType)type, (RsAllocationMipmapControl)mip,
-                                            (uint32_t)usage, (uintptr_t)ptr, true);
+                                            (uint32_t)usage, (uintptr_t)ptr);
     bitmap.unlockPixels();
     return id;
 }
@@ -1348,7 +1348,7 @@
     const void* ptr = bitmap.getPixels();
     jlong id = (jlong)(uintptr_t)rsAllocationCubeCreateFromBitmap((RsContext)con,
                                                       (RsType)type, (RsAllocationMipmapControl)mip,
-                                                      ptr, bitmap.getSize(), usage, true);
+                                                      ptr, bitmap.getSize(), usage);
     bitmap.unlockPixels();
     return id;
 }
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index e3af7e3..3f453dc 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -1051,6 +1051,30 @@
             mCurrentUserId = userId;
         }
 
+        private void putString(final String key, final String str) {
+            Settings.Secure.putStringForUser(mResolver, key, str, mCurrentUserId);
+        }
+
+        private String getString(final String key) {
+            return Settings.Secure.getStringForUser(mResolver, key, mCurrentUserId);
+        }
+
+        private void putInt(final String key, final int value) {
+            Settings.Secure.putIntForUser(mResolver, key, value, mCurrentUserId);
+        }
+
+        private int getInt(final String key, final int defaultValue) {
+            return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mCurrentUserId);
+        }
+
+        private void putBoolean(final String key, final boolean value) {
+            putInt(key, value ? 1 : 0);
+        }
+
+        private boolean getBoolean(final String key, final boolean defaultValue) {
+            return getInt(key, defaultValue ? 1 : 0) == 1;
+        }
+
         public void setCurrentProfileIds(int[] currentProfileIds) {
             synchronized (mLock) {
                 mCurrentProfileIds = currentProfileIds;
@@ -1073,34 +1097,27 @@
         }
 
         public void putSelectedSpellChecker(String sciId) {
-            Settings.Secure.putStringForUser(mResolver,
-                    Settings.Secure.SELECTED_SPELL_CHECKER, sciId, mCurrentUserId);
+            putString(Settings.Secure.SELECTED_SPELL_CHECKER, sciId);
         }
 
         public void putSelectedSpellCheckerSubtype(int hashCode) {
-            Settings.Secure.putStringForUser(mResolver,
-                    Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, String.valueOf(hashCode),
-                    mCurrentUserId);
+            putString(Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, String.valueOf(hashCode));
         }
 
         public void setSpellCheckerEnabled(boolean enabled) {
-            Settings.Secure.putIntForUser(mResolver,
-                    Settings.Secure.SPELL_CHECKER_ENABLED, enabled ? 1 : 0, mCurrentUserId);
+            putBoolean(Settings.Secure.SPELL_CHECKER_ENABLED, enabled);
         }
 
         public String getSelectedSpellChecker() {
-            return Settings.Secure.getStringForUser(mResolver,
-                    Settings.Secure.SELECTED_SPELL_CHECKER, mCurrentUserId);
+            return getString(Settings.Secure.SELECTED_SPELL_CHECKER);
         }
 
         public String getSelectedSpellCheckerSubtype() {
-            return Settings.Secure.getStringForUser(mResolver,
-                    Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, mCurrentUserId);
+            return getString(Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE);
         }
 
         public boolean isSpellCheckerEnabled() {
-            return Settings.Secure.getIntForUser(mResolver,
-                    Settings.Secure.SPELL_CHECKER_ENABLED, 1, mCurrentUserId) == 1;
+            return getBoolean(Settings.Secure.SPELL_CHECKER_ENABLED, true);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0317641..f07c1d0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1899,7 +1899,9 @@
             case SYSTEM_USER_UNLOCK_MSG: {
                 final int userId = msg.arg1;
                 mSystemServiceManager.unlockUser(userId);
-                mRecentTasks.loadUserRecentsLocked(userId);
+                synchronized (ActivityManagerService.this) {
+                    mRecentTasks.loadUserRecentsLocked(userId);
+                }
                 if (userId == UserHandle.USER_SYSTEM) {
                     startPersistentApps(PackageManager.MATCH_ENCRYPTION_UNAWARE);
                 }
@@ -6655,7 +6657,7 @@
         synchronized(this) {
             ActivityStack stack = ActivityRecord.getStackLocked(token);
             if (stack != null) {
-                ActivityRecord.activityResumedLocked(token);
+                stack.activityResumedLocked(token);
             }
         }
         Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 2ea5d15..5219827 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1277,13 +1277,6 @@
         }
     }
 
-    static void activityResumedLocked(IBinder token) {
-        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        if (DEBUG_SAVED_STATE) Slog.i(TAG_STATES, "Resumed activity; dropping state of: " + r);
-        r.icicle = null;
-        r.haveState = false;
-    }
-
     static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
         final ActivityRecord r = ActivityRecord.forTokenLocked(token);
         if (r == null) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e50722a..39895ac5 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1088,6 +1088,13 @@
         mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
     }
 
+    final void activityResumedLocked(IBinder token) {
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        if (DEBUG_SAVED_STATE) Slog.i(TAG_STATES, "Resumed activity; dropping state of: " + r);
+        r.icicle = null;
+        r.haveState = false;
+    }
+
     final void activityStoppedLocked(ActivityRecord r, Bundle icicle,
             PersistableBundle persistentState, CharSequence description) {
         if (r.state != ActivityState.STOPPING) {
@@ -4436,10 +4443,10 @@
                     "Moving to " + (andResume ? "RESUMED" : "PAUSED") + " Relaunching " + r
                     + " callers=" + Debug.getCallers(6));
             r.forceNewConfig = false;
+            mStackSupervisor.activityRelaunchingLocked(r);
             r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
                     !andResume, new Configuration(mService.mConfiguration),
                     new Configuration(r.task.mOverrideConfig), preserveWindow);
-            mStackSupervisor.activityRelaunchingLocked(r);
             // Note: don't need to call pauseIfSleepingLocked() here, because
             // the caller will only pass in 'andResume' if this activity is
             // currently resumed, which implies we aren't sleeping.
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 62275a9..ea85fa1 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -40,6 +40,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.DisplayMetrics;
 import android.util.Slog;
@@ -74,6 +75,7 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
@@ -444,9 +446,9 @@
             // task as having a true root activity.
             rootWasReset = true;
         }
-
         userId = UserHandle.getUserId(info.applicationInfo.uid);
-        mUserSetupComplete = mService.mUserController.isUserSetupCompleteLocked(userId);
+        mUserSetupComplete = Settings.Secure.getIntForUser(mService.mContext.getContentResolver(),
+                USER_SETUP_COMPLETE, 0, userId) != 0;
         if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
             // If the activity itself has requested auto-remove, then just always do it.
             autoRemoveRecents = true;
@@ -1570,6 +1572,7 @@
         pw.print(prefix); pw.print("userId="); pw.print(userId);
                 pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
                 pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
+                pw.print(" mUserSetupComplete="); pw.print(mUserSetupComplete);
                 pw.print(" mCallingPackage="); pw.println(mCallingPackage);
         if (affinity != null || rootAffinity != null) {
             pw.print(prefix); pw.print("affinity="); pw.print(affinity);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index addffd3..4a5df7a 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -24,7 +24,6 @@
 import static android.app.ActivityManager.USER_OP_SUCCESS;
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.os.Process.SYSTEM_UID;
-import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -46,14 +45,11 @@
 import android.app.IStopUserCallback;
 import android.app.IUserSwitchObserver;
 import android.app.KeyguardManager;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
-import android.database.ContentObserver;
-import android.net.Uri;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Bundle;
@@ -71,12 +67,10 @@
 import android.os.UserManagerInternal;
 import android.os.storage.IMountService;
 import android.os.storage.StorageManager;
-import android.provider.Settings;
 import android.util.IntArray;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
 import com.android.internal.R;
@@ -153,34 +147,6 @@
 
     private final LockPatternUtils mLockPatternUtils;
 
-    // Set of users who have completed the set-up process.
-    private final SparseBooleanArray mSetupCompletedUsers = new SparseBooleanArray();
-    private final UserSetupCompleteContentObserver mUserSetupCompleteContentObserver;
-
-    private class UserSetupCompleteContentObserver extends ContentObserver {
-        private final Uri mUserSetupComplete = Settings.Secure.getUriFor(USER_SETUP_COMPLETE);
-
-        public UserSetupCompleteContentObserver(Handler handler) {
-            super(handler);
-        }
-
-        void register(ContentResolver resolver) {
-            resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
-            synchronized (mService) {
-                updateUserSetupCompleteLocked(UserHandle.USER_ALL);
-            }
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri, int userId) {
-            if (mUserSetupComplete.equals(uri)) {
-                synchronized (mService) {
-                    updateUserSetupCompleteLocked(userId);
-                }
-            }
-        }
-    }
-
     UserController(ActivityManagerService service) {
         mService = service;
         mHandler = mService.mHandler;
@@ -190,7 +156,6 @@
         mUserLru.add(UserHandle.USER_SYSTEM);
         mLockPatternUtils = new LockPatternUtils(mService.mContext);
         updateStartedUserArrayLocked();
-        mUserSetupCompleteContentObserver = new UserSetupCompleteContentObserver(mHandler);
     }
 
     void finishUserSwitch(UserState uss) {
@@ -477,7 +442,6 @@
                 mStartedUsers.remove(userId);
                 mUserLru.remove(Integer.valueOf(userId));
                 updateStartedUserArrayLocked();
-                mSetupCompletedUsers.delete(userId);
 
                 mService.onUserStoppedLocked(userId);
                 // Clean up all state and processes associated with the user.
@@ -677,7 +641,6 @@
                 final Integer userIdInt = userId;
                 mUserLru.remove(userIdInt);
                 mUserLru.add(userIdInt);
-                updateUserSetupCompleteLocked(userId);
 
                 if (foreground) {
                     mCurrentUserId = userId;
@@ -892,22 +855,6 @@
         mUserSwitchObservers.finishBroadcast();
     }
 
-    void updateUserSetupCompleteLocked(int userId) {
-        final ContentResolver cr = mService.mContext.getContentResolver();
-        for (int i = mStartedUsers.size() - 1; i >= 0; i--) {
-            int startedUser = mStartedUsers.keyAt(i);
-            if (startedUser == userId || userId == UserHandle.USER_ALL) {
-                final boolean setupComplete =
-                        Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, startedUser) != 0;
-                mSetupCompletedUsers.put(startedUser, setupComplete);
-            }
-        }
-    }
-
-    boolean isUserSetupCompleteLocked(int userId) {
-        return mSetupCompletedUsers.get(userId);
-    }
-
     private void stopBackgroundUsersIfEnforced(int oldUserId) {
         // Never stop system user
         if (oldUserId == UserHandle.USER_SYSTEM) {
@@ -1218,7 +1165,6 @@
 
     void onSystemReady() {
         updateCurrentProfileIdsLocked();
-        mUserSetupCompleteContentObserver.register(mService.mContext.getContentResolver());
     }
 
     /**
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f2a9c2c..d919737 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -58,7 +58,7 @@
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
 import android.media.AudioPort;
-import android.media.AudioRecordConfiguration;
+import android.media.AudioRecordingConfiguration;
 import android.media.AudioRoutesInfo;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioRoutesObserver;
@@ -6287,8 +6287,8 @@
         mRecordMonitor.unregisterRecordingCallback(rcdb);
     }
 
-    public AudioRecordConfiguration[] getActiveRecordConfigurations() {
-        return mRecordMonitor.getActiveRecordConfigurations();
+    public AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
+        return mRecordMonitor.getActiveRecordingConfigurations();
     }
 
     //======================
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 86dcd0f..7a085e1 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -18,7 +18,7 @@
 
 import android.media.AudioFormat;
 import android.media.AudioManager;
-import android.media.AudioRecordConfiguration;
+import android.media.AudioRecordingConfiguration;
 import android.media.AudioSystem;
 import android.media.IRecordingConfigDispatcher;
 import android.media.MediaRecorder;
@@ -39,8 +39,8 @@
 
     private ArrayList<RecMonitorClient> mClients = new ArrayList<RecMonitorClient>();
 
-    private HashMap<Integer, AudioRecordConfiguration> mRecordConfigs =
-            new HashMap<Integer, AudioRecordConfiguration>();
+    private HashMap<Integer, AudioRecordingConfiguration> mRecordConfigs =
+            new HashMap<Integer, AudioRecordingConfiguration>();
 
     RecordingActivityMonitor() {
         RecMonitorClient.sMonitor = this;
@@ -54,7 +54,7 @@
         if (MediaRecorder.isSystemOnlyAudioSource(source)) {
             return;
         }
-        final AudioRecordConfiguration[] configs =
+        final AudioRecordingConfiguration[] configs =
                 updateSnapshot(event, session, source, recordingInfo);
         if (configs != null){
             synchronized(mClients) {
@@ -104,9 +104,9 @@
         }
     }
 
-    AudioRecordConfiguration[] getActiveRecordConfigurations() {
+    AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
         synchronized(mRecordConfigs) {
-            return mRecordConfigs.values().toArray(new AudioRecordConfiguration[0]);
+            return mRecordConfigs.values().toArray(new AudioRecordingConfiguration[0]);
         }
     }
 
@@ -121,10 +121,10 @@
      * @return null if the list of active recording sessions has not been modified, an array
      *     with the current active configurations otherwise.
      */
-    private AudioRecordConfiguration[] updateSnapshot(int event, int session, int source,
+    private AudioRecordingConfiguration[] updateSnapshot(int event, int session, int source,
             int[] recordingInfo) {
         final boolean configChanged;
-        final AudioRecordConfiguration[] configs;
+        final AudioRecordingConfiguration[] configs;
         synchronized(mRecordConfigs) {
             switch (event) {
             case AudioManager.RECORD_CONFIG_EVENT_STOP:
@@ -147,8 +147,8 @@
                 final int patchHandle = recordingInfo[6];
                 final Integer sessionKey = new Integer(session);
                 if (mRecordConfigs.containsKey(sessionKey)) {
-                    final AudioRecordConfiguration updatedConfig =
-                            new AudioRecordConfiguration(session, source,
+                    final AudioRecordingConfiguration updatedConfig =
+                            new AudioRecordingConfiguration(session, source,
                                     clientFormat, deviceFormat, patchHandle);
                     if (updatedConfig.equals(mRecordConfigs.get(sessionKey))) {
                         configChanged = false;
@@ -160,7 +160,7 @@
                     }
                 } else {
                     mRecordConfigs.put(sessionKey,
-                            new AudioRecordConfiguration(session, source,
+                            new AudioRecordingConfiguration(session, source,
                                     clientFormat, deviceFormat, patchHandle));
                     configChanged = true;
                 }
@@ -171,7 +171,7 @@
                 configChanged = false;
             }
             if (configChanged) {
-                configs = mRecordConfigs.values().toArray(new AudioRecordConfiguration[0]);
+                configs = mRecordConfigs.values().toArray(new AudioRecordingConfiguration[0]);
             } else {
                 configs = null;
             }
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 9820a12..c19b51f 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import android.app.AutomaticZenRule;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -44,7 +45,6 @@
 
 public class ConditionProviders extends ManagedServices {
     private final ArrayList<ConditionRecord> mRecords = new ArrayList<>();
-    private final ArrayMap<IBinder, IConditionListener> mListeners = new ArrayMap<>();
     private final ArraySet<String> mSystemConditionProviderNames;
     private final ArraySet<SystemConditionProviderService> mSystemConditionProviders
             = new ArraySet<>();
@@ -103,12 +103,6 @@
                 }
             }
         }
-        if (filter == null) {
-            pw.print("    mListeners("); pw.print(mListeners.size()); pw.println("):");
-            for (int i = 0; i < mListeners.size(); i++) {
-                pw.print("      "); pw.println(mListeners.keyAt(i));
-            }
-        }
         pw.print("    mSystemConditionProviders: "); pw.println(mSystemConditionProviderNames);
         for (int i = 0; i < mSystemConditionProviders.size(); i++) {
             mSystemConditionProviders.valueAt(i).dump(pw, filter);
@@ -173,16 +167,12 @@
         }
     }
 
-    private Condition[] validateConditions(String pkg, Condition[] conditions) {
+    private Condition[] removeDuplicateConditions(String pkg, Condition[] conditions) {
         if (conditions == null || conditions.length == 0) return null;
         final int N = conditions.length;
         final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N);
         for (int i = 0; i < N; i++) {
             final Uri id = conditions[i].id;
-            if (!Condition.isValidId(id, pkg)) {
-                Slog.w(TAG, "Ignoring condition from " + pkg + " for invalid id: " + id);
-                continue;
-            }
             if (valid.containsKey(id)) {
                 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id);
                 continue;
@@ -219,16 +209,9 @@
         synchronized(mMutex) {
             if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
                     + (conditions == null ? null : Arrays.asList(conditions)));
-            conditions = validateConditions(pkg, conditions);
+            conditions = removeDuplicateConditions(pkg, conditions);
             if (conditions == null || conditions.length == 0) return;
             final int N = conditions.length;
-            for (IConditionListener listener : mListeners.values()) {
-                try {
-                    listener.onConditionsReceived(conditions);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Error sending conditions to listener " + listener, e);
-                }
-            }
             for (int i = 0; i < N; i++) {
                 final Condition c = conditions[i];
                 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 881e8f0..24ffe1f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,21 +16,21 @@
 
 package com.android.server.notification;
 
-import static android.service.notification.NotificationAssistantService.REASON_APP_CANCEL;
-import static android.service.notification.NotificationAssistantService.REASON_APP_CANCEL_ALL;
-import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_CANCEL;
-import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_CANCEL_ALL;
-import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_CLICK;
-import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_ERROR;
-import static android.service.notification.NotificationAssistantService.REASON_GROUP_OPTIMIZATION;
-import static android.service.notification.NotificationAssistantService.REASON_GROUP_SUMMARY_CANCELED;
-import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL;
-import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL_ALL;
-import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_BANNED;
-import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_CHANGED;
-import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_SUSPENDED;
-import static android.service.notification.NotificationAssistantService.REASON_PROFILE_TURNED_OFF;
-import static android.service.notification.NotificationAssistantService.REASON_USER_STOPPED;
+import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL;
+import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL_ALL;
+import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL;
+import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL_ALL;
+import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CLICK;
+import static android.service.notification.NotificationRankerService.REASON_DELEGATE_ERROR;
+import static android.service.notification.NotificationRankerService.REASON_GROUP_OPTIMIZATION;
+import static android.service.notification.NotificationRankerService.REASON_GROUP_SUMMARY_CANCELED;
+import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL;
+import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL_ALL;
+import static android.service.notification.NotificationRankerService.REASON_PACKAGE_BANNED;
+import static android.service.notification.NotificationRankerService.REASON_PACKAGE_CHANGED;
+import static android.service.notification.NotificationRankerService.REASON_PACKAGE_SUSPENDED;
+import static android.service.notification.NotificationRankerService.REASON_PROFILE_TURNED_OFF;
+import static android.service.notification.NotificationRankerService.REASON_USER_STOPPED;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
@@ -100,7 +100,7 @@
 import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
 import android.service.notification.IStatusBarNotificationHolder;
-import android.service.notification.NotificationAssistantService;
+import android.service.notification.NotificationRankerService;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationRankingUpdate;
 import android.service.notification.StatusBarNotification;
@@ -224,8 +224,6 @@
     private WorkerHandler mHandler;
     private final HandlerThread mRankingThread = new HandlerThread("ranker",
             Process.THREAD_PRIORITY_BACKGROUND);
-    private final HandlerThread mAssistantThread = new HandlerThread("assistant",
-            Process.THREAD_PRIORITY_BACKGROUND);
 
     private Light mNotificationLight;
     Light mAttentionLight;
@@ -291,14 +289,13 @@
 
     private final UserProfiles mUserProfiles = new UserProfiles();
     private NotificationListeners mListeners;
-    private NotificationAssistant mAssistant;
+    private NotificationRanker mRankerServices;
     private ConditionProviders mConditionProviders;
     private NotificationUsageStats mUsageStats;
 
     private static final int MY_UID = Process.myUid();
     private static final int MY_PID = Process.myPid();
     private RankingHandler mRankingHandler;
-    private Handler mAssistantHandler;
 
     private static class Archive {
         final int mBufferSize;
@@ -756,7 +753,7 @@
                     }
                 }
                 mListeners.onPackagesChanged(queryReplace, pkgList);
-                mAssistant.onPackagesChanged(queryReplace, pkgList);
+                mRankerServices.onPackagesChanged(queryReplace, pkgList);
                 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
                 mRankingHelper.onPackagesChanged(queryReplace, pkgList);
             }
@@ -805,7 +802,7 @@
                 // Refresh managed services
                 mConditionProviders.onUserSwitched(user);
                 mListeners.onUserSwitched(user);
-                mAssistant.onUserSwitched(user);
+                mRankerServices.onUserSwitched(user);
                 mZenModeHelper.onUserSwitched(user);
             } else if (action.equals(Intent.ACTION_USER_ADDED)) {
                 mUserProfiles.updateCache(context);
@@ -816,7 +813,7 @@
                 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
                 mConditionProviders.onUserUnlocked(user);
                 mListeners.onUserUnlocked(user);
-                mAssistant.onUserUnlocked(user);
+                mRankerServices.onUserUnlocked(user);
                 mZenModeHelper.onUserUnlocked(user);
             }
         }
@@ -892,7 +889,6 @@
 
         mHandler = new WorkerHandler();
         mRankingThread.start();
-        mAssistantThread.start();
         String[] extractorNames;
         try {
             extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
@@ -901,7 +897,6 @@
         }
         mUsageStats = new NotificationUsageStats(getContext());
         mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
-        mAssistantHandler = new Handler(mAssistantThread.getLooper());
         mRankingHelper = new RankingHelper(getContext(),
                 mRankingHandler,
                 mUsageStats,
@@ -937,7 +932,7 @@
         importOldBlockDb();
 
         mListeners = new NotificationListeners();
-        mAssistant = new NotificationAssistant();
+        mRankerServices = new NotificationRanker();
         mStatusBar = getLocalService(StatusBarManagerInternal.class);
         mStatusBar.setNotificationDelegate(mNotificationDelegate);
 
@@ -1060,7 +1055,7 @@
             // bind to listener services.
             mSettingsObserver.observe();
             mListeners.onBootPhaseAppsCanStart();
-            mAssistant.onBootPhaseAppsCanStart();
+            mRankerServices.onBootPhaseAppsCanStart();
             mConditionProviders.onBootPhaseAppsCanStart();
         }
     }
@@ -1415,17 +1410,30 @@
          */
         @Override
         public void registerListener(final INotificationListener listener,
-                final ComponentName component, final int userid) {
+                final ComponentName component, final int userid, boolean asRanker) {
             enforceSystemOrSystemUI("INotificationManager.registerListener");
-            mListeners.registerService(listener, component, userid);
+            if (asRanker) {
+                mRankerServices.registerService(listener, component, userid);
+            } else {
+                mListeners.registerService(listener, component, userid);
+            }
         }
 
         /**
          * Remove a listener binder directly
          */
         @Override
-        public void unregisterListener(INotificationListener listener, int userid) {
-            mListeners.unregisterService(listener, userid);
+        public void unregisterListener(INotificationListener token, int userid) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                if(mRankerServices.checkServiceTokenLocked(token) != null) {
+                    mRankerServices.unregisterService(token, userid);
+                } else {
+                    mListeners.unregisterService(token, userid);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
 
         /**
@@ -1478,8 +1486,8 @@
             checkCallerIsSystemOrSameApp(component.getPackageName());
             long identity = Binder.clearCallingIdentity();
             try {
-                ManagedServices manager = mAssistant.isComponentEnabledForCurrentProfiles(component)
-                        ? mAssistant
+                ManagedServices manager = mRankerServices.isComponentEnabledForCurrentProfiles(component)
+                        ? mRankerServices
                         : mListeners;
                 manager.setComponentState(component, true);
             } finally {
@@ -1972,7 +1980,7 @@
         }
 
         @Override
-        public void setImportanceFromAssistant(INotificationListener token, String key,
+        public void setImportanceFromRankerService(INotificationListener token, String key,
                 int importance, CharSequence explanation) throws RemoteException {
             if (importance == IMPORTANCE_NONE) {
                 throw new IllegalArgumentException("blocking not allowed: key=" + key);
@@ -1980,7 +1988,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationList) {
-                    mAssistant.checkServiceTokenLocked(token);
+                    mRankerServices.checkServiceTokenLocked(token);
                     NotificationRecord n = mNotificationsByKey.get(key);
                     n.setImportance(importance, explanation);
                     mRankingHandler.requestSort();
@@ -2130,8 +2138,8 @@
                     pw.print(listener.component);
                 }
                 pw.println(')');
-                pw.println("\n  Notification assistant:");
-                mAssistant.dump(pw, filter);
+                pw.println("\n  Notification ranker services:");
+                mRankerServices.dump(pw, filter);
             }
             pw.println("\n  Policy access:");
             pw.print("    mPolicyAccess: "); pw.println(mPolicyAccess);
@@ -2358,9 +2366,9 @@
                     }
                 }
 
-                // tell the assistant about the notification
-                if (mAssistant.isEnabled()) {
-                    mAssistant.onNotificationEnqueued(r);
+                // tell the ranker service about the notification
+                if (mRankerServices.isEnabled()) {
+                    mRankerServices.onNotificationEnqueued(r);
                     // TODO delay the code below here for 100ms or until there is an answer
                 }
 
@@ -3477,21 +3485,21 @@
         }
     }
 
-    public class NotificationAssistant extends ManagedServices {
+    public class NotificationRanker extends ManagedServices {
 
-        public NotificationAssistant() {
+        public NotificationRanker() {
             super(getContext(), mHandler, mNotificationList, mUserProfiles);
         }
 
         @Override
         protected Config getConfig() {
             Config c = new Config();
-            c.caption = "notification assistant";
-            c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
-            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
-            c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
+            c.caption = "notification ranker service";
+            c.serviceInterface = NotificationRankerService.SERVICE_INTERFACE;
+            c.secureSettingName = null;
+            c.bindPermission = Manifest.permission.BIND_NOTIFICATION_RANKER_SERVICE;
             c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
-            c.clientLabel = R.string.notification_assistant_binding_label;
+            c.clientLabel = R.string.notification_ranker_binding_label;
             return c;
         }
 
@@ -3519,10 +3527,10 @@
             final StatusBarNotification sbn = r.sbn;
             TrimCache trimCache = new TrimCache(sbn);
 
-            // mServices is the list inside ManagedServices of all the assistants,
+            // mServices is the list inside ManagedServices of all the rankers,
             // There should be only one, but it's a list, so while we enforce
             // singularity elsewhere, we keep it general here, to avoid surprises.
-            for (final ManagedServiceInfo info : NotificationAssistant.this.mServices) {
+            for (final ManagedServiceInfo info : NotificationRanker.this.mServices) {
                 boolean sbnVisible = isVisibleToListener(sbn, info);
                 if (!sbnVisible) {
                     continue;
@@ -3531,7 +3539,7 @@
                 final int importance = r.getImportance();
                 final boolean fromUser = r.isImportanceFromUser();
                 final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
-                mAssistantHandler.post(new Runnable() {
+                mHandler.post(new Runnable() {
                     @Override
                     public void run() {
                         notifyEnqueued(info, sbnToPost, importance, fromUser);
@@ -3542,12 +3550,12 @@
 
         private void notifyEnqueued(final ManagedServiceInfo info,
                 final StatusBarNotification sbn, int importance, boolean fromUser) {
-            final INotificationListener assistant = (INotificationListener) info.service;
+            final INotificationListener ranker = (INotificationListener) info.service;
             StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
             try {
-                assistant.onNotificationEnqueued(sbnHolder, importance, fromUser);
+                ranker.onNotificationEnqueued(sbnHolder, importance, fromUser);
             } catch (RemoteException ex) {
-                Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+                Log.e(TAG, "unable to notify ranker (enqueued): " + ranker, ex);
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 93dcc72..206a143 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -166,6 +166,10 @@
         mInstaller.execute("rmpackagedir", packageDir);
     }
 
+    public void rmProfiles(String pkgName) throws InstallerException {
+        mInstaller.execute("rmprofiles", pkgName);
+    }
+
     public void createUserConfig(int userid) throws InstallerException {
         mInstaller.execute("mkuserconfig", userid);
     }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 8d75f60..902a3d9 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.pm;
 
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.Context;
@@ -27,8 +29,12 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutServiceInternal;
+import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
 import android.content.pm.UserInfo;
 import android.graphics.Rect;
 import android.net.Uri;
@@ -44,15 +50,16 @@
 import android.util.Slog;
 
 import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
 import java.util.List;
 
 /**
  * Service that manages requests and callbacks for launchers that support
- * managed profiles. 
+ * managed profiles.
  */
-
 public class LauncherAppsService extends SystemService {
 
     private final LauncherAppsImpl mLauncherAppsImpl;
@@ -67,21 +74,25 @@
         publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl);
     }
 
-    class LauncherAppsImpl extends ILauncherApps.Stub {
+    static class LauncherAppsImpl extends ILauncherApps.Stub {
         private static final boolean DEBUG = false;
         private static final String TAG = "LauncherAppsService";
         private final Context mContext;
         private final PackageManager mPm;
         private final UserManager mUm;
+        private final ShortcutServiceInternal mShortcutServiceInternal;
         private final PackageCallbackList<IOnAppsChangedListener> mListeners
                 = new PackageCallbackList<IOnAppsChangedListener>();
 
-        private MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+        private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
 
         public LauncherAppsImpl(Context context) {
             mContext = context;
             mPm = mContext.getPackageManager();
             mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+            mShortcutServiceInternal = Preconditions.checkNotNull(
+                    LocalServices.getService(ShortcutServiceInternal.class));
+            mShortcutServiceInternal.addListener(mPackageMonitor);
         }
 
         /*
@@ -174,6 +185,20 @@
             }
         }
 
+        private void verifyCallingPackage(String callingPackage) {
+            int packageUid = -1;
+            try {
+                packageUid = mPm.getPackageUid(callingPackage,
+                        PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+                                | PackageManager.MATCH_UNINSTALLED_PACKAGES);
+            } catch (NameNotFoundException e) {
+                Log.e(TAG, "Package not found: " + callingPackage);
+            }
+            if (packageUid != Binder.getCallingUid()) {
+                throw new SecurityException("Calling package name mismatch");
+            }
+        }
+
         /**
          * Checks if the user is enabled.
          */
@@ -264,6 +289,57 @@
             }
         }
 
+        private void enforceShortcutPermission(UserHandle user) {
+            ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
+            // STOPSHIP Implement it
+        }
+
+        @Override
+        public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
+                String packageName, ComponentName componentName, int flags, UserHandle user)
+                throws RemoteException {
+            enforceShortcutPermission(user);
+            verifyCallingPackage(callingPackage);
+
+            return new ParceledListSlice<>(
+                    mShortcutServiceInternal.getShortcuts(callingPackage, changedSince, packageName,
+                    componentName, flags, user.getIdentifier()));
+        }
+
+        @Override
+        public ParceledListSlice getShortcutInfo(String callingPackage, String packageName,
+                List<String> ids, UserHandle user) throws RemoteException {
+            enforceShortcutPermission(user);
+            verifyCallingPackage(callingPackage);
+
+            return new ParceledListSlice<>(
+                    mShortcutServiceInternal.getShortcutInfo(callingPackage, packageName,
+                    ids, user.getIdentifier()));
+        }
+
+        @Override
+        public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
+                UserHandle user) throws RemoteException {
+            enforceShortcutPermission(user);
+            verifyCallingPackage(callingPackage);
+
+            mShortcutServiceInternal.pinShortcuts(callingPackage, packageName,
+                    ids, user.getIdentifier());
+        }
+
+        @Override
+        public void startShortcut(String callingPackage, ShortcutInfo shortcut, Rect sourceBounds,
+                Bundle startActivityOptions, UserHandle user) throws RemoteException {
+            enforceShortcutPermission(user);
+            verifyCallingPackage(callingPackage);
+
+            final Intent intent = mShortcutServiceInternal.createShortcutIntent(callingPackage,
+                    shortcut, user.getIdentifier());
+            // TODO
+            Slog.e(TAG, "startShortcut() not implemented yet, but the intent is " + intent);
+            throw new RuntimeException("not implemented yet");
+        }
+
         @Override
         public boolean isActivityEnabled(ComponentName component, UserHandle user)
                 throws RemoteException {
@@ -355,7 +431,7 @@
         }
 
 
-        private class MyPackageMonitor extends PackageMonitor {
+        private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
 
             /** Checks if user is a profile of or same as listeningUser.
               * and the user is enabled. */
@@ -390,6 +466,8 @@
                 }
             }
 
+            // TODO Simplify with lambdas.
+
             @Override
             public void onPackageAdded(String packageName, int uid) {
                 UserHandle user = new UserHandle(getChangingUserId());
@@ -523,6 +601,25 @@
                 super.onPackagesUnsuspended(packages);
             }
 
+            @Override
+            public void onShortcutChanged(@NonNull String packageName,
+                    @NonNull List<ShortcutInfo> shortcuts, @UserIdInt int userId) {
+                final UserHandle user = UserHandle.of(userId);
+
+                final int n = mListeners.beginBroadcast();
+                for (int i = 0; i < n; i++) {
+                    IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+                    UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+                    if (!isEnabledProfileOf(user, listeningUser, "onShortcutChanged")) continue;
+                    try {
+                        listener.onShortcutChanged(user, packageName,
+                                new ParceledListSlice<>(shortcuts));
+                    } catch (RemoteException re) {
+                        Slog.d(TAG, "Callback failed ", re);
+                    }
+                }
+                mListeners.finishBroadcast();
+            }
         }
 
         class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index c9613b4..561682c 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -191,7 +191,6 @@
                         throw new IllegalStateException("Invalid dexopt:" + dexoptNeeded);
                 }
 
-
                 Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
                         + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
                         + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8d4c9e5..e180e050 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4443,6 +4443,13 @@
     }
 
     @Override
+    public List<String> getAllPackages() {
+        synchronized (mPackages) {
+            return new ArrayList<String>(mPackages.keySet());
+        }
+    }
+
+    @Override
     public String[] getPackagesForUid(int uid) {
         uid = UserHandle.getAppId(uid);
         // reader
@@ -15147,6 +15154,16 @@
     }
 
     @Override
+    public void clearApplicationProfileData(String packageName) {
+        enforceSystemOrRoot("Only the system can clear all profile data");
+        try {
+            mInstaller.rmProfiles(packageName);
+        } catch (InstallerException ex) {
+            Log.e(TAG, "Could not clear profile data of package " + packageName);
+        }
+    }
+
+    @Override
     public void clearApplicationUserData(final String packageName,
             final IPackageDataObserver observer, final int userId) {
         mContext.enforceCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index ccbd823..28d34a8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -233,17 +233,30 @@
         boolean useJitProfiles = false;
         boolean extractOnly = false;
         boolean forceCompilation = false;
+        boolean allPackages = false;
+        boolean clearProfileData = false;
         String compilationMode = "default";
 
         String opt;
         while ((opt = getNextOption()) != null) {
             switch (opt) {
-                case "-m":
-                    compilationMode = getNextArgRequired();
+                case "-a":
+                    allPackages = true;
+                    break;
+                case "-c":
+                    clearProfileData = true;
                     break;
                 case "-f":
                     forceCompilation = true;
                     break;
+                case "-m":
+                    compilationMode = getNextArgRequired();
+                    break;
+                case "--reset":
+                    forceCompilation = true;
+                    clearProfileData = true;
+                    compilationMode = "extract";
+                    break;
                 default:
                     pw.println("Error: Unknown option: " + opt);
                     return 1;
@@ -255,7 +268,7 @@
                 useJitProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
                 extractOnly = false;
                 break;
-            case "all":
+            case "full":
                 useJitProfiles = false;
                 extractOnly = false;
                 break;
@@ -272,19 +285,49 @@
                 return 1;
         }
 
-        String packageName = getNextArg();
-        if (packageName == null) {
-            pw.println("Error: package name not specified");
-            return 1;
+        List<String> packageNames = null;
+        if (allPackages) {
+            packageNames = mInterface.getAllPackages();
+        } else {
+            String packageName = getNextArg();
+            if (packageName == null) {
+                pw.println("Error: package name not specified");
+                return 1;
+            }
+            packageNames = Collections.singletonList(packageName);
         }
 
-        boolean success = mInterface.performDexOpt(packageName, null /* instructionSet */,
-                useJitProfiles, extractOnly, forceCompilation);
-        if (success) {
+        List<String> failedPackages = new ArrayList<>();
+        for (String packageName : packageNames) {
+            if (clearProfileData) {
+                mInterface.clearApplicationProfileData(packageName);
+            }
+
+            boolean result = mInterface.performDexOpt(packageName, null /* instructionSet */,
+                        useJitProfiles, extractOnly, forceCompilation);
+            if (!result) {
+                failedPackages.add(packageName);
+            }
+        }
+
+        if (failedPackages.isEmpty()) {
             pw.println("Success");
             return 0;
+        } else if (failedPackages.size() == 1) {
+            pw.println("Failure: package " + failedPackages.get(0) + " could not be compiled");
+            return 1;
         } else {
-            pw.println("Failure: package " + packageName + " could not be compiled");
+            pw.print("Failure: the following packages could not be compiled: ");
+            boolean is_first = true;
+            for (String packageName : failedPackages) {
+                if (is_first) {
+                    is_first = false;
+                } else {
+                    pw.print(", ");
+                }
+                pw.print(packageName);
+            }
+            pw.println();
             return 1;
         }
     }
@@ -1135,12 +1178,17 @@
         pw.println("  help");
         pw.println("    Print this help text.");
         pw.println("");
-        pw.println("  compile [-m MODE] [-f] TARGET-PACKAGE");
-        pw.println("    Trigger compilation of TARGET-PACKAGE.");
+        pw.println("  compile [-m MODE] [-f] [-c] [--reset] (-a | TARGET-PACKAGE)");
+        pw.println("    Trigger compilation of TARGET-PACKAGE or all packages if \"-a\".");
         pw.println("    Options:");
-        pw.println("      -m: select compilation mode");
-        pw.println("          MODE can be one of \"default\", \"all\", \"profile\", and \"extract\"");
+        pw.println("      -a: compile all packages");
+        pw.println("      -c: clear profile data before compiling");
         pw.println("      -f: force compilation even if not needed");
+        pw.println("      -m: select compilation mode");
+        pw.println("          MODE can be one of \"default\", \"full\", \"profile\"," +
+                   " and \"extract\"");
+        pw.println("      --reset: restore package to its post-install state");
+        pw.println("          shorthand for \"-c -f -m extract\"");
         pw.println("  list features");
         pw.println("    Prints all features of the system.");
         pw.println("  list instrumentation [-f] [TARGET-PACKAGE]");
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
new file mode 100644
index 0000000..5382ff1
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -0,0 +1,1529 @@
+/*
+ * 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.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IShortcutService;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutServiceInternal;
+import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
+import android.graphics.drawable.Icon;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.text.format.Time;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * TODO:
+ * - Make save async
+ *
+ * - Add Bitmap support
+ *
+ * - Implement updateShortcuts
+ *
+ * - Listen to PACKAGE_*, remove orphan info, update timestamp for icon res
+ *
+ * - Pinned per each launcher package (multiple launchers)
+ *
+ * - Dev option to reset all counts for QA (for now use "adb shell cmd shortcut reset-throttling")
+ *
+ * - Load config from settings
+ */
+public class ShortcutService extends IShortcutService.Stub {
+    private static final String TAG = "ShortcutService";
+
+    private static final boolean DEBUG = true; // STOPSHIP if true
+    private static final boolean DEBUG_LOAD = true; // STOPSHIP if true
+
+    private static final int DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
+    private static final int DEFAULT_MAX_DAILY_UPDATES = 10;
+    private static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
+
+    private static final int SAVE_DELAY_MS = 5000; // in milliseconds.
+
+    @VisibleForTesting
+    static final String FILENAME_BASE_STATE = "shortcut_service.xml";
+
+    @VisibleForTesting
+    static final String DIRECTORY_PER_USER = "shortcut_service";
+
+    @VisibleForTesting
+    static final String FILENAME_USER_PACKAGES = "shortcuts.xml";
+
+    private static final String DIRECTORY_BITMAPS = "bitmaps";
+
+    private static final String TAG_ROOT = "root";
+    private static final String TAG_LAST_RESET_TIME = "last_reset_time";
+    private static final String ATTR_VALUE = "value";
+
+    private final Context mContext;
+
+    private final Object mLock = new Object();
+
+    private final Handler mHandler;
+
+    @GuardedBy("mLock")
+    private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
+
+    @GuardedBy("mLock")
+    private long mRawLastResetTime;
+
+    /**
+     * All the information relevant to shortcuts from a single package (per-user).
+     *
+     * TODO Move the persisting code to this class.
+     */
+    private static class PackageShortcuts {
+        /**
+         * All the shortcuts from the package, keyed on IDs.
+         */
+        final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
+
+        /**
+         * # of dynamic shortcuts.
+         */
+        private int mDynamicShortcutCount = 0;
+
+        /**
+         * # of times the package has called rate-limited APIs.
+         */
+        private int mApiCallCountInner;
+
+        /**
+         * When {@link #mApiCallCountInner} was reset last time.
+         */
+        private long mLastResetTime;
+
+        /**
+         * @return the all shortcuts.  Note DO NOT add/remove or touch the flags of the result
+         * directly, which would cause {@link #mDynamicShortcutCount} to be out of sync.
+         */
+        @GuardedBy("mLock")
+        public ArrayMap<String, ShortcutInfo> getShortcuts() {
+            return mShortcuts;
+        }
+
+        /**
+         * Add a shortcut, or update one with the same ID, with taking over existing flags.
+         *
+         * It checks the max number of dynamic shortcuts.
+         */
+        @GuardedBy("mLock")
+        public void updateShortcutWithCapping(@NonNull ShortcutService s,
+                @NonNull ShortcutInfo newShortcut) {
+            final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
+
+            int oldFlags = 0;
+            int newDynamicCount = mDynamicShortcutCount;
+
+            if (oldShortcut != null) {
+                oldFlags = oldShortcut.getFlags();
+                if (oldShortcut.isDynamic()) {
+                    newDynamicCount--;
+                }
+            }
+            if (newShortcut.isDynamic()) {
+                newDynamicCount++;
+            }
+            // Make sure there's still room.
+            s.enforceMaxDynamicShortcuts(newDynamicCount);
+
+            // Okay, make it dynamic and add.
+            newShortcut.addFlags(oldFlags);
+
+            mShortcuts.put(newShortcut.getId(), newShortcut);
+            mDynamicShortcutCount = newDynamicCount;
+        }
+
+        @GuardedBy("mLock")
+        public void deleteAllDynamicShortcuts() {
+            ArrayList<String> removeList = null; // Lazily initialize.
+
+            for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+                final ShortcutInfo si = mShortcuts.valueAt(i);
+
+                if (!si.isDynamic()) {
+                    continue;
+                }
+                if (si.isPinned()) {
+                    // Still pinned, so don't remove; just make it non-dynamic.
+                    si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+                } else {
+                    if (removeList == null) {
+                        removeList = new ArrayList<>();
+                    }
+                    removeList.add(si.getId());
+                }
+            }
+            if (removeList != null) {
+                for (int i = removeList.size() - 1 ; i >= 0; i--) {
+                    mShortcuts.remove(removeList.get(i));
+                }
+            }
+            mDynamicShortcutCount = 0;
+        }
+
+        @GuardedBy("mLock")
+        public void deleteDynamicWithId(@NonNull String shortcutId) {
+            final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
+
+            if (oldShortcut == null) {
+                return;
+            }
+            if (oldShortcut.isDynamic()) {
+                mDynamicShortcutCount--;
+            }
+            if (oldShortcut.isPinned()) {
+                oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+            } else {
+                mShortcuts.remove(shortcutId);
+            }
+        }
+
+        @GuardedBy("mLock")
+        public void pinAll(List<String> shortcutIds) {
+            for (int i = shortcutIds.size() - 1; i >= 0; i--) {
+                final ShortcutInfo shortcut = mShortcuts.get(shortcutIds.get(i));
+                if (shortcut != null) {
+                    shortcut.addFlags(ShortcutInfo.FLAG_PINNED);
+                }
+            }
+        }
+
+        /**
+         * Number of calls that the caller has made, since the last reset.
+         */
+        @GuardedBy("mLock")
+        public int getApiCallCount(@NonNull ShortcutService s) {
+            final long last = s.getLastResetTimeLocked();
+
+            // If not reset yet, then reset.
+            if (mLastResetTime < last) {
+                mApiCallCountInner = 0;
+                mLastResetTime = last;
+            }
+            return mApiCallCountInner;
+        }
+
+        /**
+         * If the caller app hasn't been throttled yet, increment {@link #mApiCallCountInner}
+         * and return true.  Otherwise just return false.
+         */
+        @GuardedBy("mLock")
+        public boolean tryApiCall(@NonNull ShortcutService s) {
+            if (getApiCallCount(s) >= s.mMaxDailyUpdates) {
+                return false;
+            }
+            mApiCallCountInner++;
+            return true;
+        }
+
+        @GuardedBy("mLock")
+        public void resetRateLimitingForCommandLine() {
+            mApiCallCountInner = 0;
+            mLastResetTime = 0;
+        }
+
+        /**
+         * Find all shortcuts that match {@code query}.
+         */
+        @GuardedBy("mLock")
+        public void findAll(@NonNull List<ShortcutInfo> result,
+                @Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
+            for (int i = 0; i < mShortcuts.size(); i++) {
+                final ShortcutInfo si = mShortcuts.valueAt(i);
+                if (query == null || query.test(si)) {
+                    result.add(si.clone(cloneFlag));
+                }
+            }
+        }
+    }
+
+    /**
+     * User ID -> package name -> list of ShortcutInfos.
+     */
+    @GuardedBy("mLock")
+    private final SparseArray<ArrayMap<String, PackageShortcuts>> mShortcuts =
+            new SparseArray<>();
+
+    /**
+     * Max number of dynamic shortcuts that each application can have at a time.
+     */
+    @GuardedBy("mLock")
+    private int mMaxDynamicShortcuts;
+
+    /**
+     * Max number of updating API calls that each application can make a day.
+     */
+    @GuardedBy("mLock")
+    private int mMaxDailyUpdates;
+
+    /**
+     * Actual throttling-reset interval.  By default it's a day.
+     */
+    @GuardedBy("mLock")
+    private long mResetInterval;
+
+    public ShortcutService(Context context) {
+        mContext = Preconditions.checkNotNull(context);
+        LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
+        mHandler = new Handler(BackgroundThread.get().getLooper());
+    }
+
+    /**
+     * System service lifecycle.
+     */
+    public static final class Lifecycle extends SystemService {
+        final ShortcutService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+            mService = new ShortcutService(context);
+        }
+
+        @Override
+        public void onStart() {
+            publishBinderService(Context.SHORTCUT_SERVICE, mService);
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            mService.onBootPhase(phase);
+        }
+
+        @Override
+        public void onCleanupUser(int userHandle) {
+            synchronized (mService.mLock) {
+                mService.onCleanupUserInner(userHandle);
+            }
+        }
+
+        @Override
+        public void onStartUser(int userId) {
+            synchronized (mService.mLock) {
+                mService.onStartUserLocked(userId);
+            }
+        }
+    }
+
+    /** lifecycle event */
+    void onBootPhase(int phase) {
+        if (DEBUG) {
+            Slog.d(TAG, "onBootPhase: " + phase);
+        }
+        switch (phase) {
+            case SystemService.PHASE_LOCK_SETTINGS_READY:
+                initialize();
+                break;
+        }
+    }
+
+    /** lifecycle event */
+    void onStartUserLocked(int userId) {
+        // Preload
+        getUserShortcutsLocked(userId);
+    }
+
+    /** lifecycle event */
+    void onCleanupUserInner(int userId) {
+        // Unload
+        mShortcuts.delete(userId);
+    }
+
+    /** Return the base state file name */
+    private AtomicFile getBaseStateFile() {
+        final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
+        path.mkdirs();
+        return new AtomicFile(path);
+    }
+
+    /**
+     * Init the instance. (load the state file, etc)
+     */
+    private void initialize() {
+        synchronized (mLock) {
+            injectLoadConfigurationLocked();
+            loadBaseStateLocked();
+        }
+    }
+
+    // Test overrides it to inject different values.
+    @VisibleForTesting
+    void injectLoadConfigurationLocked() {
+        mResetInterval = DEFAULT_RESET_INTERVAL_SEC * 1000L;
+        mMaxDailyUpdates = DEFAULT_MAX_DAILY_UPDATES;
+        mMaxDynamicShortcuts = DEFAULT_MAX_SHORTCUTS_PER_APP;
+    }
+
+    // === Persistings ===
+
+    @Nullable
+    private String parseStringAttribute(XmlPullParser parser, String attribute) {
+        return parser.getAttributeValue(null, attribute);
+    }
+
+    private long parseLongAttribute(XmlPullParser parser, String attribute) {
+        final String value = parseStringAttribute(parser, attribute);
+        if (TextUtils.isEmpty(value)) {
+            return 0;
+        }
+        try {
+            return Long.parseLong(value);
+        } catch (NumberFormatException e) {
+            Slog.e(TAG, "Error parsing long " + value);
+            return 0;
+        }
+    }
+
+    @Nullable
+    private ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) {
+        final String value = parseStringAttribute(parser, attribute);
+        if (TextUtils.isEmpty(value)) {
+            return null;
+        }
+        return ComponentName.unflattenFromString(value);
+    }
+
+    @Nullable
+    private Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
+        final String value = parseStringAttribute(parser, attribute);
+        if (TextUtils.isEmpty(value)) {
+            return null;
+        }
+        try {
+            return Intent.parseUri(value, /* flags =*/ 0);
+        } catch (URISyntaxException e) {
+            Slog.e(TAG, "Error parsing intent", e);
+            return null;
+        }
+    }
+
+    private void writeTagValue(XmlSerializer out, String tag, String value) throws IOException {
+        if (TextUtils.isEmpty(value)) return;
+
+        out.startTag(null, tag);
+        out.attribute(null, ATTR_VALUE, value);
+        out.endTag(null, tag);
+    }
+
+    private void writeTagValue(XmlSerializer out, String tag, long value) throws IOException {
+        writeTagValue(out, tag, Long.toString(value));
+    }
+
+    private void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)
+            throws IOException, XmlPullParserException {
+        if (bundle == null) return;
+
+        out.startTag(null, tag);
+        bundle.saveToXml(out);
+        out.endTag(null, tag);
+    }
+
+    private void writeAttr(XmlSerializer out, String name, String value) throws IOException {
+        if (TextUtils.isEmpty(value)) return;
+
+        out.attribute(null, name, value);
+    }
+
+    private void writeAttr(XmlSerializer out, String name, long value) throws IOException {
+        writeAttr(out, name, String.valueOf(value));
+    }
+
+    private void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
+        if (comp == null) return;
+        writeAttr(out, name, comp.flattenToString());
+    }
+
+    private void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException {
+        if (intent == null) return;
+
+        writeAttr(out, name, intent.toUri(/* flags =*/ 0));
+    }
+
+    @VisibleForTesting
+    void saveBaseStateLocked() {
+        final AtomicFile file = getBaseStateFile();
+        if (DEBUG) {
+            Slog.i(TAG, "Saving to " + file.getBaseFile());
+        }
+
+        FileOutputStream outs = null;
+        try {
+            outs = file.startWrite();
+
+            // Write to XML
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(outs, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+            out.startTag(null, TAG_ROOT);
+
+            // Body.
+            writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
+
+            // Epilogue.
+            out.endTag(null, TAG_ROOT);
+            out.endDocument();
+
+            // Close.
+            file.finishWrite(outs);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
+            file.failWrite(outs);
+        }
+    }
+
+    private void loadBaseStateLocked() {
+        mRawLastResetTime = 0;
+
+        final AtomicFile file = getBaseStateFile();
+        if (DEBUG) {
+            Slog.i(TAG, "Loading from " + file.getBaseFile());
+        }
+        try (FileInputStream in = file.openRead()) {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(in, StandardCharsets.UTF_8.name());
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                if (type != XmlPullParser.START_TAG) {
+                    continue;
+                }
+                final int depth = parser.getDepth();
+                // Check the root tag
+                final String tag = parser.getName();
+                if (depth == 1) {
+                    if (!TAG_ROOT.equals(tag)) {
+                        Slog.e(TAG, "Invalid root tag: " + tag);
+                        return;
+                    }
+                    continue;
+                }
+                // Assume depth == 2
+                switch (tag) {
+                    case TAG_LAST_RESET_TIME:
+                        mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
+                        break;
+                    default:
+                        Slog.e(TAG, "Invalid tag: " + tag);
+                        break;
+                }
+            }
+        } catch (FileNotFoundException e) {
+            // Use the default
+        } catch (IOException|XmlPullParserException e) {
+            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
+
+            mRawLastResetTime = 0;
+        }
+        // Adjust the last reset time.
+        getLastResetTimeLocked();
+    }
+
+    private void saveUserLocked(@UserIdInt int userId) {
+        final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
+        if (DEBUG) {
+            Slog.i(TAG, "Saving to " + path);
+        }
+        path.mkdirs();
+        final AtomicFile file = new AtomicFile(path);
+        FileOutputStream outs = null;
+        try {
+            outs = file.startWrite();
+
+            // Write to XML
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(outs, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+            out.startTag(null, TAG_ROOT);
+
+            final ArrayMap<String, PackageShortcuts> packages = getUserShortcutsLocked(userId);
+
+            // Body.
+            for (int i = 0; i < packages.size(); i++) {
+                final String packageName = packages.keyAt(i);
+                final PackageShortcuts shortcuts = packages.valueAt(i);
+
+                // TODO Move this to PackageShortcuts.
+
+                out.startTag(null, "package");
+
+                writeAttr(out, "name", packageName);
+                writeAttr(out, "dynamic-count", shortcuts.mDynamicShortcutCount);
+                writeAttr(out, "call-count", shortcuts.mApiCallCountInner);
+                writeAttr(out, "last-reset", shortcuts.mLastResetTime);
+
+                final int size = shortcuts.getShortcuts().size();
+                for (int j = 0; j < size; j++) {
+                    saveShortcut(out, shortcuts.getShortcuts().valueAt(j));
+                }
+
+                out.endTag(null, "package");
+            }
+
+            // Epilogue.
+            out.endTag(null, TAG_ROOT);
+            out.endDocument();
+
+            // Close.
+            file.finishWrite(outs);
+        } catch (IOException|XmlPullParserException e) {
+            Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
+            file.failWrite(outs);
+        }
+    }
+
+    private void saveShortcut(XmlSerializer out, ShortcutInfo si)
+            throws IOException, XmlPullParserException {
+        out.startTag(null, "shortcut");
+        writeAttr(out, "id", si.getId());
+        // writeAttr(out, "package", si.getPackageName()); // not needed
+        writeAttr(out, "activity", si.getActivityComponent());
+        // writeAttr(out, "icon", si.getIcon());  // We don't save it.
+        writeAttr(out, "title", si.getTitle());
+        writeAttr(out, "intent", si.getIntent());
+        writeAttr(out, "weight", si.getWeight());
+        writeAttr(out, "timestamp", si.getLastChangedTimestamp());
+        writeAttr(out, "flags", si.getFlags());
+        writeAttr(out, "icon-res", si.getIconResourceId());
+        writeAttr(out, "bitmap-path", si.getBitmapPath());
+
+        writeTagExtra(out, "intent-extras", si.getIntentPersistableExtras());
+        writeTagExtra(out, "extras", si.getExtras());
+
+        out.endTag(null, "shortcut");
+    }
+
+    private static IOException throwForInvalidTag(int depth, String tag) throws IOException {
+        throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth));
+    }
+
+    @Nullable
+    private ArrayMap<String, PackageShortcuts> loadUserLocked(@UserIdInt int userId) {
+        final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
+        if (DEBUG) {
+            Slog.i(TAG, "Loading from " + path);
+        }
+        path.mkdirs();
+        final AtomicFile file = new AtomicFile(path);
+
+        final FileInputStream in;
+        try {
+            in = file.openRead();
+        } catch (FileNotFoundException e) {
+            if (DEBUG) {
+                Slog.i(TAG, "Not found " + path);
+            }
+            return null;
+        }
+        final ArrayMap<String, PackageShortcuts> ret = new ArrayMap<String, PackageShortcuts>();
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(in, StandardCharsets.UTF_8.name());
+
+            String packageName = null;
+            PackageShortcuts shortcuts = null;
+
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                if (type != XmlPullParser.START_TAG) {
+                    continue;
+                }
+                final int depth = parser.getDepth();
+
+                // TODO Move some of this to PackageShortcuts.
+
+                final String tag = parser.getName();
+                if (DEBUG_LOAD) {
+                    Slog.d(TAG, String.format("depth=%d type=%d name=%s",
+                            depth, type, tag));
+                }
+                switch (depth) {
+                    case 1: {
+                        if (TAG_ROOT.equals(tag)) {
+                            continue;
+                        }
+                        break;
+                    }
+                    case 2: {
+                        switch (tag) {
+                            case "package":
+                                packageName = parseStringAttribute(parser, "name");
+                                shortcuts = new PackageShortcuts();
+                                ret.put(packageName, shortcuts);
+
+                                shortcuts.mDynamicShortcutCount =
+                                        (int) parseLongAttribute(parser, "dynamic-count");
+                                shortcuts.mApiCallCountInner =
+                                        (int) parseLongAttribute(parser, "call-count");
+                                shortcuts.mLastResetTime = parseLongAttribute(parser, "last-reset");
+                                continue;
+                        }
+                        break;
+                    }
+                    case 3: {
+                        switch (tag) {
+                            case "shortcut":
+                                final ShortcutInfo si = parseShortcut(parser, packageName);
+                                shortcuts.mShortcuts.put(si.getId(), si);
+                                continue;
+                        }
+                        break;
+                    }
+                }
+                throwForInvalidTag(depth, tag);
+            }
+            return ret;
+        } catch (IOException|XmlPullParserException e) {
+            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
+            return null;
+        } finally {
+            IoUtils.closeQuietly(in);
+        }
+    }
+
+    private ShortcutInfo parseShortcut(XmlPullParser parser, String packgeName)
+            throws IOException, XmlPullParserException {
+        String id;
+        ComponentName activityComponent;
+        Icon icon;
+        String title;
+        Intent intent;
+        PersistableBundle intentPersistableExtras = null;
+        int weight;
+        PersistableBundle extras = null;
+        long lastChangedTimestamp;
+        int flags;
+        int iconRes;
+        String bitmapPath;
+
+        id = parseStringAttribute(parser, "id");
+        activityComponent = parseComponentNameAttribute(parser, "activity");
+        title = parseStringAttribute(parser, "title");
+        intent = parseIntentAttribute(parser, "intent");
+        weight = (int) parseLongAttribute(parser, "weight");
+        lastChangedTimestamp = (int) parseLongAttribute(parser, "timestamp");
+        flags = (int) parseLongAttribute(parser, "flags");
+        iconRes = (int) parseLongAttribute(parser, "icon-res");
+        bitmapPath = parseStringAttribute(parser, "bitmap-path");
+
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+            final int depth = parser.getDepth();
+            final String tag = parser.getName();
+            if (DEBUG_LOAD) {
+                Slog.d(TAG, String.format("  depth=%d type=%d name=%s",
+                        depth, type, tag));
+            }
+            switch (tag) {
+                case "intent-extras":
+                    intentPersistableExtras = PersistableBundle.restoreFromXml(parser);
+                    continue;
+                case "extras":
+                    extras = PersistableBundle.restoreFromXml(parser);
+                    continue;
+            }
+            throw throwForInvalidTag(depth, tag);
+        }
+        return new ShortcutInfo(
+                id, packgeName, activityComponent, /* icon =*/ null, title, intent,
+                intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
+                iconRes, bitmapPath);
+    }
+
+    // TODO Actually make it async.
+    private void scheduleSaveBaseState() {
+        synchronized (mLock) {
+            saveBaseStateLocked();
+        }
+    }
+
+    // TODO Actually make it async.
+    private void scheduleSaveUser(@UserIdInt int userId) {
+        synchronized (mLock) {
+            saveUserLocked(userId);
+        }
+    }
+
+    /** Return the last reset time. */
+    long getLastResetTimeLocked() {
+        updateTimes();
+        return mRawLastResetTime;
+    }
+
+    /** Return the next reset time. */
+    long getNextResetTimeLocked() {
+        updateTimes();
+        return mRawLastResetTime + mResetInterval;
+    }
+
+    /**
+     * Update the last reset time.
+     */
+    private void updateTimes() {
+
+        final long now = injectCurrentTimeMillis();
+
+        final long prevLastResetTime = mRawLastResetTime;
+
+        if (mRawLastResetTime == 0) { // first launch.
+            // TODO Randomize??
+            mRawLastResetTime = now;
+        } else if (now < mRawLastResetTime) {
+            // Clock rewound.
+            // TODO Randomize??
+            mRawLastResetTime = now;
+        } else {
+            // TODO Do it properly.
+            while ((mRawLastResetTime + mResetInterval) <= now) {
+                mRawLastResetTime += mResetInterval;
+            }
+        }
+        if (prevLastResetTime != mRawLastResetTime) {
+            scheduleSaveBaseState();
+        }
+    }
+
+    /** Return the per-user state. */
+    @GuardedBy("mLock")
+    @NonNull
+    private ArrayMap<String, PackageShortcuts> getUserShortcutsLocked(@UserIdInt int userId) {
+        ArrayMap<String, PackageShortcuts> userPackages = mShortcuts.get(userId);
+        if (userPackages == null) {
+            userPackages = loadUserLocked(userId);
+            if (userPackages == null) {
+                userPackages = new ArrayMap<>();
+            }
+            mShortcuts.put(userId, userPackages);
+        }
+        return userPackages;
+    }
+
+    /** Return the per-user per-package state. */
+    @GuardedBy("mLock")
+    @NonNull
+    private PackageShortcuts getPackageShortcutsLocked(
+            @NonNull String packageName, @UserIdInt int userId) {
+        final ArrayMap<String, PackageShortcuts> userPackages = getUserShortcutsLocked(userId);
+        PackageShortcuts shortcuts = userPackages.get(packageName);
+        if (shortcuts == null) {
+            shortcuts = new PackageShortcuts();
+            userPackages.put(packageName, shortcuts);
+        }
+        return shortcuts;
+    }
+
+    // === Caller validation ===
+
+    private boolean isCallerSystem() {
+        final int callingUid = injectBinderCallingUid();
+         return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
+    }
+
+    private boolean isCallerShell() {
+        final int callingUid = injectBinderCallingUid();
+        return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
+    }
+
+    private void enforceSystemOrShell() {
+        Preconditions.checkState(isCallerSystem() || isCallerShell(),
+                "Caller must be system or shell");
+    }
+
+    private void enforceShell() {
+        Preconditions.checkState(isCallerShell(), "Caller must be shell");
+    }
+
+    private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
+        Preconditions.checkStringNotEmpty(packageName, "packageName");
+
+        if (isCallerSystem()) {
+            return; // no check
+        }
+
+        final int callingUid = injectBinderCallingUid();
+
+        // Otherwise, make sure the arguments are valid.
+        if (UserHandle.getUserId(callingUid) != userId) {
+            throw new SecurityException("Invalid user-ID");
+        }
+        verifyCallingPackage(packageName);
+    }
+
+    private void verifyCallingPackage(@NonNull String packageName) {
+        Preconditions.checkStringNotEmpty(packageName, "packageName");
+
+        if (isCallerSystem()) {
+            return; // no check
+        }
+
+        if (injectGetPackageUid(packageName) == injectBinderCallingUid()) {
+            return; // Caller is valid.
+        }
+        throw new SecurityException("Caller UID= doesn't own " + packageName);
+    }
+
+    // Test overrides it.
+    int injectGetPackageUid(String packageName) {
+        try {
+
+            // TODO Is MATCH_UNINSTALLED_PACKAGES correct to get SD card app info?
+
+            return mContext.getPackageManager().getPackageUid(packageName,
+                    PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+                            | PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        } catch (NameNotFoundException e) {
+            return -1;
+        }
+    }
+
+    /**
+     * Throw if {@code numShortcuts} is bigger than {@link #mMaxDynamicShortcuts}.
+     */
+    void enforceMaxDynamicShortcuts(int numShortcuts) {
+        if (numShortcuts > mMaxDynamicShortcuts) {
+            throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
+        }
+    }
+
+    /**
+     * - Sends a notification to LauncherApps
+     * - Write to file
+     */
+    private void userPackageChanged(@NonNull String packageName, @UserIdInt int userId) {
+        notifyListeners(packageName, userId);
+        scheduleSaveUser(userId);
+    }
+
+    private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
+        final ArrayList<ShortcutChangeListener> copy;
+        final List<ShortcutInfo> shortcuts = new ArrayList<>();
+        synchronized (mLock) {
+            copy = new ArrayList<>(mListeners);
+
+            getPackageShortcutsLocked(packageName, userId)
+                    .findAll(shortcuts, /* query =*/ null, ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+        }
+        for (int i = copy.size() - 1; i >= 0; i--) {
+            copy.get(i).onShortcutChanged(packageName, shortcuts, userId);
+        }
+    }
+
+    /**
+     * Clean up / validate an incoming shortcut.
+     * - Make sure all mandatory fields are set.
+     * - Make sure the intent's extras are persistable, and them to set
+     *  {@link ShortcutInfo#mIntentPersistableExtras}.  Also clear its extras.
+     * - Clear flags.
+     */
+    private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut) {
+        Preconditions.checkNotNull(shortcut, "Null shortcut detected");
+        if (shortcut.getActivityComponent() != null) {
+            Preconditions.checkState(
+                    shortcut.getPackageName().equals(
+                            shortcut.getActivityComponent().getPackageName()),
+                    "Activity package name mismatch");
+        }
+
+        shortcut.enforceMandatoryFields();
+
+        final Intent intent = shortcut.getIntent();
+        final Bundle intentExtras = intent.getExtras();
+        if (intentExtras != null && intentExtras.size() > 0) {
+            intent.replaceExtras((Bundle) null);
+
+            // PersistableBundle's constructor will throw IllegalArgumentException if original
+            // extras contain something not persistable.
+            shortcut.setIntentPersistableExtras(new PersistableBundle(intentExtras));
+        }
+
+        // TODO Save the icon
+        shortcut.setIcon(null);
+
+        shortcut.setFlags(0);
+    }
+
+    // === APIs ===
+
+    @Override
+    public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
+            @UserIdInt int userId) {
+        verifyCaller(packageName, userId);
+
+        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+        final int size = newShortcuts.size();
+
+        synchronized (mLock) {
+            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
+
+            // Throttling.
+            if (!ps.tryApiCall(this)) {
+                return false;
+            }
+            enforceMaxDynamicShortcuts(size);
+
+            // Validate the shortcuts.
+            for (int i = 0; i < size; i++) {
+                fixUpIncomingShortcutInfo(newShortcuts.get(i));
+            }
+
+            // First, remove all un-pinned; dynamic shortcuts
+            ps.deleteAllDynamicShortcuts();
+
+            // Then, add/update all.  We need to make sure to take over "pinned" flag.
+            for (int i = 0; i < size; i++) {
+                final ShortcutInfo newShortcut = newShortcuts.get(i);
+                newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
+                ps.updateShortcutWithCapping(this, newShortcut);
+            }
+        }
+        userPackageChanged(packageName, userId);
+
+        return true;
+    }
+
+    @Override
+    public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
+            @UserIdInt int userId) {
+        verifyCaller(packageName, userId);
+
+        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+
+        synchronized (mLock) {
+
+            if (true) {
+                throw new RuntimeException("not implemented yet");
+            }
+
+            // TODO Similar to setDynamicShortcuts, but don't add new ones, and don't change flags.
+            // Update non-null fields only.
+        }
+        userPackageChanged(packageName, userId);
+
+        return true;
+    }
+
+    @Override
+    public boolean addDynamicShortcut(String packageName, ShortcutInfo newShortcut,
+            @UserIdInt int userId) {
+        verifyCaller(packageName, userId);
+
+        synchronized (mLock) {
+            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
+
+            // Throttling.
+            if (!ps.tryApiCall(this)) {
+                return false;
+            }
+
+            // Validate the shortcut.
+            fixUpIncomingShortcutInfo(newShortcut);
+
+            // Add it.
+            newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
+            ps.updateShortcutWithCapping(this, newShortcut);
+        }
+        userPackageChanged(packageName, userId);
+
+        return true;
+    }
+
+    @Override
+    public void deleteDynamicShortcut(String packageName, String shortcutId,
+            @UserIdInt int userId) {
+        verifyCaller(packageName, userId);
+        Preconditions.checkStringNotEmpty(shortcutId, "shortcutId must be provided");
+
+        synchronized (mLock) {
+            getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(shortcutId);
+        }
+        userPackageChanged(packageName, userId);
+    }
+
+    @Override
+    public void deleteAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
+        verifyCaller(packageName, userId);
+
+        synchronized (mLock) {
+            getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts();
+        }
+        userPackageChanged(packageName, userId);
+    }
+
+    @Override
+    public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
+            @UserIdInt int userId) {
+        verifyCaller(packageName, userId);
+        synchronized (mLock) {
+            return getShortcutsWithQueryLocked(
+                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
+                    ShortcutInfo::isDynamic);
+        }
+    }
+
+    @Override
+    public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
+            @UserIdInt int userId) {
+        verifyCaller(packageName, userId);
+        synchronized (mLock) {
+            return getShortcutsWithQueryLocked(
+                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
+                    ShortcutInfo::isPinned);
+        }
+    }
+
+    private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
+            @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
+
+        final ArrayList<ShortcutInfo> ret = new ArrayList<>();
+
+        getPackageShortcutsLocked(packageName, userId).findAll(ret, query, cloneFlags);
+
+        return new ParceledListSlice<>(ret);
+    }
+
+    @Override
+    public int getMaxDynamicShortcutCount(String packageName, @UserIdInt int userId)
+            throws RemoteException {
+        verifyCaller(packageName, userId);
+
+        return mMaxDynamicShortcuts;
+    }
+
+    @Override
+    public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
+        verifyCaller(packageName, userId);
+
+        synchronized (mLock) {
+            return mMaxDailyUpdates
+                    - getPackageShortcutsLocked(packageName, userId).getApiCallCount(this);
+        }
+    }
+
+    @Override
+    public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
+        verifyCaller(packageName, userId);
+
+        synchronized (mLock) {
+            return getNextResetTimeLocked();
+        }
+    }
+
+    /**
+     * Reset all throttling, for developer options and command line.  Only system/shell can call it.
+     */
+    @Override
+    public void resetThrottling() {
+        enforceSystemOrShell();
+
+        resetThrottlingInner();
+    }
+
+    @VisibleForTesting
+    void resetThrottlingInner() {
+        synchronized (mLock) {
+            mRawLastResetTime = injectCurrentTimeMillis();
+        }
+        scheduleSaveBaseState();
+    }
+
+    /**
+     * Entry point from {@link LauncherApps}.
+     */
+    private class LocalService extends ShortcutServiceInternal {
+        @Override
+        public List<ShortcutInfo> getShortcuts(
+                @NonNull String callingPackage, long changedSince,
+                @Nullable String packageName, @Nullable ComponentName componentName,
+                int queryFlags, int userId) {
+            final ArrayList<ShortcutInfo> ret = new ArrayList<>();
+            final int cloneFlag =
+                    ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) == 0)
+                            ? ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER
+                            : ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
+
+            synchronized (mLock) {
+                if (packageName != null) {
+                    getShortcutsInnerLocked(packageName, changedSince, componentName, queryFlags,
+                            userId, ret, cloneFlag);
+                } else {
+                    final ArrayMap<String, PackageShortcuts> packages =
+                            getUserShortcutsLocked(userId);
+                    for (int i = 0; i < packages.size(); i++) {
+                        getShortcutsInnerLocked(
+                                packages.keyAt(i),
+                                changedSince, componentName, queryFlags, userId, ret, cloneFlag);
+                    }
+                }
+            }
+            return ret;
+        }
+
+        private void getShortcutsInnerLocked(@Nullable String packageName,long changedSince,
+                @Nullable ComponentName componentName, int queryFlags,
+                int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
+            getPackageShortcutsLocked(packageName, userId).findAll(ret,
+                    (ShortcutInfo si) -> {
+                        if (si.getLastChangedTimestamp() < changedSince) {
+                            return false;
+                        }
+                        if (componentName != null
+                                && !componentName.equals(si.getActivityComponent())) {
+                            return false;
+                        }
+                        final boolean matchDynamic =
+                                ((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
+                                && si.isDynamic();
+                        final boolean matchPinned =
+                                ((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
+                                        && si.isPinned();
+                        return matchDynamic || matchPinned;
+                    }, cloneFlag);
+        }
+
+        @Override
+        public List<ShortcutInfo> getShortcutInfo(
+                @NonNull String callingPackage,
+                @NonNull String packageName, @Nullable List<String> ids, int userId) {
+            // Calling permission must be checked by LauncherAppsImpl.
+            Preconditions.checkStringNotEmpty(packageName, "packageName");
+
+            final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size());
+            final ArraySet<String> idSet = new ArraySet<>(ids);
+            synchronized (mLock) {
+                getPackageShortcutsLocked(packageName, userId).findAll(ret,
+                        (ShortcutInfo si) -> idSet.contains(si.getId()),
+                        ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+            }
+            return ret;
+        }
+
+        @Override
+        public void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
+                @NonNull List<String> shortcutIds, int userId) {
+            // Calling permission must be checked by LauncherAppsImpl.
+            Preconditions.checkStringNotEmpty(packageName, "packageName");
+            Preconditions.checkNotNull(shortcutIds, "shortcutIds");
+
+            synchronized (mLock) {
+                getPackageShortcutsLocked(packageName, userId).pinAll(shortcutIds);
+            }
+            userPackageChanged(packageName, userId);
+        }
+
+        @Override
+        public Intent createShortcutIntent(@NonNull String callingPackage,
+                @NonNull ShortcutInfo shortcut, int userId) {
+            // Calling permission must be checked by LauncherAppsImpl.
+            Preconditions.checkNotNull(shortcut, "shortcut");
+
+            synchronized (mLock) {
+                final ShortcutInfo fullShortcut =
+                        getPackageShortcutsLocked(shortcut.getPackageName(), userId)
+                        .getShortcuts().get(shortcut.getId());
+                if (fullShortcut == null) {
+                    return null;
+                } else {
+                    final Intent intent = fullShortcut.getIntent();
+                    final PersistableBundle extras = fullShortcut.getIntentPersistableExtras();
+                    if (extras != null) {
+                        intent.replaceExtras(new Bundle(extras));
+                    }
+
+                    return intent;
+                }
+            }
+        }
+
+        @Override
+        public void addListener(@NonNull ShortcutChangeListener listener) {
+            synchronized (mLock) {
+                mListeners.add(Preconditions.checkNotNull(listener));
+            }
+        }
+    }
+
+    // === Dump ===
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump UserManager from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid()
+                    + " without permission "
+                    + android.Manifest.permission.DUMP);
+            return;
+        }
+        dumpInner(pw);
+    }
+
+    @VisibleForTesting
+    void dumpInner(PrintWriter pw) {
+        synchronized (mLock) {
+            final long now = injectCurrentTimeMillis();
+            pw.print("Now: [");
+            pw.print(now);
+            pw.print("] ");
+            pw.print(formatTime(now));
+            pw.print("  Raw last reset: [");
+            pw.print(mRawLastResetTime);
+            pw.print("] ");
+            pw.print(formatTime(mRawLastResetTime));
+
+            final long last = getLastResetTimeLocked();
+            final long next = getNextResetTimeLocked();
+            pw.print("  Last reset: [");
+            pw.print(last);
+            pw.print("] ");
+            pw.print(formatTime(last));
+
+            pw.print("  Next reset: [");
+            pw.print(next);
+            pw.print("] ");
+            pw.print(formatTime(next));
+            pw.println();
+
+            pw.println();
+
+            for (int i = 0; i < mShortcuts.size(); i++) {
+                dumpUserLocked(pw, mShortcuts.keyAt(i));
+            }
+
+        }
+    }
+
+    private void dumpUserLocked(PrintWriter pw, int userId) {
+        pw.print("  User: ");
+        pw.print(userId);
+        pw.println();
+
+        final ArrayMap<String, PackageShortcuts> packages = mShortcuts.get(userId);
+        if (packages == null) {
+            return;
+        }
+        for (int j = 0; j < packages.size(); j++) {
+            dumpPackageLocked(pw, userId, packages.keyAt(j));
+        }
+        pw.println();
+    }
+
+    private void dumpPackageLocked(PrintWriter pw, int userId, String packageName) {
+        final PackageShortcuts shortcuts = mShortcuts.get(userId).get(packageName);
+        if (shortcuts == null) {
+            return;
+        }
+
+        pw.print("    Package: ");
+        pw.print(packageName);
+        pw.println();
+
+        pw.print("      Calls: ");
+        pw.print(shortcuts.getApiCallCount(this));
+        pw.println();
+
+        // This should be after getApiCallCount(), which may update it.
+        pw.print("      Last reset: [");
+        pw.print(shortcuts.mLastResetTime);
+        pw.print("] ");
+        pw.print(formatTime(shortcuts.mLastResetTime));
+        pw.println();
+
+        pw.println("      Shortcuts:");
+        final int size = shortcuts.getShortcuts().size();
+        for (int i = 0; i < size; i++) {
+            pw.print("        ");
+            pw.println(shortcuts.getShortcuts().valueAt(i).toInsecureString());
+        }
+    }
+
+    private static String formatTime(long time) {
+        Time tobj = new Time();
+        tobj.set(time);
+        return tobj.format("%Y-%m-%d %H:%M:%S");
+    }
+
+    // === Shell support ===
+
+    @Override
+    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+            String[] args, ResultReceiver resultReceiver) throws RemoteException {
+
+        enforceShell();
+
+        (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
+    }
+
+    /**
+     * Handle "adb shell cmd".
+     */
+    private class MyShellCommand extends ShellCommand {
+        @Override
+        public int onCommand(String cmd) {
+            if (cmd == null) {
+                return handleDefaultCommands(cmd);
+            }
+            final PrintWriter pw = getOutPrintWriter();
+            switch(cmd) {
+                case "reset-package-throttling":
+                    return handleResetPackageThrottling();
+                case "reset-throttling":
+                    return handleResetThrottling();
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+        }
+
+        @Override
+        public void onHelp() {
+            final PrintWriter pw = getOutPrintWriter();
+            pw.println("Usage: cmd shortcut COMMAND [options ...]");
+            pw.println();
+            pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE");
+            pw.println("    Reset throttling for a package");
+            pw.println();
+            pw.println("cmd shortcut reset-throttling");
+            pw.println("    Reset throttling for all packages and users");
+            pw.println();
+        }
+
+        private int handleResetThrottling() {
+            resetThrottling();
+            return 0;
+        }
+
+        private int handleResetPackageThrottling() {
+            final PrintWriter pw = getOutPrintWriter();
+
+            int userId = UserHandle.USER_SYSTEM;
+            String opt;
+            while ((opt = getNextOption()) != null) {
+                switch (opt) {
+                    case "--user":
+                        userId = UserHandle.parseUserArg(getNextArgRequired());
+                        break;
+                    default:
+                        pw.println("Error: Unknown option: " + opt);
+                        return 1;
+                }
+            }
+            final String packageName = getNextArgRequired();
+
+            synchronized (mLock) {
+                getPackageShortcutsLocked(packageName, userId).resetRateLimitingForCommandLine();
+                saveUserLocked(userId);
+            }
+
+            return 0;
+        }
+    }
+
+    // === Unit test support ===
+
+    // Injection point.
+    long injectCurrentTimeMillis() {
+        return System.currentTimeMillis();
+    }
+
+    // Injection point.
+    int injectBinderCallingUid() {
+        return getCallingUid();
+    }
+
+    File injectSystemDataPath() {
+        return Environment.getDataSystemDirectory();
+    }
+
+    File injectUserDataPath(@UserIdInt int userId) {
+        return new File(Environment.getDataSystemDeDirectory(userId), DIRECTORY_PER_USER);
+    }
+
+    @VisibleForTesting
+    SparseArray<ArrayMap<String, PackageShortcuts>> getShortcutsForTest() {
+        return mShortcuts;
+    }
+
+    @VisibleForTesting
+    void setMaxDynamicShortcutsForTest(int max) {
+        mMaxDynamicShortcuts = max;
+    }
+
+    @VisibleForTesting
+    void setMaxDailyUpdatesForTest(int max) {
+        mMaxDailyUpdates = max;
+    }
+
+    @VisibleForTesting
+    public void setResetIntervalForTest(long interval) {
+        mResetInterval = interval;
+    }
+}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e51a2e1..83a5ef5 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3360,8 +3360,8 @@
             throws RemoteException {
         synchronized (mLock) {
             IShortcutService service = mShortcutKeyServices.get(shortcutCode);
-            if (service != null && service.asBinder().isBinderAlive()) {
-                    throw new RemoteException("Key already exists.");
+            if (service != null && service.asBinder().pingBinder()) {
+                throw new RemoteException("Key already exists.");
             }
 
             mShortcutKeyServices.put(shortcutCode, shortcutService);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 49aaa4a..30442bc 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1292,6 +1292,7 @@
         @Override
         public void unblockContent(
                 IBinder sessionToken, String unblockedRating, int userId) {
+            ensureParentalControlsPermission();
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
                     userId, "unblockContent");
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index a81fba0..55b3c7b 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -190,7 +190,8 @@
 
     public void clearThumbnail() {
         if (thumbnail != null) {
-            thumbnail.destroy();
+            thumbnail.hide();
+            mService.mWindowPlacerLocked.destroyAfterTransaction(thumbnail);
             thumbnail = null;
         }
         deferThumbnailDestruction = false;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 2731f429..9795c93 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -129,6 +129,7 @@
     boolean mAlwaysFocusable;
 
     boolean mAppStopped;
+    int mPendingRelaunchCount;
 
     ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
 
@@ -531,6 +532,26 @@
         }
     }
 
+    boolean isRelaunching() {
+        return mPendingRelaunchCount > 0;
+    }
+
+    void startRelaunching() {
+        if (canFreezeBounds()) {
+            freezeBounds();
+        }
+        mPendingRelaunchCount++;
+    }
+
+    void finishRelaunching() {
+        if (canFreezeBounds()) {
+            unfreezeBounds();
+        }
+        if (mPendingRelaunchCount > 0) {
+            mPendingRelaunchCount--;
+        }
+    }
+
     void addWindow(WindowState w) {
         for (int i = allAppWindows.size() - 1; i >= 0; i--) {
             WindowState candidate = allAppWindows.get(i);
@@ -579,20 +600,26 @@
         }
     }
 
+    private boolean canFreezeBounds() {
+        // For freeform windows, we can't freeze the bounds at the moment because this would make
+        // the resizing unresponsive.
+        return mTask != null && !mTask.inFreeformWorkspace();
+    }
+
     /**
      * Freezes the task bounds. The size of this task reported the app will be fixed to the bounds
      * freezed by {@link Task#prepareFreezingBounds} until {@link #unfreezeBounds} gets called, even
      * if they change in the meantime. If the bounds are already frozen, the bounds will be frozen
      * with a queue.
      */
-    void freezeBounds() {
+    private void freezeBounds() {
         mFrozenBounds.offer(new Rect(mTask.mPreparedFrozenBounds));
     }
 
     /**
      * Unfreezes the previously frozen bounds. See {@link #freezeBounds}.
      */
-    void unfreezeBounds() {
+    private void unfreezeBounds() {
         mFrozenBounds.remove();
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState win = windows.get(i);
@@ -658,7 +685,10 @@
                     pw.print(" startingMoved="); pw.println(startingMoved);
         }
         if (!mFrozenBounds.isEmpty()) {
-            pw.print(prefix); pw.print("mFrozenBounds="); pw.print(mFrozenBounds);
+            pw.print(prefix); pw.print("mFrozenBounds="); pw.println(mFrozenBounds);
+        }
+        if (mPendingRelaunchCount != 0) {
+            pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index fc5d8ce..95be233 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -84,10 +84,13 @@
     /** The user of this dim layer. */
     private final DimLayerUser mUser;
 
-    DimLayer(WindowManagerService service, DimLayerUser user, int displayId) {
+    private final String mName;
+
+    DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) {
         mUser = user;
         mDisplayId = displayId;
         mService = service;
+        mName = name;
         if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
     }
 
@@ -100,7 +103,7 @@
                     16, 16, PixelFormat.OPAQUE,
                     SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
             } else {
-                mDimSurface = new SurfaceControl(service.mFxSession, TAG,
+                mDimSurface = new SurfaceControl(service.mFxSession, mName,
                     16, 16, PixelFormat.OPAQUE,
                     SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
             }
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 6d1cec4..97d0ae0 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -10,6 +10,8 @@
 import android.util.Slog;
 import android.util.TypedValue;
 
+import com.android.server.wm.DimLayer.DimLayerUser;
+
 import java.io.PrintWriter;
 
 /**
@@ -18,7 +20,8 @@
  * as well as other use cases (such as dimming above a dead window).
  */
 class DimLayerController {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "DimLayerController" : TAG_WM;
+    private static final String TAG_LOCAL = "DimLayerController";
+    private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
 
     /** Amount of time in milliseconds to animate the dim surface from one value to another,
      * when no window animation is driving it. */
@@ -63,7 +66,8 @@
                     newDimLayer = state.dimLayer;
                 } else {
                     // Create new full screen dim layer.
-                    newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId);
+                    newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
+                            getDimLayerTag(dimLayerUser));
                 }
                 dimLayerUser.getDimBounds(mTmpBounds);
                 newDimLayer.setBounds(mTmpBounds);
@@ -73,7 +77,8 @@
             }
         } else {
             newDimLayer = (state.dimLayer == null || previousFullscreen)
-                    ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId)
+                    ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
+                            getDimLayerTag(dimLayerUser))
                     : state.dimLayer;
             dimLayerUser.getDimBounds(mTmpBounds);
             newDimLayer.setBounds(mTmpBounds);
@@ -81,6 +86,10 @@
         state.dimLayer = newDimLayer;
     }
 
+    private static String getDimLayerTag(DimLayerUser dimLayerUser) {
+        return TAG_LOCAL + "/" + dimLayerUser.toShortString();
+    }
+
     private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
         if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
                 + dimLayerUser.toShortString());
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index b6aa3f2..9bceee7 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -16,6 +16,17 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
+import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.RemoteCallbackList;
@@ -30,19 +41,6 @@
 
 import com.android.server.wm.DimLayer.DimLayerUser;
 
-import java.util.ArrayList;
-
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
-import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
 /**
  * Keeps information about the docked stack divider.
  */
@@ -106,7 +104,8 @@
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
         mDividerInsets = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_divider_insets);
-        mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId());
+        mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
+                "DockedStackDim");
         mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
                 context, android.R.interpolator.fast_out_slow_in);
     }
@@ -247,8 +246,9 @@
 
     void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
         SurfaceControl.openTransaction();
-        TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
-        boolean visibleAndValid = visible && stack != null;
+        final TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
+        final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
+        boolean visibleAndValid = visible && stack != null && dockedStack != null;
         if (visibleAndValid) {
             stack.getDimBounds(mTmpRect);
             if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index f7035c5..92701de 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -60,7 +60,8 @@
 import java.lang.annotation.RetentionPolicy;
 
 class TaskPositioner implements DimLayer.DimLayerUser {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskPositioner" : TAG_WM;
+    private static final String TAG_LOCAL = "TaskPositioner";
+    private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
 
     // The margin the pointer position has to be within the side of the screen to be
     // considered at the side of the screen.
@@ -287,7 +288,7 @@
         }
         mService.pauseRotationLocked();
 
-        mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
+        mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId(), TAG_LOCAL);
         mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
         mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 86327f7..676b90f 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -585,7 +585,8 @@
         }
 
         mDisplayContent = displayContent;
-        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId());
+        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
+                "animation background stackId=" + mStackId);
 
         Rect bounds = null;
         final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 85bddee..4698e4e 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -771,6 +771,7 @@
         }
 
         mService.destroyPreservedSurfaceLocked();
+        mService.mWindowPlacerLocked.destroyPendingSurfaces();
 
         if (DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index ccba88d..0979cd3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -35,7 +35,7 @@
 
     static final boolean DEBUG_RESIZE = false;
     static final boolean DEBUG = false;
-    static final boolean DEBUG_ADD_REMOVE = true;
+    static final boolean DEBUG_ADD_REMOVE = false;
     static final boolean DEBUG_FOCUS = false;
     static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || false;
     static final boolean DEBUG_ANIM = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b64aaa8..a998bc3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -24,6 +24,9 @@
 import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -118,6 +121,7 @@
 import android.view.inputmethod.InputMethodManagerInternal;
 import android.widget.Toast;
 
+import com.android.internal.R;
 import com.android.internal.app.IAssistScreenshotReceiver;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IShortcutService;
@@ -2340,6 +2344,7 @@
                 mTokenMap.remove(token.token);
             } else if (atoken != null) {
                 atoken.firstWindowDrawn = false;
+                atoken.allDrawn = false;
             }
         }
 
@@ -4251,6 +4256,28 @@
                         TAG_WM, "No longer Stopped: " + wtoken);
                 wtoken.mAppStopped = false;
                 wtoken.setWindowsExiting(false);
+                mOpeningApps.add(wtoken);
+                wtoken.startingMoved = false;
+
+                // If the token is currently hidden (should be the
+                // common case), then we need to set up to wait for
+                // its windows to be ready.
+                if (wtoken.hidden) {
+                    wtoken.allDrawn = false;
+                    wtoken.deferClearAllDrawn = false;
+                    wtoken.waitingToShow = true;
+
+                    if (wtoken.clientHidden) {
+                        // In the case where we are making an app visible
+                        // but holding off for a transition, we still need
+                        // to tell the client to make its windows visible so
+                        // they get drawn.  Otherwise, we will wait on
+                        // performing the transition until all windows have
+                        // been drawn, they never will be, and we are sad.
+                        wtoken.clientHidden = false;
+                        wtoken.sendAppVisibilityToClients();
+                    }
+                }
             }
 
             // If we are preparing an app transition, then delay changing
@@ -4268,29 +4295,7 @@
                 }
                 wtoken.inPendingTransaction = true;
                 if (visible) {
-                    mOpeningApps.add(wtoken);
-                    wtoken.startingMoved = false;
                     wtoken.mEnteringAnimation = true;
-
-                    // If the token is currently hidden (should be the
-                    // common case), then we need to set up to wait for
-                    // its windows to be ready.
-                    if (wtoken.hidden) {
-                        wtoken.allDrawn = false;
-                        wtoken.deferClearAllDrawn = false;
-                        wtoken.waitingToShow = true;
-
-                        if (wtoken.clientHidden) {
-                            // In the case where we are making an app visible
-                            // but holding off for a transition, we still need
-                            // to tell the client to make its windows visible so
-                            // they get drawn.  Otherwise, we will wait on
-                            // performing the transition until all windows have
-                            // been drawn, they never will be, and we are sad.
-                            wtoken.clientHidden = false;
-                            wtoken.sendAppVisibilityToClients();
-                        }
-                    }
                 } else {
                     wtoken.setWindowsExiting(true);
                     mClosingApps.add(wtoken);
@@ -7468,6 +7473,35 @@
         return mCurrentFocus;
     }
 
+    private void showAuditSafeModeNotification() {
+        PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0,
+                new Intent(Intent.ACTION_VIEW,
+                           Uri.parse("https://support.google.com/nexus/answer/2852139")), 0);
+
+        String title = mContext.getString(R.string.audit_safemode_notification);
+
+        Notification notification = new Notification.Builder(mContext)
+                .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
+                .setWhen(0)
+                .setOngoing(true)
+                .setTicker(title)
+                .setLocalOnly(true)
+                .setPriority(Notification.PRIORITY_HIGH)
+                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                .setColor(mContext.getColor(
+                        com.android.internal.R.color.system_notification_accent_color))
+                .setContentTitle(title)
+                .setContentText(mContext.getString(R.string.audit_safemode_notification_details))
+                .setContentIntent(pendingIntent)
+                .build();
+
+        NotificationManager notificationManager = (NotificationManager) mContext
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+
+        notificationManager.notifyAsUser(null, R.string.audit_safemode_notification, notification,
+                UserHandle.ALL);
+    }
+
     public boolean detectSafeMode() {
         if (!mInputMonitor.waitForInputDevicesReady(
                 INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) {
@@ -7500,6 +7534,7 @@
 
                     if (auditSafeMode >= buildDate) {
                         mSafeMode = true;
+                        showAuditSafeModeNotification();
                     } else {
                         SystemProperties.set(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, "");
                         SystemProperties.set(ShutdownThread.AUDIT_SAFEMODE_PROPERTY, "");
@@ -9636,8 +9671,8 @@
     public void notifyAppRelaunching(IBinder token) {
         synchronized (mWindowMap) {
             AppWindowToken appWindow = findAppWindowToken(token);
-            if (canFreezeBounds(appWindow)) {
-                appWindow.freezeBounds();
+            if (appWindow != null) {
+                appWindow.startRelaunching();
             }
         }
     }
@@ -9645,20 +9680,12 @@
     public void notifyAppRelaunchingFinished(IBinder token) {
         synchronized (mWindowMap) {
             AppWindowToken appWindow = findAppWindowToken(token);
-            if (canFreezeBounds(appWindow)) {
-                appWindow.unfreezeBounds();
+            if (appWindow != null) {
+                appWindow.finishRelaunching();
             }
         }
     }
 
-    private boolean canFreezeBounds(AppWindowToken appWindow) {
-
-        // For freeform windows, we can't freeze the bounds at the moment because this would make
-        // the resizing unresponsive.
-        return appWindow != null && appWindow.mTask != null
-                && !appWindow.mTask.inFreeformWorkspace();
-    }
-
     @Override
     public int getDockedDividerInsetsLw() {
         return getDefaultDisplayContentLocked().getDockedDividerController().getContentInsets();
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index e3955fe..8ada2f1 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -46,7 +46,6 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.Debug;
-import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -125,6 +124,8 @@
     }
     private final LayerAndToken mTmpLayerAndToken = new LayerAndToken();
 
+    private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
+
     public WindowSurfacePlacer(WindowManagerService service) {
         mService = service;
         mWallpaperControllerLocked = mService.mWallpaperControllerLocked;
@@ -542,6 +543,7 @@
         mService.enableScreenIfNeededLocked();
 
         mService.scheduleAnimationLocked();
+        mService.mWindowPlacerLocked.destroyPendingSurfaces();
 
         if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
                 "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
@@ -1278,7 +1280,12 @@
                         "Check opening app=" + wtoken + ": allDrawn="
                         + wtoken.allDrawn + " startingDisplayed="
                         + wtoken.startingDisplayed + " startingMoved="
-                        + wtoken.startingMoved);
+                        + wtoken.startingMoved + " isRelaunching()="
+                        + wtoken.isRelaunching());
+
+                if (wtoken.isRelaunching()) {
+                    return false;
+                }
 
                 final boolean drawnBeforeRestoring = wtoken.allDrawn;
                 wtoken.restoreSavedSurfaces();
@@ -1620,6 +1627,25 @@
         }
     }
 
+    /**
+     * Puts the {@param surface} into a pending list to be destroyed after the current transaction
+     * has been committed.
+     */
+    void destroyAfterTransaction(SurfaceControl surface) {
+        mPendingDestroyingSurfaces.add(surface);
+    }
+
+    /**
+     * Destroys any surfaces that have been put into the pending list with
+     * {@link #destroyAfterTransaction}.
+     */
+    void destroyPendingSurfaces() {
+        for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
+            mPendingDestroyingSurfaces.get(i).destroy();
+        }
+        mPendingDestroyingSurfaces.clear();
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mTraversalScheduled="); pw.println(mTraversalScheduled);
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d979675..6ab4804 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -33,6 +33,7 @@
 import android.accounts.AccountManager;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
@@ -3642,7 +3643,7 @@
             if (count == 0 ||
                     count > admin.maximumFailedPasswordsForWipe ||
                     (count == admin.maximumFailedPasswordsForWipe &&
-                            mUserManager.getUserInfo(userId).isPrimary())) {
+                            getUserInfo(userId).isPrimary())) {
                 count = admin.maximumFailedPasswordsForWipe;
                 strictestAdmin = admin;
             }
@@ -3650,6 +3651,15 @@
         return strictestAdmin;
     }
 
+    private UserInfo getUserInfo(@UserIdInt int userId) {
+        final long token = mInjector.binderClearCallingIdentity();
+        try {
+            return mUserManager.getUserInfo(userId);
+        } finally {
+            mInjector.binderRestoreCallingIdentity(token);
+        }
+    }
+
     @Override
     public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException {
         if (!mHasFeature) {
@@ -5913,7 +5923,7 @@
      * - adb if there are not accounts.
      */
     private void enforceCanSetProfileOwnerLocked(int userHandle) {
-        UserInfo info = mUserManager.getUserInfo(userHandle);
+        UserInfo info = getUserInfo(userHandle);
         if (info == null) {
             // User doesn't exist.
             throw new IllegalArgumentException(
@@ -6065,12 +6075,7 @@
     }
 
     private boolean isManagedProfile(int userHandle) {
-        long ident = mInjector.binderClearCallingIdentity();
-        try {
-            return mUserManager.getUserInfo(userHandle).isManagedProfile();
-        } finally {
-            mInjector.binderRestoreCallingIdentity(ident);
-        }
+        return getUserInfo(userHandle).isManagedProfile();
     }
 
     private void enableIfNecessary(String packageName, int userId) {
@@ -6409,7 +6414,7 @@
         try {
             // If we have an enabled packages list for a managed profile the packages
             // we should check are installed for the parent user.
-            UserInfo user = mUserManager.getUserInfo(userIdToCheck);
+            UserInfo user = getUserInfo(userIdToCheck);
             if (user.isManagedProfile()) {
                 userIdToCheck = user.profileGroupId;
             }
@@ -6455,7 +6460,7 @@
             List<AccessibilityServiceInfo> enabledServices = null;
             long id = mInjector.binderClearCallingIdentity();
             try {
-                UserInfo user = mUserManager.getUserInfo(userId);
+                UserInfo user = getUserInfo(userId);
                 if (user.isManagedProfile()) {
                     userId = user.profileGroupId;
                 }
@@ -6537,7 +6542,7 @@
             if (result != null) {
                 long id = mInjector.binderClearCallingIdentity();
                 try {
-                    UserInfo user = mUserManager.getUserInfo(userId);
+                    UserInfo user = getUserInfo(userId);
                     if (user.isManagedProfile()) {
                         userId = user.profileGroupId;
                     }
@@ -6591,7 +6596,7 @@
         long token = mInjector.binderClearCallingIdentity();
         try {
             UserInfo currentUser;
-            UserInfo callingUser = mUserManager.getUserInfo(callingUserId);
+            UserInfo callingUser = getUserInfo(callingUserId);
             try {
                 currentUser = mInjector.getIActivityManager().getCurrentUser();
             } catch (RemoteException e) {
@@ -8223,13 +8228,7 @@
             getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
         }
         final int callingUserId = mInjector.userHandleGetCallingUserId();
-        final UserInfo user;
-        long ident = mInjector.binderClearCallingIdentity();
-        try {
-            user = mUserManager.getUserInfo(callingUserId);
-        } finally {
-            mInjector.binderRestoreCallingIdentity(ident);
-        }
+        final UserInfo user = getUserInfo(callingUserId);
         return user != null && user.isManagedProfile();
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 68fd0f6..b316cbd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -494,7 +494,6 @@
         abstract void writeInner(XmlSerializer out) throws IOException;
 
         abstract boolean readInner(XmlPullParser parser, int depth, String tag);
-
     }
 
     private class DeviceOwnerReadWriter extends FileReadWriter {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a2d5259..59f8284a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -83,6 +83,7 @@
 import com.android.server.pm.LauncherAppsService;
 import com.android.server.pm.OtaDexoptService;
 import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.ShortcutService;
 import com.android.server.pm.UserManagerService;
 import com.android.server.power.PowerManagerService;
 import com.android.server.power.ShutdownThread;
@@ -725,6 +726,9 @@
                 // Always start the Device Policy Manager, so that the API is compatible with
                 // API8.
                 mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
+
+// TODO is this a good place?
+                mSystemServiceManager.startService(ShortcutService.Lifecycle.class);
             }
 
             if (!disableSystemUI) {
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 3ae1072..23f186c 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -106,6 +106,10 @@
 
         <service android:name="com.android.server.job.MockPriorityJobService"
                  android:permission="android.permission.BIND_JOB_SERVICE" />
+
+        <activity android:name="com.android.server.pm.ShortcutManagerTest$ShortcutActivity" />
+        <activity android:name="com.android.server.pm.ShortcutManagerTest$ShortcutActivity2" />
+        <activity android:name="com.android.server.pm.ShortcutManagerTest$ShortcutActivity3" />
     </application>
 
     <instrumentation
diff --git a/services/tests/servicestests/res/drawable/icon1.png b/services/tests/servicestests/res/drawable/icon1.png
new file mode 100644
index 0000000..64eb294
--- /dev/null
+++ b/services/tests/servicestests/res/drawable/icon1.png
Binary files differ
diff --git a/services/tests/servicestests/res/drawable/icon2.png b/services/tests/servicestests/res/drawable/icon2.png
new file mode 100644
index 0000000..75024841
--- /dev/null
+++ b/services/tests/servicestests/res/drawable/icon2.png
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java
new file mode 100644
index 0000000..eb16a1d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java
@@ -0,0 +1,44 @@
+
+/*
+ * 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.pm;
+
+import android.content.pm.ShortcutInfo;
+import android.test.AndroidTestCase;
+
+import com.android.server.testutis.TestUtils;
+
+/**
+ * Tests for {@link ShortcutInfo}.
+
+ m FrameworksServicesTests &&
+ adb install \
+   -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.pm.ShortcutInfoTest \
+   -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+
+ */
+public class ShortcutInfoTest extends AndroidTestCase {
+
+    public void testNoId() {
+        TestUtils.assertExpectException(
+                IllegalArgumentException.class,
+                "ID must be provided",
+                () -> new ShortcutInfo.Builder(mContext).build());
+    }
+
+    // TODO Add more tests.
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
new file mode 100644
index 0000000..a9b6684
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -0,0 +1,1206 @@
+/*
+ * 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.pm;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.content.pm.ShortcutServiceInternal;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.FileUtils;
+import android.os.UserHandle;
+import android.test.AndroidTestCase;
+import android.test.mock.MockContext;
+import android.util.Log;
+
+import com.android.frameworks.servicestests.R;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+import org.junit.Assert;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tests for ShortcutService and ShortcutManager.
+ *
+ m FrameworksServicesTests &&
+ adb install \
+ -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest \
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ */
+public class ShortcutManagerTest extends AndroidTestCase {
+    private static final String TAG = "ShortcutManagerTest";
+
+    /**
+     * Whether to enable dump or not.  Should be only true when debugging to avoid bugs where
+     * dump affecting the behavior.
+     */
+    private static final boolean ENABLE_DUMP = true; // DO NOT SUBMIT WITH true
+
+    /** Context used in the client side */
+    private final class ClientContext extends MockContext {
+        @Override
+        public String getPackageName() {
+            return mInjectedClientPackage;
+        }
+    }
+
+    /** Context used in the service side */
+    private final class ServiceContext extends MockContext {
+    }
+
+    /** ShortcutService with injection override methods. */
+    private final class ShortcutServiceTestable extends ShortcutService {
+        public ShortcutServiceTestable(Context context) {
+            super(context);
+
+        }
+
+        @Override
+        void injectLoadConfigurationLocked() {
+            setResetIntervalForTest(INTERVAL);
+            setMaxDynamicShortcutsForTest(MAX_SHORTCUTS);
+            setMaxDailyUpdatesForTest(MAX_DAILY_UPDATES);
+        }
+
+        @Override
+        long injectCurrentTimeMillis() {
+            return mInjectedCurrentTimeLillis;
+        }
+
+        @Override
+        int injectBinderCallingUid() {
+            return mInjectedCallingUid;
+        }
+
+        @Override
+        int injectGetPackageUid(String packageName) {
+            Integer uid = mInjectedPackageUidMap.get(packageName);
+            return uid != null ? uid : -1;
+        }
+
+        @Override
+        File injectSystemDataPath() {
+            return new File(mInjectedFilePathRoot, "system");
+        }
+
+        @Override
+        File injectUserDataPath(@UserIdInt int userId) {
+            return new File(mInjectedFilePathRoot, "user-" + userId);
+        }
+    }
+
+    /** ShortcutManager with injection override methods. */
+    private final class ShortcutManagerTestable extends ShortcutManager {
+        public ShortcutManagerTestable(Context context, ShortcutServiceTestable service) {
+            super(context, service);
+        }
+
+        @Override
+        protected int injectMyUserId() {
+            return UserHandle.getUserId(mInjectedCallingUid);
+        }
+    }
+
+
+    public static class ShortcutActivity extends Activity {
+    }
+
+    public static class ShortcutActivity2 extends Activity {
+    }
+
+    public static class ShortcutActivity3 extends Activity {
+    }
+
+    private ServiceContext mServiceContext;
+    private ClientContext mClientContext;
+
+    private ShortcutServiceTestable mService;
+    private ShortcutManagerTestable mManager;
+    private ShortcutServiceInternal mInternal;
+
+    private File mInjectedFilePathRoot;
+
+    private long mInjectedCurrentTimeLillis;
+
+    private int mInjectedCallingUid;
+    private String mInjectedClientPackage;
+
+    private Map<String, Integer> mInjectedPackageUidMap;
+
+    private static final String CALLING_PACKAGE_1 = "com.android.test.1";
+    private static final int CALLING_UID_1 = 10001;
+
+    private static final String CALLING_PACKAGE_2 = "com.android.test.2";
+    private static final int CALLING_UID_2 = 10002;
+
+    private static final String CALLING_PACKAGE_3 = "com.android.test.3";
+    private static final int CALLING_UID_3 = 10003;
+
+    private static final String LAUNCHER_1 = "com.android.launcher.1";
+    private static final int LAUNCHER_UID_1 = 10011;
+
+    private static final String LAUNCHER_2 = "com.android.launcher.2";
+    private static final int LAUNCHER_UID_2 = 10012;
+
+    private static final long START_TIME = 1234560000000L;
+
+    private static final long INTERVAL = 10000;
+
+    private static final int MAX_SHORTCUTS = 5;
+
+    private static final int MAX_DAILY_UPDATES = 3;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mServiceContext = new ServiceContext();
+        mClientContext = new ClientContext();
+
+        // Prepare injection values.
+
+        mInjectedCurrentTimeLillis = START_TIME;
+
+        mInjectedPackageUidMap = new HashMap<>();
+        mInjectedPackageUidMap.put(CALLING_PACKAGE_1, CALLING_UID_1);
+        mInjectedPackageUidMap.put(CALLING_PACKAGE_2, CALLING_UID_2);
+        mInjectedPackageUidMap.put(CALLING_PACKAGE_3, CALLING_UID_3);
+        mInjectedPackageUidMap.put(LAUNCHER_1, LAUNCHER_UID_1);
+        mInjectedPackageUidMap.put(LAUNCHER_2, LAUNCHER_UID_2);
+
+        mInjectedFilePathRoot = new File(getContext().getCacheDir(), "test-files");
+
+        // Empty the data directory.
+        if (mInjectedFilePathRoot.exists()) {
+            Assert.assertTrue("failed to delete dir",
+                    FileUtils.deleteContents(mInjectedFilePathRoot));
+        }
+        mInjectedFilePathRoot.mkdirs();
+
+        initService();
+        setCaller(CALLING_PACKAGE_1);
+    }
+
+    /** (Re-) init the manager and the service. */
+    private void initService() {
+        LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
+
+        // Instantiate targets.
+        mService = new ShortcutServiceTestable(mServiceContext);
+        mManager = new ShortcutManagerTestable(mClientContext, mService);
+
+        mInternal = LocalServices.getService(ShortcutServiceInternal.class);
+
+        // Load the setting file.
+        mService.onBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);
+    }
+
+    /** Replace the current calling package */
+    private void setCaller(String packageName) {
+        mInjectedClientPackage = packageName;
+        mInjectedCallingUid = Preconditions.checkNotNull(mInjectedPackageUidMap.get(packageName));
+    }
+
+    private String getCallingPackage() {
+        return mInjectedClientPackage;
+    }
+
+    private int getCallingUserId() {
+        return UserHandle.getUserId(mInjectedCallingUid);
+    }
+
+    /** For debugging */
+    private void dumpsysOnLogcat() {
+        if (!ENABLE_DUMP) return;
+
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        final PrintWriter pw = new PrintWriter(out);
+        mService.dumpInner(pw);
+        pw.close();
+
+        Log.e(TAG, "Dumping ShortcutService:");
+        for (String line : out.toString().split("\n")) {
+            Log.e(TAG, line);
+        }
+    }
+
+    /**
+     * For debugging, dump arbitrary file on logcat.
+     */
+    private void dumpFileOnLogcat(String path) {
+        if (!ENABLE_DUMP) return;
+
+        Log.i(TAG, "Dumping file: " + path);
+        final StringBuilder sb = new StringBuilder();
+        try (BufferedReader br = new BufferedReader(new FileReader(path))) {
+            String line;
+            while ((line = br.readLine()) != null) {
+                Log.i(TAG, line);
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Couldn't read file", e);
+            fail("Exception " + e);
+        }
+    }
+
+    /**
+     * For debugging, dump the main state file on logcat.
+     */
+    private void dumpBaseStateFile() {
+        dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath()
+                + "/system/" + ShortcutService.FILENAME_BASE_STATE);
+    }
+
+    /**
+     * For debugging, dump per-user state file on logcat.
+     */
+    private void dumpUserFile(int userId) {
+        dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath()
+                + "/user-" + userId
+                + "/" + ShortcutService.FILENAME_USER_PACKAGES);
+    }
+
+    private static Bundle makeBundle(Object... keysAndValues) {
+        Preconditions.checkState((keysAndValues.length % 2) == 0);
+
+        if (keysAndValues.length == 0) {
+            return null;
+        }
+        final Bundle ret = new Bundle();
+
+        for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
+            final String key = keysAndValues[i].toString();
+            final Object value = keysAndValues[i + 1];
+
+            if (value == null) {
+                ret.putString(key, null);
+            } else if (value instanceof Integer) {
+                ret.putInt(key, (Integer) value);
+            } else if (value instanceof String) {
+                ret.putString(key, (String) value);
+            } else if (value instanceof Bundle) {
+                ret.putBundle(key, (Bundle) value);
+            } else {
+                fail("Type not supported yet: " + value.getClass().getName());
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * Make a shortcut with an ID.
+     */
+    private ShortcutInfo makeShortcut(String id) {
+        return makeShortcut(
+                id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
+    }
+
+    /**
+     * Make a shortcut with an ID and timestamp.
+     */
+    private ShortcutInfo makeShortcutWithTimestamp(String id, long timestamp) {
+        final ShortcutInfo s = makeShortcut(
+                id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
+        s.setTimestamp(timestamp);
+        return s;
+    }
+
+    /**
+     * Make multiple shortcuts with IDs.
+     */
+    private List<ShortcutInfo> makeShortcuts(String... ids) {
+        final ArrayList<ShortcutInfo> ret = new ArrayList();
+        for (String id : ids) {
+            ret.add(makeShortcut(id));
+        }
+        return ret;
+    }
+
+    /**
+     * Make a shortcut with details.
+     */
+    private ShortcutInfo makeShortcut(String id, String title, ComponentName activity,
+            Icon icon, Intent intent, int weight) {
+        final ShortcutInfo.Builder  b = new ShortcutInfo.Builder(mClientContext)
+                .setId(id)
+                .setTitle(title)
+                .setWeight(weight)
+                .setIntent(intent);
+        if (icon != null) {
+            b.setIcon(icon);
+        }
+        if (activity != null) {
+            b.setActivityComponent(activity);
+        }
+        final ShortcutInfo s = b.build();
+
+        s.setTimestamp(mInjectedCurrentTimeLillis); // HACK
+
+        return s;
+    }
+
+    /**
+     * Make an intent.
+     */
+    private Intent makeIntent(String action, Class<?> clazz, Object... bundleKeysAndValues) {
+        final Intent intent = new Intent(action);
+        intent.setComponent(makeComponent(clazz));
+        intent.replaceExtras(makeBundle(bundleKeysAndValues));
+        return intent;
+    }
+
+    /**
+     * Make an component name, with the client context.
+     */
+    @NonNull
+    private ComponentName makeComponent(Class<?> clazz) {
+        return new ComponentName(mClientContext, clazz);
+    }
+
+    @NonNull
+    private ShortcutInfo findById(List<ShortcutInfo> list, String id) {
+        for (ShortcutInfo s : list) {
+            if (s.getId().equals(id)) {
+                return s;
+            }
+        }
+        fail("Shortcut with id " + id + " not found");
+        return null;
+    }
+
+    private void assertResetTimes(long expectedLastResetTime, long expectedNextResetTime) {
+        assertEquals(expectedLastResetTime, mService.getLastResetTimeLocked());
+        assertEquals(expectedNextResetTime, mService.getNextResetTimeLocked());
+    }
+
+    @NonNull
+    private List<ShortcutInfo> assertShortcutIds(@NonNull List<ShortcutInfo> actualShortcuts,
+            String... expectedIds) {
+        final HashSet<String> expected = new HashSet<>(Arrays.asList(expectedIds));
+        final HashSet<String> actual = new HashSet<>();
+        for (ShortcutInfo s : actualShortcuts) {
+            actual.add(s.getId());
+        }
+
+        // Compare the sets.
+        assertEquals(expected, actual);
+        return actualShortcuts;
+    }
+
+    @NonNull
+    private List<ShortcutInfo> assertAllHaveIntents(
+            @NonNull List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertNotNull("ID " + s.getId(), s.getIntent());
+        }
+        return actualShortcuts;
+    }
+
+    @NonNull
+    private List<ShortcutInfo> assertAllNotHaveIntents(
+            @NonNull List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertNull("ID " + s.getId(), s.getIntent());
+        }
+        return actualShortcuts;
+    }
+
+    @NonNull
+    private List<ShortcutInfo> assertAllHaveTitle(
+            @NonNull List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertNotNull("ID " + s.getId(), s.getTitle());
+        }
+        return actualShortcuts;
+    }
+
+    @NonNull
+    private List<ShortcutInfo> assertAllNotHaveTitle(
+            @NonNull List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertNull("ID " + s.getId(), s.getTitle());
+        }
+        return actualShortcuts;
+    }
+
+    @NonNull
+    private List<ShortcutInfo> assertAllHaveFlags(@NonNull List<ShortcutInfo> actualShortcuts,
+            int shortcutFlags) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertTrue("ID " + s.getId(), s.hasFlags(shortcutFlags));
+        }
+        return actualShortcuts;
+    }
+
+    @NonNull
+    private List<ShortcutInfo> assertAllDynamic(@NonNull List<ShortcutInfo> actualShortcuts) {
+        return assertAllHaveFlags(actualShortcuts, ShortcutInfo.FLAG_DYNAMIC);
+    }
+
+    @NonNull
+    private List<ShortcutInfo> assertAllPinned(@NonNull List<ShortcutInfo> actualShortcuts) {
+        return assertAllHaveFlags(actualShortcuts, ShortcutInfo.FLAG_PINNED);
+    }
+
+    @NonNull
+    private List<ShortcutInfo> assertAllDynamicOrPinned(
+            @NonNull List<ShortcutInfo> actualShortcuts) {
+        for (ShortcutInfo s : actualShortcuts) {
+            assertTrue("ID " + s.getId(), s.isDynamic() || s.isPinned());
+        }
+        return actualShortcuts;
+    }
+
+    /**
+     * Test for the first launch path, no settings file available.
+     */
+    public void testFirstInitialize() {
+        assertResetTimes(START_TIME, START_TIME + INTERVAL);
+    }
+
+    /**
+     * Test for {@link ShortcutService#updateTimes()}
+     */
+    public void testUpdateAndGetNextResetTimeLocked() {
+        assertResetTimes(START_TIME, START_TIME + INTERVAL);
+
+        // Advance clock.
+        mInjectedCurrentTimeLillis += 100;
+
+        // Shouldn't have changed.
+        assertResetTimes(START_TIME, START_TIME + INTERVAL);
+
+        // Advance clock, almost the reset time.
+        mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
+
+        // Shouldn't have changed.
+        assertResetTimes(START_TIME, START_TIME + INTERVAL);
+
+        // Advance clock.
+        mInjectedCurrentTimeLillis += 1;
+
+        assertResetTimes(START_TIME + INTERVAL, START_TIME + 2 * INTERVAL);
+
+        // Advance further; 4 days since start.
+        mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL + 50;
+
+        assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
+    }
+
+    /**
+     * Test for the restoration from saved file.
+     */
+    public void testInitializeFromSavedFile() {
+
+        mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL + 50;
+        assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
+
+        mService.saveBaseStateLocked();
+
+        dumpBaseStateFile();
+
+        // Restore.
+        initService();
+
+        assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
+    }
+
+    /**
+     * Test for the restoration from restored file.
+     */
+    public void testLoadFromBrokenFile() {
+        // TODO Add various broken cases.
+    }
+
+    // === Test for app side APIs ===
+
+    /** Test for {@link android.content.pm.ShortcutManager#getMaxDynamicShortcutCount()} */
+    public void testGetMaxDynamicShortcutCount() {
+        assertEquals(MAX_SHORTCUTS, mManager.getMaxDynamicShortcutCount());
+    }
+
+    /** Test for {@link android.content.pm.ShortcutManager#getRemainingCallCount()} */
+    public void testGetRemainingCallCount() {
+        assertEquals(MAX_DAILY_UPDATES, mManager.getRemainingCallCount());
+    }
+
+    /** Test for {@link android.content.pm.ShortcutManager#getRateLimitResetTime()} */
+    public void testGetRateLimitResetTime() {
+        assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+        mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL + 50;
+
+        assertEquals(START_TIME + 5 * INTERVAL, mManager.getRateLimitResetTime());
+    }
+
+    public void testSetDynamicShortcuts() {
+        final Icon icon1 = Icon.createWithResource(mContext, R.drawable.icon1);
+        final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+                mContext.getResources(), R.drawable.icon2));
+
+        final ShortcutInfo si1 = makeShortcut(
+                "shortcut1",
+                "Title 1",
+                makeComponent(ShortcutActivity.class),
+                icon1,
+                makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+                        "key1", "val1", "nest", makeBundle("key", 123)),
+                /* weight */ 10);
+
+        final ShortcutInfo si2 = makeShortcut(
+                "shortcut2",
+                "Title 2",
+                /* activity */ null,
+                icon2,
+                makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+                /* weight */ 12);
+        final ShortcutInfo si3 = makeShortcut("shortcut3");
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
+        assertEquals(2, mManager.getDynamicShortcuts().size());
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        // TODO: Check fields
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(1, mManager.getDynamicShortcuts().size());
+        assertEquals(1, mManager.getRemainingCallCount());
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList()));
+        assertEquals(0, mManager.getDynamicShortcuts().size());
+        assertEquals(0, mManager.getRemainingCallCount());
+
+        dumpsysOnLogcat();
+
+        mInjectedCurrentTimeLillis++; // Need to advance the clock for reset to work.
+        mService.resetThrottlingInner();
+
+        dumpsysOnLogcat();
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2, si3)));
+        assertEquals(2, mManager.getDynamicShortcuts().size());
+
+        // TODO Check max number
+    }
+
+    public void testAddDynamicShortcuts() {
+        final ShortcutInfo si1 = makeShortcut("shortcut1");
+        final ShortcutInfo si2 = makeShortcut("shortcut2");
+        final ShortcutInfo si3 = makeShortcut("shortcut3");
+
+        assertEquals(3, mManager.getRemainingCallCount());
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(2, mManager.getRemainingCallCount());
+        assertEquals(1, mManager.getDynamicShortcuts().size());
+
+        assertTrue(mManager.addDynamicShortcut(si2));
+        assertEquals(1, mManager.getRemainingCallCount());
+        assertEquals(2, mManager.getDynamicShortcuts().size());
+
+        // Add with the same ID
+        assertTrue(mManager.addDynamicShortcut(makeShortcut("shortcut1")));
+        assertEquals(0, mManager.getRemainingCallCount());
+        assertEquals(2, mManager.getDynamicShortcuts().size()); // Still 2
+
+        // TODO Check max number
+
+        // TODO Check fields.
+    }
+
+    public void testDeleteDynamicShortcut() {
+        final ShortcutInfo si1 = makeShortcut("shortcut1");
+        final ShortcutInfo si2 = makeShortcut("shortcut2");
+        final ShortcutInfo si3 = makeShortcut("shortcut3");
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
+        assertEquals(3, mManager.getDynamicShortcuts().size());
+
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        mManager.deleteDynamicShortcut("shortcut1");
+        assertEquals(2, mManager.getDynamicShortcuts().size());
+
+        mManager.deleteDynamicShortcut("shortcut1");
+        assertEquals(2, mManager.getDynamicShortcuts().size());
+
+        mManager.deleteDynamicShortcut("shortcutXXX");
+        assertEquals(2, mManager.getDynamicShortcuts().size());
+
+        mManager.deleteDynamicShortcut("shortcut2");
+        assertEquals(1, mManager.getDynamicShortcuts().size());
+
+        mManager.deleteDynamicShortcut("shortcut3");
+        assertEquals(0, mManager.getDynamicShortcuts().size());
+
+        // Still 2 calls left.
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        // TODO Make sure pinned shortcuts won't be deleted.
+    }
+
+    public void testDeleteAllDynamicShortcuts() {
+        final ShortcutInfo si1 = makeShortcut("shortcut1");
+        final ShortcutInfo si2 = makeShortcut("shortcut2");
+        final ShortcutInfo si3 = makeShortcut("shortcut3");
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
+        assertEquals(3, mManager.getDynamicShortcuts().size());
+
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        mManager.deleteAllDynamicShortcuts();
+        assertEquals(0, mManager.getDynamicShortcuts().size());
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        // Note delete shouldn't affect throttling, so...
+        assertEquals(0, mManager.getDynamicShortcuts().size());
+        assertEquals(0, mManager.getDynamicShortcuts().size());
+        assertEquals(0, mManager.getDynamicShortcuts().size());
+
+        // This should still work.
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
+        assertEquals(3, mManager.getDynamicShortcuts().size());
+
+        // Still 1 call left
+        assertEquals(1, mManager.getRemainingCallCount());
+
+        // TODO Make sure pinned shortcuts won't be deleted.
+    }
+
+    public void testThrottling() {
+        final ShortcutInfo si1 = makeShortcut("shortcut1");
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        mInjectedCurrentTimeLillis++;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(1, mManager.getRemainingCallCount());
+
+        mInjectedCurrentTimeLillis++;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(0, mManager.getRemainingCallCount());
+
+        // Reached the max
+
+        mInjectedCurrentTimeLillis++;
+        assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(0, mManager.getRemainingCallCount());
+
+        // Still throttled
+        mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
+        assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(0, mManager.getRemainingCallCount());
+
+        // Now it should work.
+        mInjectedCurrentTimeLillis++;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        mInjectedCurrentTimeLillis++;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(1, mManager.getRemainingCallCount());
+
+        mInjectedCurrentTimeLillis++;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(0, mManager.getRemainingCallCount());
+
+        mInjectedCurrentTimeLillis++;
+        assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(0, mManager.getRemainingCallCount());
+
+        // 4 days later...
+        mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        // Make sure getRemainingCallCount() itself gets reset withou calling setDynamicShortcuts().
+        mInjectedCurrentTimeLillis = START_TIME + 8 * INTERVAL;
+        assertEquals(3, mManager.getRemainingCallCount());
+    }
+
+    public void testThrottling_perPackage() {
+        final ShortcutInfo si1 = makeShortcut("shortcut1");
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        mInjectedCurrentTimeLillis++;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(1, mManager.getRemainingCallCount());
+
+        mInjectedCurrentTimeLillis++;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(0, mManager.getRemainingCallCount());
+
+        // Reached the max
+
+        mInjectedCurrentTimeLillis++;
+        assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+
+        // Try from a different caller.
+        mInjectedClientPackage = CALLING_PACKAGE_2;
+        mInjectedCallingUid = CALLING_UID_2;
+
+        // Need to create a new one wit the updated package name.
+        final ShortcutInfo si2 = makeShortcut("shortcut1");
+
+        assertEquals(3, mManager.getRemainingCallCount());
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        mInjectedCurrentTimeLillis++;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+        assertEquals(1, mManager.getRemainingCallCount());
+
+        // Back to the original caller, still throttled.
+        mInjectedClientPackage = CALLING_PACKAGE_1;
+        mInjectedCallingUid = CALLING_UID_1;
+
+        mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
+        assertEquals(0, mManager.getRemainingCallCount());
+        assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertEquals(0, mManager.getRemainingCallCount());
+
+        // Now it should work.
+        mInjectedCurrentTimeLillis++;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+
+        mInjectedCurrentTimeLillis++;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+
+        mInjectedCurrentTimeLillis++;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+
+        mInjectedCurrentTimeLillis++;
+        assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+
+        mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL;
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+        assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+
+        mInjectedClientPackage = CALLING_PACKAGE_2;
+        mInjectedCallingUid = CALLING_UID_2;
+
+        assertEquals(3, mManager.getRemainingCallCount());
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+        assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+    }
+
+    // TODO: updateShortcuts()
+    // TODO: getPinnedShortcuts()
+
+    // === Test for launcher side APIs ===
+
+    public void testGetShortcuts() {
+
+        // Set up shortcuts.
+
+        setCaller(CALLING_PACKAGE_1);
+        final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 5000);
+        final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 1000);
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+
+        setCaller(CALLING_PACKAGE_2);
+        final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
+        final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
+        final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2, s2_3, s2_4)));
+
+        setCaller(CALLING_PACKAGE_3);
+        final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s3", 5000);
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s3_2)));
+
+        setCaller(LAUNCHER_1);
+
+        // Get dynamic
+        assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+                mInternal.getShortcuts(getCallingPackage(), /* time =*/ 0, CALLING_PACKAGE_1,
+                        /* activity =*/ null,
+                    ShortcutQuery.FLAG_GET_DYNAMIC, getCallingUserId()),
+                "s1", "s2"))));
+
+        // Get pinned
+        assertShortcutIds(
+                mInternal.getShortcuts(getCallingPackage(),  /* time =*/ 0, CALLING_PACKAGE_1,
+                        /* activity =*/ null,
+                        ShortcutQuery.FLAG_GET_PINNED, getCallingUserId())
+                /* none */);
+
+        // Get both, with timestamp
+        assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+                mInternal.getShortcuts(getCallingPackage(),  /* time =*/ 1000, CALLING_PACKAGE_2,
+                        /* activity =*/ null,
+                        ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_DYNAMIC,
+                        getCallingUserId()),
+                "s2", "s3"))));
+
+        // FLAG_GET_KEY_FIELDS_ONLY
+        assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+                mInternal.getShortcuts(getCallingPackage(), /* time =*/ 1000, CALLING_PACKAGE_2,
+                        /* activity =*/ null,
+                        ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY,
+                        getCallingUserId()),
+                "s2", "s3"))));
+
+        // Pin some shortcuts.
+        mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_2,
+                Arrays.asList("s3", "s4"), getCallingUserId());
+
+        // Pinned ones only
+        assertAllPinned(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
+                mInternal.getShortcuts(getCallingPackage(), /* time =*/ 1000, CALLING_PACKAGE_2,
+                        /* activity =*/ null,
+                        ShortcutQuery.FLAG_GET_PINNED,
+                        getCallingUserId()),
+                "s3"))));
+
+        // All packages.
+        assertShortcutIds(
+                mInternal.getShortcuts(getCallingPackage(),
+                        /* time =*/ 5000, /* package= */ null,
+                        /* activity =*/ null,
+                        ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED,
+                        getCallingUserId()),
+                "s1", "s3");
+
+        // TODO More tests: pinned but dynamic, filter by activity
+    }
+
+    public void testGetShortcutInfo() {
+        // Create shortcuts.
+        setCaller(CALLING_PACKAGE_1);
+        final ShortcutInfo s1_1 = makeShortcut(
+                "s1",
+                "Title 1",
+                makeComponent(ShortcutActivity.class),
+                /* icon =*/ null,
+                makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+                        "key1", "val1", "nest", makeBundle("key", 123)),
+                /* weight */ 10);
+
+        final ShortcutInfo s1_2 = makeShortcut(
+                "s2",
+                "Title 2",
+                /* activity */ null,
+                /* icon =*/ null,
+                makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+                /* weight */ 12);
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+        dumpsysOnLogcat();
+
+        setCaller(CALLING_PACKAGE_2);
+        final ShortcutInfo s2_1 = makeShortcut(
+                "s1",
+                "ABC",
+                makeComponent(ShortcutActivity2.class),
+                /* icon =*/ null,
+                makeIntent(Intent.ACTION_ANSWER, ShortcutActivity2.class,
+                        "key1", "val1", "nest", makeBundle("key", 123)),
+                /* weight */ 10);
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_1)));
+        dumpsysOnLogcat();
+
+        // Pin some.
+        setCaller(LAUNCHER_1);
+
+        mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_1,
+                Arrays.asList("s2"), getCallingUserId());
+
+        dumpsysOnLogcat();
+
+        // Delete some.
+        setCaller(CALLING_PACKAGE_1);
+        assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+        mManager.deleteDynamicShortcut("s2");
+        assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+
+        dumpsysOnLogcat();
+
+        setCaller(LAUNCHER_1);
+        List<ShortcutInfo> list;
+
+        // Note we don't guarantee the orders.
+        list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
+                mInternal.getShortcutInfo(getCallingPackage(), CALLING_PACKAGE_1,
+                Arrays.asList("s2", "s1", "s3", null), getCallingUserId()))),
+                "s1", "s2");
+        assertEquals("Title 1", findById(list, "s1").getTitle());
+        assertEquals("Title 2", findById(list, "s2").getTitle());
+
+        assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
+                mInternal.getShortcutInfo(getCallingPackage(), CALLING_PACKAGE_1,
+                        Arrays.asList("s3"), getCallingUserId())))
+                /* none */);
+
+        list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
+                mInternal.getShortcutInfo(getCallingPackage(), CALLING_PACKAGE_2,
+                        Arrays.asList("s1", "s2", "s3"), getCallingUserId()))),
+                "s1");
+        assertEquals("ABC", findById(list, "s1").getTitle());
+    }
+
+    public void testPinShortcutAndGetPinnedShortcuts() {
+        // Create some shortcuts.
+        setCaller(CALLING_PACKAGE_1);
+        final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
+        final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+
+        setCaller(CALLING_PACKAGE_2);
+        final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
+        final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
+        final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2, s2_3, s2_4)));
+
+        setCaller(CALLING_PACKAGE_3);
+        final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s2", 1000);
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2)));
+
+        // Pin some.
+        setCaller(LAUNCHER_1);
+
+        mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_1,
+                Arrays.asList("s2", "s3"), getCallingUserId());
+
+        mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_2,
+                Arrays.asList("s3", "s4", "s5"), getCallingUserId());
+
+        mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_3,
+                Arrays.asList("s3"), getCallingUserId());  // Note ID doesn't exist
+
+        // Delete some.
+        setCaller(CALLING_PACKAGE_1);
+        assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+        mManager.deleteDynamicShortcut("s2");
+        assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+
+        setCaller(CALLING_PACKAGE_2);
+        assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+        mManager.deleteDynamicShortcut("s3");
+        assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+
+        setCaller(CALLING_PACKAGE_3);
+        assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+        mManager.deleteDynamicShortcut("s2");
+        assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+
+        // Get pinned shortcuts from launcher
+        setCaller(LAUNCHER_1);
+
+        // CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists.
+        assertShortcutIds(assertAllPinned(
+                mInternal.getShortcuts(getCallingPackage(), /* time =*/ 0, CALLING_PACKAGE_1,
+                /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED, getCallingUserId())),
+                "s2");
+
+        assertShortcutIds(assertAllPinned(
+                mInternal.getShortcuts(getCallingPackage(), /* time =*/ 0, CALLING_PACKAGE_2,
+                /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED, getCallingUserId())),
+                "s3", "s4");
+
+        assertShortcutIds(assertAllPinned(
+                mInternal.getShortcuts(getCallingPackage(), /* time =*/ 0, CALLING_PACKAGE_3,
+                /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED, getCallingUserId()))
+                /* none */);
+    }
+
+    public void testCreateShortcutIntent() {
+        // Create some shortcuts.
+        setCaller(CALLING_PACKAGE_1);
+        final ShortcutInfo s1_1 = makeShortcut(
+                "s1",
+                "Title 1",
+                makeComponent(ShortcutActivity.class),
+                /* icon =*/ null,
+                makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+                        "key1", "val1", "nest", makeBundle("key", 123)),
+                /* weight */ 10);
+
+        final ShortcutInfo s1_2 = makeShortcut(
+                "s2",
+                "Title 2",
+                /* activity */ null,
+                /* icon =*/ null,
+                makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+                /* weight */ 12);
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+
+        setCaller(CALLING_PACKAGE_2);
+        final ShortcutInfo s2_1 = makeShortcut(
+                "s1",
+                "ABC",
+                makeComponent(ShortcutActivity.class),
+                /* icon =*/ null,
+                makeIntent(Intent.ACTION_ANSWER, ShortcutActivity.class,
+                        "key1", "val1", "nest", makeBundle("key", 123)),
+                /* weight */ 10);
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_1)));
+
+        // Pin all.
+        setCaller(LAUNCHER_1);
+        mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_1,
+                Arrays.asList("s1", "s2"), getCallingUserId());
+
+        mInternal.pinShortcuts(getCallingPackage(), CALLING_PACKAGE_2,
+                Arrays.asList("s1"), getCallingUserId());
+
+        // Just to make it complicated, delete some.
+        setCaller(CALLING_PACKAGE_1);
+        mManager.deleteDynamicShortcut("s2");
+
+        // intent and check.
+        setCaller(LAUNCHER_1);
+        Intent intent;
+        intent = mInternal.createShortcutIntent(getCallingPackage(),
+                s1_1.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO), getCallingUserId());
+        assertEquals(ShortcutActivity2.class.getName(), intent.getComponent().getClassName());
+
+        intent = mInternal.createShortcutIntent(getCallingPackage(),
+                s1_2.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO), getCallingUserId());
+        assertEquals(ShortcutActivity3.class.getName(), intent.getComponent().getClassName());
+
+        intent = mInternal.createShortcutIntent(getCallingPackage(),
+                s2_1.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO), getCallingUserId());
+        assertEquals(ShortcutActivity.class.getName(), intent.getComponent().getClassName());
+
+        // TODO Check extra, etc
+    }
+
+    // === Test for persisting ===
+
+    public void testSaveAndLoadUser_empty() {
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList()));
+
+        Log.i(TAG, "Saved state");
+        dumpsysOnLogcat();
+        dumpUserFile(0);
+
+        // Restore.
+        initService();
+
+        assertEquals(0, mManager.getDynamicShortcuts().size());
+    }
+
+    /**
+     * Try save and load, also stop/start the user.
+     */
+    public void testSaveAndLoadUser() {
+        // First, create some shortcuts and save.
+        final Icon icon1 = Icon.createWithResource(mContext, R.drawable.icon1);
+        final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+                mContext.getResources(), R.drawable.icon2));
+
+        final ShortcutInfo si1 = makeShortcut(
+                "shortcut1",
+                "Title 1",
+                makeComponent(ShortcutActivity.class),
+                icon1,
+                makeIntent(Intent.ACTION_ASSIST, ShortcutActivity2.class,
+                        "key1", "val1", "nest", makeBundle("key", 123)),
+                /* weight */ 10);
+
+        final ShortcutInfo si2 = makeShortcut(
+                "shortcut2",
+                "Title 2",
+                /* activity */ null,
+                icon2,
+                makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
+                /* weight */ 12);
+
+        assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
+
+        assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        Log.i(TAG, "Saved state");
+        dumpsysOnLogcat();
+        dumpUserFile(0);
+
+        // Restore.
+        initService();
+
+        // Before the load, the map should be empty.
+        assertEquals(0, mService.getShortcutsForTest().size());
+
+        // this will pre-load the per-user info.
+        mService.onStartUserLocked(UserHandle.USER_SYSTEM);
+
+        // Now it's loaded.
+        assertEquals(1, mService.getShortcutsForTest().size());
+
+        // Start another user
+        mService.onStartUserLocked(10);
+
+        // Now the size is 2.
+        assertEquals(2, mService.getShortcutsForTest().size());
+
+        Log.i(TAG, "Dumping the new instance");
+
+        List<ShortcutInfo> loaded = mManager.getDynamicShortcuts();
+
+        Log.i(TAG, "Loaded state");
+        dumpsysOnLogcat();
+
+        assertEquals(2, loaded.size());
+
+        assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        // Try stopping the user
+        mService.onCleanupUserInner(UserHandle.USER_SYSTEM);
+
+        // Now it's unloaded.
+        assertEquals(1, mService.getShortcutsForTest().size());
+
+        // TODO Check all other fields
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java b/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java
new file mode 100644
index 0000000..52e8f37
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java
@@ -0,0 +1,47 @@
+/*
+ * 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.testutis;
+
+import android.test.MoreAsserts;
+
+import junit.framework.Assert;
+
+public class TestUtils {
+    private TestUtils() {
+    }
+
+    public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
+            Runnable r) {
+        assertExpectException(expectedExceptionType, null, r);
+    }
+
+    public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
+            String expectedExceptionMessageRegex, Runnable r) {
+        try {
+            r.run();
+            Assert.fail("Expected exception type " + expectedExceptionType.getClass().getName()
+                    + " was not thrown");
+        } catch (Throwable e) {
+            Assert.assertTrue(
+                    "Expected exception type was " + expectedExceptionType.getClass().getName()
+                    + " but caught " + e.getClass().getName(),
+                    expectedExceptionType.isAssignableFrom(e.getClass()));
+            if (expectedExceptionMessageRegex != null) {
+                MoreAsserts.assertContainsRegex(expectedExceptionMessageRegex, e.getMessage());
+            }
+        }
+    }
+}
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear_clamp.xml b/tests/VectorDrawableTest/res/color/fill_gradient_linear_clamp.xml
new file mode 100644
index 0000000..6a24453
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_linear_clamp.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:startColor="?android:attr/colorPrimary"
+          android:endColor="?android:attr/colorControlActivated"
+          android:centerColor="#00ff0000"
+          android:startX="0"
+          android:startY="0"
+          android:endX="50"
+          android:endY="50"
+          android:type="linear"
+          android:tileMode="clamp">
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap_mirror.xml b/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap_mirror.xml
new file mode 100644
index 0000000..d342bca
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap_mirror.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:startColor="?android:attr/colorPrimary"
+          android:endColor="?android:attr/colorControlActivated"
+          android:centerColor="#f00"
+          android:startX="0"
+          android:startY="0"
+          android:endX="50"
+          android:endY="50"
+          android:type="linear"
+          android:tileMode="mirror">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#f00"/>
+    <item android:offset="0.4" android:color="#fff"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_repeat.xml b/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_repeat.xml
new file mode 100644
index 0000000..afb45aa
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_repeat.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:startColor="?android:attr/colorPrimary"
+          android:endColor="?android:attr/colorControlActivated"
+          android:centerColor="#f00"
+          android:startX="0"
+          android:startY="0"
+          android:endX="50"
+          android:endY="50"
+          android:type="linear"
+          android:tileMode="repeat">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#fff"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_clamp.xml b/tests/VectorDrawableTest/res/color/fill_gradient_radial_clamp.xml
new file mode 100644
index 0000000..64b32f6
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_radial_clamp.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerColor="#ff0000"
+          android:endColor="?android:attr/colorControlActivated"
+          android:centerX="300"
+          android:centerY="300"
+          android:gradientRadius="50"
+          android:startColor="?android:attr/colorPrimary"
+          android:type="radial"
+          android:tileMode="clamp">
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item.xml b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item.xml
index 51b0e17..c6cea7c 100644
--- a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item.xml
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-    <!--
+<!--
 /*
  * Copyright (C) 2016 The Android Open Source Project
  *
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_repeat.xml b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_repeat.xml
new file mode 100644
index 0000000..fb4346a
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_repeat.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerColor="#ff0000"
+          android:endColor="#ff0000ff"
+          android:centerX="300"
+          android:centerY="300"
+          android:gradientRadius="50"
+          android:startColor="#ffffffff"
+          android:type="radial"
+          android:tileMode="repeat">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#fff"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml
index 8caa1b4..fefbe9f 100644
--- a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-    <!--
+<!--
 /*
  * Copyright (C) 2016 The Android Open Source Project
  *
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short_mirror.xml b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short_mirror.xml
new file mode 100644
index 0000000..8b5ad7c
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short_mirror.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerX="300"
+          android:centerY="300"
+          android:gradientRadius="50"
+          android:type="radial"
+          android:tileMode="mirror">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep_clamp.xml b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_clamp.xml
new file mode 100644
index 0000000..80f39f3e
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_clamp.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerColor="#ff0000"
+          android:endColor="#ff0000ff"
+          android:centerX="500"
+          android:centerY="500"
+          android:gradientRadius="10"
+          android:startColor="#ffffffff"
+          android:type="sweep"
+          android:tileMode="clamp">
+</gradient>
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_long_mirror.xml b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_long_mirror.xml
new file mode 100644
index 0000000..0890bd6
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_long_mirror.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerX="500"
+          android:centerY="500"
+          android:gradientRadius="10"
+          android:type="sweep"
+          android:tileMode="mirror">
+    <item android:offset="-0.3" android:color="#f00"/>
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#0f0"/>
+    <item android:offset="0.6" android:color="#00f"/>
+    <item android:offset="0.7" android:color="?android:attr/colorControlActivated"/>
+    <item android:offset="1.5" android:color="#00f"/>
+</gradient>
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_repeat.xml b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_repeat.xml
new file mode 100644
index 0000000..2ec5014
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_repeat.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerColor="#ff0000"
+          android:endColor="#ff0000ff"
+          android:centerX="500"
+          android:centerY="500"
+          android:gradientRadius="10"
+          android:startColor="#ffffffff"
+          android:type="sweep"
+          android:tileMode="repeat">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#fff"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient_clamp.xml b/tests/VectorDrawableTest/res/color/stroke_gradient_clamp.xml
new file mode 100644
index 0000000..3d746e7
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/stroke_gradient_clamp.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:centerColor="#7f7f7f"
+          android:endColor="#ffffff"
+          android:startColor="#000000"
+          android:startX="0"
+          android:endX="50"
+          android:startY="0"
+          android:endY="0"
+          android:type="linear"
+          android:tileMode="clamp">
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient_item_alpha_mirror.xml b/tests/VectorDrawableTest/res/color/stroke_gradient_item_alpha_mirror.xml
new file mode 100644
index 0000000..352a2fd
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/stroke_gradient_item_alpha_mirror.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:startX="0"
+          android:endX="50"
+          android:startY="0"
+          android:endY="0"
+          android:type="linear"
+          android:tileMode="mirror">
+    <item android:offset="0.1" android:color="#f00"/>
+    <item android:offset="0.2" android:color="#2f0f"/>
+    <item android:offset="0.9" android:color="#f00f"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient_item_repeat.xml b/tests/VectorDrawableTest/res/color/stroke_gradient_item_repeat.xml
new file mode 100644
index 0000000..42281d1
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/stroke_gradient_item_repeat.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:centerColor="#7f7f7f"
+          android:endColor="#ffffff"
+          android:startColor="#000000"
+          android:startX="0"
+          android:endX="50"
+          android:startY="0"
+          android:endY="0"
+          android:type="linear"
+          android:tileMode="repeat">
+    <item android:offset="0.1" android:color="#f00"/>
+    <item android:offset="0.2" android:color="#f0f"/>
+    <item android:offset="0.9" android:color="#f00f"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_1_clamp.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_1_clamp.xml
new file mode 100644
index 0000000..2fa440a
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_1_clamp.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+<group android:name="backgroundGroup"
+       android:scaleX="0.5"
+       android:scaleY="0.5">
+    <path
+            android:name="background1"
+            android:fillColor="@color/fill_gradient_linear_clamp"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background2"
+            android:fillColor="@color/fill_gradient_radial_clamp"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background3"
+            android:fillColor="@color/fill_gradient_sweep_clamp"
+            android:pathData="M 400,400 l 200,0 l 0, 200 l -200, 0 z" />
+</group>
+<group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0" >
+    <path
+            android:name="twoLines"
+            android:pathData="@string/twoLinePathData"
+            android:strokeColor="@color/stroke_gradient_clamp"
+            android:strokeWidth="20" />
+
+    <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0">
+        <path
+                android:name="twoLines1"
+                android:pathData="@string/twoLinePathData"
+                android:strokeColor="@color/stroke_gradient_clamp"
+                android:strokeWidth="20" />
+
+        <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines3"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient_clamp"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+
+        <group
+                android:name="translateGroupHalf"
+                android:translateX="65.0"
+                android:translateY="80.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines2"
+                        android:pathData="@string/twoLinePathData"
+                        android:fillColor="@color/fill_gradient_linear_clamp"
+                        android:strokeColor="@color/stroke_gradient_clamp"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+    </group>
+</group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_2_repeat.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_2_repeat.xml
new file mode 100644
index 0000000..5a43f80
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_2_repeat.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+<group android:name="backgroundGroup"
+       android:scaleX="0.5"
+       android:scaleY="0.5">
+    <path
+            android:name="background1"
+            android:fillColor="@color/fill_gradient_linear_item_repeat"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background2"
+            android:fillColor="@color/fill_gradient_radial_item_repeat"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background3"
+            android:fillColor="@color/fill_gradient_sweep_item_repeat"
+            android:pathData="M 400,400 l 200,0 l 0, 200 l -200, 0 z" />
+</group>
+<group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0" >
+    <path
+            android:name="twoLines"
+            android:pathData="@string/twoLinePathData"
+            android:strokeColor="@color/stroke_gradient_item_repeat"
+            android:strokeWidth="20" />
+
+    <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0">
+        <path
+                android:name="twoLines1"
+                android:pathData="@string/twoLinePathData"
+                android:strokeColor="@color/stroke_gradient_item_repeat"
+                android:strokeWidth="20" />
+
+        <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines3"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient_item_repeat"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+
+        <group
+                android:name="translateGroupHalf"
+                android:translateX="65.0"
+                android:translateY="80.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines2"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient_item_repeat"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+    </group>
+</group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_3_mirror.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_3_mirror.xml
new file mode 100644
index 0000000..e8de7c2
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_3_mirror.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+<group android:name="backgroundGroup"
+       android:scaleX="0.5"
+       android:scaleY="0.5">
+    <path
+            android:name="background1"
+            android:fillColor="@color/fill_gradient_linear_item_overlap_mirror"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background2"
+            android:fillColor="@color/fill_gradient_radial_item_short_mirror"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background3"
+            android:fillColor="@color/fill_gradient_sweep_item_long_mirror"
+            android:pathData="M 400,400 l 200,0 l 0, 200 l -200, 0 z" />
+</group>
+<group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0" >
+    <path
+            android:name="twoLines"
+            android:pathData="@string/twoLinePathData"
+            android:strokeColor="@color/stroke_gradient_item_alpha_mirror"
+            android:strokeWidth="20" />
+
+    <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0">
+        <path
+                android:name="twoLines1"
+                android:pathData="@string/twoLinePathData"
+                android:strokeColor="@color/stroke_gradient_item_alpha_mirror"
+                android:strokeWidth="20" />
+
+        <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines3"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient_item_alpha_mirror"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+
+        <group
+                android:name="translateGroupHalf"
+                android:translateX="65.0"
+                android:translateY="80.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines2"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient_item_alpha"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+    </group>
+</group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
index 7172147..495d620 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
@@ -38,6 +38,9 @@
             R.drawable.vector_icon_gradient_1,
             R.drawable.vector_icon_gradient_2,
             R.drawable.vector_icon_gradient_3,
+            R.drawable.vector_icon_gradient_1_clamp,
+            R.drawable.vector_icon_gradient_2_repeat,
+            R.drawable.vector_icon_gradient_3_mirror,
             R.drawable.vector_icon_state_list_simple,
             R.drawable.vector_icon_state_list_theme,
             R.drawable.vector_drawable01,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index d1fd56a..c7ae6fc 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -168,8 +168,8 @@
         RTL_ATTRS.put("?android:attr/paddingRight", "paddingEnd");
         RTL_ATTRS.put("?android:attr/layout_marginLeft", "layout_marginStart");
         RTL_ATTRS.put("?android:attr/layout_marginRight", "layout_marginEnd");
-        RTL_ATTRS.put("?android:attr/layout_toLeft", "layout_toStartOf");
-        RTL_ATTRS.put("?android:attr/layout_toRight", "layout_toEndOf");
+        RTL_ATTRS.put("?android:attr/layout_toLeftOf", "layout_toStartOf");
+        RTL_ATTRS.put("?android:attr/layout_toRightOf", "layout_toEndOf");
         RTL_ATTRS.put("?android:attr/layout_alignParentLeft", "layout_alignParentStart");
         RTL_ATTRS.put("?android:attr/layout_alignParentRight", "layout_alignParentEnd");
         RTL_ATTRS.put("?android:attr/drawableLeft", "drawableStart");
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index a46aaec..a9259fa 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -305,9 +305,12 @@
      */
     public static class InformationElement {
         public static final int EID_SSID = 0;
+        public static final int EID_SUPPORTED_RATES = 1;
         public static final int EID_TIM = 5;
         public static final int EID_BSS_LOAD = 11;
+        public static final int EID_ERP = 42;
         public static final int EID_RSN = 48;
+        public static final int EID_EXTENDED_SUPPORTED_RATES = 50;
         public static final int EID_HT_OPERATION = 61;
         public static final int EID_INTERWORKING = 107;
         public static final int EID_ROAMING_CONSORTIUM = 111;