Merge "Fix location QuickSettings bug"
diff --git a/Android.bp b/Android.bp
index ee2281f..5590609 100644
--- a/Android.bp
+++ b/Android.bp
@@ -113,6 +113,7 @@
         "core/java/android/content/IOnPrimaryClipChangedListener.aidl",
         "core/java/android/content/IRestrictionsManager.aidl",
         "core/java/android/content/ISyncAdapter.aidl",
+        "core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl",
         "core/java/android/content/ISyncContext.aidl",
         "core/java/android/content/ISyncServiceAdapter.aidl",
         "core/java/android/content/ISyncStatusObserver.aidl",
@@ -331,6 +332,8 @@
         "core/java/android/view/IPinnedStackController.aidl",
         "core/java/android/view/IPinnedStackListener.aidl",
         "core/java/android/view/IRemoteAnimationRunner.aidl",
+        "core/java/android/view/IRecentsAnimationController.aidl",
+        "core/java/android/view/IRecentsAnimationRunner.aidl",
         "core/java/android/view/IRemoteAnimationFinishedCallback.aidl",
         "core/java/android/view/IRotationWatcher.aidl",
         "core/java/android/view/IWallpaperVisibilityListener.aidl",
diff --git a/api/current.txt b/api/current.txt
index b7016a2..70add65 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8767,6 +8767,7 @@
     method public void onSecurityException(android.accounts.Account, android.os.Bundle, java.lang.String, android.content.SyncResult);
     method public void onSyncCanceled();
     method public void onSyncCanceled(java.lang.Thread);
+    method public boolean onUnsyncableAccount();
     field public static final deprecated int LOG_SYNC_DETAILS = 2743; // 0xab7
   }
 
@@ -22101,6 +22102,7 @@
     method public int getChannelConfiguration();
     method public int getChannelCount();
     method public android.media.AudioFormat getFormat();
+    method public android.os.PersistableBundle getMetrics();
     method public static int getMinBufferSize(int, int, int);
     method public int getNotificationMarkerPosition();
     method public int getPositionNotificationPeriod();
@@ -22149,6 +22151,14 @@
     method public android.media.AudioRecord.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
   }
 
+  public static final class AudioRecord.MetricsConstants {
+    field public static final java.lang.String CHANNELS = "android.media.audiorecord.channels";
+    field public static final java.lang.String ENCODING = "android.media.audiorecord.encoding";
+    field public static final java.lang.String LATENCY = "android.media.audiorecord.latency";
+    field public static final java.lang.String SAMPLERATE = "android.media.audiorecord.samplerate";
+    field public static final java.lang.String SOURCE = "android.media.audiorecord.source";
+  }
+
   public static abstract interface AudioRecord.OnRecordPositionUpdateListener {
     method public abstract void onMarkerReached(android.media.AudioRecord);
     method public abstract void onPeriodicNotification(android.media.AudioRecord);
@@ -22208,6 +22218,7 @@
     method public int getChannelCount();
     method public android.media.AudioFormat getFormat();
     method public static float getMaxVolume();
+    method public android.os.PersistableBundle getMetrics();
     method public static int getMinBufferSize(int, int, int);
     method public static float getMinVolume();
     method protected deprecated int getNativeFrameCount();
@@ -22288,6 +22299,14 @@
     method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
   }
 
+  public static final class AudioTrack.MetricsConstants {
+    field public static final java.lang.String CHANNELMASK = "android.media.audiorecord.channelmask";
+    field public static final java.lang.String CONTENTTYPE = "android.media.audiotrack.type";
+    field public static final java.lang.String SAMPLERATE = "android.media.audiorecord.samplerate";
+    field public static final java.lang.String STREAMTYPE = "android.media.audiotrack.streamtype";
+    field public static final java.lang.String USAGE = "android.media.audiotrack.usage";
+  }
+
   public static abstract interface AudioTrack.OnPlaybackPositionUpdateListener {
     method public abstract void onMarkerReached(android.media.AudioTrack);
     method public abstract void onPeriodicNotification(android.media.AudioTrack);
@@ -23396,6 +23415,7 @@
     method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
     method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
     method public int getSampleFlags();
+    method public long getSampleSize();
     method public long getSampleTime();
     method public int getSampleTrackIndex();
     method public final int getTrackCount();
@@ -36499,6 +36519,18 @@
     field public static final deprecated java.lang.String WINDOW_ANIMATION_SCALE = "window_animation_scale";
   }
 
+  public class SettingsSlicesContract {
+    field public static final java.lang.String AUTHORITY = "android.settings.slices";
+    field public static final android.net.Uri BASE_URI;
+    field public static final java.lang.String KEY_AIRPLANE_MODE = "airplane_mode";
+    field public static final java.lang.String KEY_BATTERY_SAVER = "battery_saver";
+    field public static final java.lang.String KEY_BLUETOOTH = "bluetooth";
+    field public static final java.lang.String KEY_LOCATION = "location";
+    field public static final java.lang.String KEY_WIFI = "wifi";
+    field public static final java.lang.String PATH_SETTING_ACTION = "action";
+    field public static final java.lang.String PATH_SETTING_INTENT = "intent";
+  }
+
   public class SyncStateContract {
     ctor public SyncStateContract();
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 6be004c..9127059 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5625,7 +5625,7 @@
     field public final java.lang.String description;
     field public final boolean isFallback;
     field public final java.lang.String packageName;
-    field public final java.lang.String[] signatures;
+    field public final android.content.pm.Signature[] signatures;
   }
 
   public final class WebViewUpdateService {
diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md
index 9ea6fea..5946515 100644
--- a/cmds/bootanimation/FORMAT.md
+++ b/cmds/bootanimation/FORMAT.md
@@ -30,7 +30,7 @@
 
 It is followed by a number of rows of the form:
 
-    TYPE COUNT PAUSE PATH [#RGBHEX CLOCK]
+    TYPE COUNT PAUSE PATH [#RGBHEX [CLOCK1 [CLOCK2]]]
 
   * **TYPE:** a single char indicating what type of animation segment this is:
       + `p` -- this part will play unless interrupted by the end of the boot
@@ -39,11 +39,38 @@
   * **PAUSE:** number of FRAMES to delay after this part ends
   * **PATH:** directory in which to find the frames for this part (e.g. `part0`)
   * **RGBHEX:** _(OPTIONAL)_ a background color, specified as `#RRGGBB`
-  * **CLOCK:** _(OPTIONAL)_ the y-coordinate at which to draw the current time (for watches)
+  * **CLOCK1, CLOCK2:** _(OPTIONAL)_ the coordinates at which to draw the current time (for watches):
+      + If only `CLOCK1` is provided it is the y-coordinate of the clock and the x-coordinate
+        defaults to `c`
+      + If both `CLOCK1` and `CLOCK2` are provided then `CLOCK1` is the x-coordinate and `CLOCK2` is
+        the y-coodinate
+      + Values can be either a positive integer, a negative integer, or `c`
+          - `c` -- will centre the text
+          - `n` -- will position the text n pixels from the start; left edge for x-axis, bottom edge
+            for y-axis
+          - `-n` -- will position the text n pixels from the end; right edge for x-axis, top edge
+            for y-axis
+          - Examples:
+              * `-24` or `c -24` will position the text 24 pixels from the top of the screen,
+                centred horizontally
+              * `16 c` will position the text 16 pixels from the left of the screen, centred
+                vertically
+              * `-32 32` will position the text such that the bottom right corner is 32 pixels above
+                and 32 pixels left of the edges of the screen
 
 There is also a special TYPE, `$SYSTEM`, that loads `/system/media/bootanimation.zip`
 and plays that.
 
+## clock_font.png
+
+The file used to draw the time on top of the boot animation. The font format is as follows:
+  * The file specifies glyphs for the ascii characters 32-127 (0x20-0x7F), both regular weight and
+    bold weight.
+  * The image is divided into a grid of characters
+  * There are 16 columns and 6 rows
+  * Each row is divided in half: regular weight glyphs on the top half, bold glyphs on the bottom
+  * For a NxM image each character glyph will be N/16 pixels wide and M/(12*2) pixels high
+
 ## loading and playing frames
 
 Each part is scanned and loaded directly from the zip archive. Within a part directory, every file
diff --git a/cmds/incident/main.cpp b/cmds/incident/main.cpp
index 519852d..cdec6a0 100644
--- a/cmds/incident/main.cpp
+++ b/cmds/incident/main.cpp
@@ -148,9 +148,19 @@
 static int
 get_dest(const char* arg)
 {
-    if (strcmp(arg, "LOCAL") == 0) return 0;
-    if (strcmp(arg, "EXPLICIT") == 0) return 1;
-    if (strcmp(arg, "AUTOMATIC") == 0) return 2;
+    if (strcmp(arg, "L") == 0
+        || strcmp(arg, "LOCAL") == 0) {
+      return DEST_LOCAL;
+    }
+    if (strcmp(arg, "E") == 0
+        || strcmp(arg, "EXPLICIT") == 0) {
+      return DEST_EXPLICIT;
+    }
+    if (strcmp(arg, "A") == 0
+        || strcmp(arg, "AUTO") == 0
+        || strcmp(arg, "AUTOMATIC") == 0) {
+      return DEST_AUTOMATIC;
+    }
     return -1; // return the default value
 }
 
diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp
index 5db2239..44adaec 100644
--- a/cmds/incidentd/src/Privacy.cpp
+++ b/cmds/incidentd/src/Privacy.cpp
@@ -67,8 +67,14 @@
 
 PrivacySpec new_spec_from_args(int dest)
 {
-  if (dest < 0) return PrivacySpec();
-  return PrivacySpec(dest);
+    switch (dest) {
+        case android::os::DEST_AUTOMATIC:
+        case android::os::DEST_EXPLICIT:
+        case android::os::DEST_LOCAL:
+            return PrivacySpec(dest);
+        default:
+            return PrivacySpec();
+    }
 }
 
 PrivacySpec get_default_dropbox_spec() { return PrivacySpec(android::os::DEST_AUTOMATIC); }
\ No newline at end of file
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index eabbb96..565b092 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -137,7 +137,7 @@
 
 LOCAL_MODULE_CLASS := EXECUTABLES
 
-LOCAL_INIT_RC := statsd.rc
+#LOCAL_INIT_RC := statsd.rc
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 77b156f8..c990296 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -21,7 +21,7 @@
 option java_package = "com.android.os";
 option java_outer_classname = "AtomsProto";
 
-import "frameworks/base/core/proto/android/app/activitymanager.proto";
+import "frameworks/base/core/proto/android/app/enums.proto";
 
 /**
  * The master atom class. This message defines all of the available
@@ -44,7 +44,7 @@
         BleUnoptimizedScanStateChanged ble_unoptimized_scan_state_changed = 3;
         BleScanResultReceived ble_scan_result_received = 4;
         SensorStateChanged sensor_state_changed = 5;
-        GpsScanStateChanged gps_scan_state_changed = 6; // TODO: untested
+        GpsScanStateChanged gps_scan_state_changed = 6;
         SyncStateChanged sync_state_changed = 7;
         ScheduledJobStateChanged scheduled_job_state_changed = 8;
         ScreenBrightnessChanged screen_brightness_changed = 9;
@@ -185,9 +185,8 @@
 message UidProcessStateChanged {
     optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation
 
-    // The state.
-    // TODO: Use the real (mapped) process states.
-    optional android.app.ProcessState state = 2;
+    // The state, from frameworks/base/core/proto/android/app/enums.proto.
+    optional android.app.ProcessStateEnum state = 2;
 }
 
 /**
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 83b72d9..00d8658 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -63,7 +63,7 @@
 }
 
 static string getFilePath(const char* path, int64_t timestamp, int64_t uid, int64_t configID) {
-    return StringPrintf("%s/%lld-%d-%lld", path, (long long)timestamp, (int)uid,
+    return StringPrintf("%s/%lld_%d_%lld", path, (long long)timestamp, (int)uid,
                         (long long)configID);
 }
 
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index cd029c0..b58c523 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -129,6 +129,8 @@
 import com.android.internal.policy.DecorView;
 import com.android.internal.policy.PhoneWindow;
 
+import dalvik.system.VMRuntime;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -2136,11 +2138,15 @@
      * @param params non-null parameters to be combined with previously set parameters when entering
      * picture-in-picture.
      *
-     * @return true if the system puts this activity into picture-in-picture mode or was already
-     * in picture-in-picture mode (@see {@link #isInPictureInPictureMode())
+     * @return true if the system successfully put this activity into picture-in-picture mode or was
+     * already in picture-in-picture mode (@see {@link #isInPictureInPictureMode()). If the device
+     * does not support picture-in-picture, return false.
      */
     public boolean enterPictureInPictureMode(@NonNull PictureInPictureParams params) {
         try {
+            if (!deviceSupportsPictureInPictureMode()) {
+                return false;
+            }
             if (params == null) {
                 throw new IllegalArgumentException("Expected non-null picture-in-picture params");
             }
@@ -2168,6 +2174,9 @@
      */
     public void setPictureInPictureParams(@NonNull PictureInPictureParams params) {
         try {
+            if (!deviceSupportsPictureInPictureMode()) {
+                return;
+            }
             if (params == null) {
                 throw new IllegalArgumentException("Expected non-null picture-in-picture params");
             }
@@ -2190,6 +2199,13 @@
         }
     }
 
+    /**
+     * @return Whether this device supports picture-in-picture.
+     */
+    private boolean deviceSupportsPictureInPictureMode() {
+        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE);
+    }
+
     void dispatchMovedToDisplay(int displayId, Configuration config) {
         updateDisplay(displayId);
         onMovedToDisplay(displayId, config);
@@ -7099,11 +7115,12 @@
         mFragments.dispatchStart();
         mFragments.reportLoaderStart();
 
-        // This property is set for all builds except final release
-        boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
         boolean isAppDebuggable =
                 (mApplication.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
 
+        // This property is set for all builds except final release
+        boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
+
         if (isAppDebuggable || isDlwarningEnabled) {
             String dlwarning = getDlWarning();
             if (dlwarning != null) {
@@ -7124,6 +7141,28 @@
             }
         }
 
+        // We might disable this for final builds.
+        boolean isApiWarningEnabled = true;
+
+        if (isAppDebuggable || isApiWarningEnabled) {
+            if (VMRuntime.getRuntime().hasUsedHiddenApi()) {
+                String appName = getApplicationInfo().loadLabel(getPackageManager())
+                        .toString();
+                String warning = "Detected problems with API compatiblity\n"
+                                 + "(please consult log for detail)";
+                if (isAppDebuggable) {
+                    new AlertDialog.Builder(this)
+                        .setTitle(appName)
+                        .setMessage(warning)
+                        .setPositiveButton(android.R.string.ok, null)
+                        .setCancelable(false)
+                        .show();
+                } else {
+                    Toast.makeText(this, appName + "\n" + warning, Toast.LENGTH_LONG).show();
+                }
+            }
+        }
+
         mActivityTransitionState.enterReady(this);
     }
 
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 8035058..4d5ac6f 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -28,7 +28,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.UriPermission;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
@@ -576,18 +575,68 @@
     /** @hide Process does not exist. */
     public static final int PROCESS_STATE_NONEXISTENT = 19;
 
-    // NOTE: If PROCESS_STATEs are added or changed, then new fields must be added
-    // to frameworks/base/core/proto/android/app/activitymanager.proto and the following method must
+    // NOTE: If PROCESS_STATEs are added, then new fields must be added
+    // to frameworks/base/core/proto/android/app/enums.proto and the following method must
     // be updated to correctly map between them.
+    // However, if the current ActivityManager values are merely modified, no update should be made
+    // to enums.proto, to which values can only be added but never modified. Note that the proto
+    // versions do NOT have the ordering restrictions of the ActivityManager process state.
     /**
-     * Maps ActivityManager.PROCESS_STATE_ values to ProcessState enum.
+     * Maps ActivityManager.PROCESS_STATE_ values to enums.proto ProcessStateEnum value.
      *
      * @param amInt a process state of the form ActivityManager.PROCESS_STATE_
-     * @return the value of the corresponding ActivityManager's ProcessState enum.
+     * @return the value of the corresponding enums.proto ProcessStateEnum value.
      * @hide
      */
     public static final int processStateAmToProto(int amInt) {
-        return amInt * 100;
+        switch (amInt) {
+            case PROCESS_STATE_UNKNOWN:
+                return AppProtoEnums.PROCESS_STATE_UNKNOWN;
+            case PROCESS_STATE_PERSISTENT:
+                return AppProtoEnums.PROCESS_STATE_PERSISTENT;
+            case PROCESS_STATE_PERSISTENT_UI:
+                return AppProtoEnums.PROCESS_STATE_PERSISTENT_UI;
+            case PROCESS_STATE_TOP:
+                return AppProtoEnums.PROCESS_STATE_TOP;
+            case PROCESS_STATE_FOREGROUND_SERVICE:
+                return AppProtoEnums.PROCESS_STATE_FOREGROUND_SERVICE;
+            case PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
+                return AppProtoEnums.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+            case PROCESS_STATE_IMPORTANT_FOREGROUND:
+                return AppProtoEnums.PROCESS_STATE_IMPORTANT_FOREGROUND;
+            case PROCESS_STATE_IMPORTANT_BACKGROUND:
+                return AppProtoEnums.PROCESS_STATE_IMPORTANT_BACKGROUND;
+            case PROCESS_STATE_TRANSIENT_BACKGROUND:
+                return AppProtoEnums.PROCESS_STATE_TRANSIENT_BACKGROUND;
+            case PROCESS_STATE_BACKUP:
+                return AppProtoEnums.PROCESS_STATE_BACKUP;
+            case PROCESS_STATE_SERVICE:
+                return AppProtoEnums.PROCESS_STATE_SERVICE;
+            case PROCESS_STATE_RECEIVER:
+                return AppProtoEnums.PROCESS_STATE_RECEIVER;
+            case PROCESS_STATE_TOP_SLEEPING:
+                return AppProtoEnums.PROCESS_STATE_TOP_SLEEPING;
+            case PROCESS_STATE_HEAVY_WEIGHT:
+                return AppProtoEnums.PROCESS_STATE_HEAVY_WEIGHT;
+            case PROCESS_STATE_HOME:
+                return AppProtoEnums.PROCESS_STATE_HOME;
+            case PROCESS_STATE_LAST_ACTIVITY:
+                return AppProtoEnums.PROCESS_STATE_LAST_ACTIVITY;
+            case PROCESS_STATE_CACHED_ACTIVITY:
+                return AppProtoEnums.PROCESS_STATE_CACHED_ACTIVITY;
+            case PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+                return AppProtoEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+            case PROCESS_STATE_CACHED_RECENT:
+                return AppProtoEnums.PROCESS_STATE_CACHED_RECENT;
+            case PROCESS_STATE_CACHED_EMPTY:
+                return AppProtoEnums.PROCESS_STATE_CACHED_EMPTY;
+            case PROCESS_STATE_NONEXISTENT:
+                return AppProtoEnums.PROCESS_STATE_NONEXISTENT;
+            default:
+                // ActivityManager process state (amInt)
+                // could not be mapped to an AppProtoEnums ProcessState state.
+                return AppProtoEnums.PROCESS_STATE_UNKNOWN_TO_PROTO;
+        }
     }
 
     /** @hide The lowest process state number */
@@ -2686,17 +2735,22 @@
     /**
      * Permits an application to get the persistent URI permissions granted to another.
      *
-     * <p>Typically called by Settings.
+     * <p>Typically called by Settings or DocumentsUI, requires
+     * {@code GET_APP_GRANTED_URI_PERMISSIONS}.
      *
-     * @param packageName application to look for the granted permissions
+     * @param packageName application to look for the granted permissions, or {@code null} to get
+     * granted permissions for all applications
      * @return list of granted URI permissions
      *
      * @hide
      */
-    public ParceledListSlice<UriPermission> getGrantedUriPermissions(String packageName) {
+    public ParceledListSlice<GrantedUriPermission> getGrantedUriPermissions(
+            @Nullable String packageName) {
         try {
-            return getService().getGrantedUriPermissions(packageName,
-                    UserHandle.myUserId());
+            @SuppressWarnings("unchecked")
+            final ParceledListSlice<GrantedUriPermission> castedList = getService()
+                    .getGrantedUriPermissions(packageName, UserHandle.myUserId());
+            return castedList;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2705,7 +2759,7 @@
     /**
      * Permits an application to clear the persistent URI permissions granted to another.
      *
-     * <p>Typically called by Settings.
+     * <p>Typically called by Settings, requires {@code CLEAR_APP_GRANTED_URI_PERMISSIONS}.
      *
      * @param packageName application to clear its granted permissions
      *
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 4bcd677..fee5827 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -207,6 +207,12 @@
             "android.activity.taskOverlayCanResume";
 
     /**
+     * See {@link #setAvoidMoveToFront()}.
+     * @hide
+     */
+    private static final String KEY_AVOID_MOVE_TO_FRONT = "android.activity.avoidMoveToFront";
+
+    /**
      * Where the split-screen-primary stack should be positioned.
      * @hide
      */
@@ -307,6 +313,7 @@
     private boolean mDisallowEnterPictureInPictureWhileLaunching;
     private boolean mTaskOverlay;
     private boolean mTaskOverlayCanResume;
+    private boolean mAvoidMoveToFront;
     private AppTransitionAnimationSpec mAnimSpecs[];
     private int mRotationAnimationHint = -1;
     private Bundle mAppVerificationBundle;
@@ -923,6 +930,7 @@
         mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
         mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false);
         mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false);
+        mAvoidMoveToFront = opts.getBoolean(KEY_AVOID_MOVE_TO_FRONT, false);
         mSplitScreenCreateMode = opts.getInt(KEY_SPLIT_SCREEN_CREATE_MODE,
                 SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
         mDisallowEnterPictureInPictureWhileLaunching = opts.getBoolean(
@@ -1239,6 +1247,25 @@
         return mTaskOverlayCanResume;
     }
 
+    /**
+     * Sets whether the activity launched should not cause the activity stack it is contained in to
+     * be moved to the front as a part of launching.
+     *
+     * @hide
+     */
+    public void setAvoidMoveToFront() {
+        mAvoidMoveToFront = true;
+    }
+
+    /**
+     * @return whether the activity launch should prevent moving the associated activity stack to
+     *         the front.
+     * @hide
+     */
+    public boolean getAvoidMoveToFront() {
+        return mAvoidMoveToFront;
+    }
+
     /** @hide */
     public int getSplitScreenCreateMode() {
         return mSplitScreenCreateMode;
@@ -1416,6 +1443,7 @@
         b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
         b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
         b.putBoolean(KEY_TASK_OVERLAY_CAN_RESUME, mTaskOverlayCanResume);
+        b.putBoolean(KEY_AVOID_MOVE_TO_FRONT, mAvoidMoveToFront);
         b.putInt(KEY_SPLIT_SCREEN_CREATE_MODE, mSplitScreenCreateMode);
         b.putBoolean(KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING,
                 mDisallowEnterPictureInPictureWhileLaunching);
diff --git a/core/java/android/app/GrantedUriPermission.aidl b/core/java/android/app/GrantedUriPermission.aidl
new file mode 100644
index 0000000..2734af0
--- /dev/null
+++ b/core/java/android/app/GrantedUriPermission.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 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.app;
+
+/** @hide */
+parcelable GrantedUriPermission;
\ No newline at end of file
diff --git a/core/java/android/app/GrantedUriPermission.java b/core/java/android/app/GrantedUriPermission.java
new file mode 100644
index 0000000..9e84fe1
--- /dev/null
+++ b/core/java/android/app/GrantedUriPermission.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 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.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.UriPermission;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents an {@link UriPermission} granted to a package.
+ *
+ * {@hide}
+ */
+public class GrantedUriPermission implements Parcelable {
+
+    public final Uri uri;
+    public final String packageName;
+
+    public GrantedUriPermission(@NonNull Uri uri, @Nullable String packageName) {
+        this.uri = uri;
+        this.packageName = packageName;
+    }
+
+    @Override
+    public String toString() {
+        return packageName + ":" + uri;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(uri, flags);
+        out.writeString(packageName);
+    }
+
+    public static final Parcelable.Creator<GrantedUriPermission> CREATOR =
+            new Parcelable.Creator<GrantedUriPermission>() {
+                @Override
+                public GrantedUriPermission createFromParcel(Parcel in) {
+                    return new GrantedUriPermission(in);
+                }
+
+                @Override
+                public GrantedUriPermission[] newArray(int size) {
+                    return new GrantedUriPermission[size];
+                }
+            };
+
+    private GrantedUriPermission(Parcel in) {
+        uri = in.readParcelable(null);
+        packageName = in.readString();
+    }
+}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 9c15562..6dcecf1 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.app.ApplicationErrorReport;
 import android.app.ContentProviderHolder;
+import android.app.GrantedUriPermission;
 import android.app.IApplicationThread;
 import android.app.IActivityController;
 import android.app.IAppTask;
@@ -65,6 +66,7 @@
 import android.os.StrictMode;
 import android.os.WorkSource;
 import android.service.voice.IVoiceInteractionSession;
+import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationDefinition;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.IResultReceiver;
@@ -145,6 +147,7 @@
     void publishService(in IBinder token, in Intent intent, in IBinder service);
     void activityResumed(in IBinder token);
     void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
+    void setAgentApp(in String packageName, @nullable String agent);
     void setAlwaysFinish(boolean enabled);
     boolean startInstrumentation(in ComponentName className, in String profileFile,
             int flags, in Bundle arguments, in IInstrumentationWatcher watcher,
@@ -441,8 +444,9 @@
             in Bundle options, int userId);
     int startAssistantActivity(in String callingPackage, int callingPid, int callingUid,
             in Intent intent, in String resolvedType, in Bundle options, int userId);
-    int startRecentsActivity(in IAssistDataReceiver assistDataReceiver, in Bundle options,
-            in Bundle activityOptions, int userId);
+    void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver,
+            in IRecentsAnimationRunner recentsAnimationRunner);
+    void cancelRecentsAnimation();
     int startActivityFromRecents(int taskId, in Bundle options);
     Bundle getActivityOptions(in IBinder token);
     List<IBinder> getAppTasks(in String callingPackage);
@@ -569,7 +573,7 @@
             in Rect tempDockedTaskInsetBounds,
             in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
     int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
-    // Gets the URI permissions granted to an arbitrary package.
+    // Gets the URI permissions granted to an arbitrary package (or all packages if null)
     // NOTE: this is different from getPersistedUriPermissions(), which returns the URIs the package
     // granted to another packages (instead of those granted to it).
     ParceledListSlice getGrantedUriPermissions(in String packageName, int userId);
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 998ac5f..c34f4d9 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import android.app.ActivityThread.ActivityClientRecord;
+import android.app.servertransaction.PendingTransactionActions;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.os.Binder;
@@ -141,6 +143,21 @@
             }
             r.window = r.activity.getWindow();
             r.instanceState = null;
+
+            final ActivityClientRecord clientRecord = mActivityThread.getActivityClient(r);
+            final PendingTransactionActions pendingActions;
+
+            if (!r.activity.mFinished) {
+                // This matches pending actions set in ActivityThread#handleLaunchActivity
+                pendingActions = new PendingTransactionActions();
+                pendingActions.setOldState(clientRecord.state);
+                pendingActions.setRestoreInstanceState(true);
+                pendingActions.setCallOnPostCreate(true);
+            } else {
+                pendingActions = null;
+            }
+
+            mActivityThread.handleStartActivity(clientRecord, pendingActions);
             r.curState = STARTED;
             
             if (desiredState == RESUMED) {
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index 0ed1b08..6fbe9c6 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -87,6 +87,15 @@
     }
 
     /**
+     * Return a new ProfilerInfo instance, with fields populated from this object,
+     * and {@link agent} and {@link attachAgentDuringBind} as given.
+     */
+    public ProfilerInfo setAgent(String agent, boolean attachAgentDuringBind) {
+        return new ProfilerInfo(this.profileFile, this.profileFd, this.samplingInterval,
+                this.autoStopProfiler, this.streamingOutput, agent, attachAgentDuringBind);
+    }
+
+    /**
      * Close profileFd, if it is open. The field will be null after a call to this function.
      */
     public void closeFd() {
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
index 2629929..5a1216b7 100644
--- a/core/java/android/content/AbstractThreadedSyncAdapter.java
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.Trace;
 import android.util.Log;
 
@@ -166,6 +167,12 @@
 
     private class ISyncAdapterImpl extends ISyncAdapter.Stub {
         @Override
+        public void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb)
+                throws RemoteException {
+            cb.onUnsyncableAccountDone(AbstractThreadedSyncAdapter.this.onUnsyncableAccount());
+        }
+
+        @Override
         public void startSync(ISyncContext syncContext, String authority, Account account,
                 Bundle extras) {
             if (ENABLE_LOG) {
@@ -374,6 +381,26 @@
     }
 
     /**
+     * Allows to defer syncing until all accounts are properly set up.
+     *
+     * <p>Called when a account / authority pair
+     * <ul>
+     * <li>that can be handled by this adapter</li>
+     * <li>{@link ContentResolver#requestSync(SyncRequest) is synced}</li>
+     * <li>and the account/provider {@link ContentResolver#getIsSyncable(Account, String) has
+     * unknown state (<0)}.</li>
+     * </ul>
+     *
+     * <p>This might be called on a different service connection as {@link #onPerformSync}.
+     *
+     * @return If {@code false} syncing is deferred. Returns {@code true} by default, i.e. by
+     *         default syncing starts immediately.
+     */
+    public boolean onUnsyncableAccount() {
+        return true;
+    }
+
+    /**
      * Perform a sync for this account. SyncAdapter-specific parameters may
      * be specified in extras, which is guaranteed to not be null. Invocations
      * of this method are guaranteed to be serialized.
diff --git a/core/java/android/content/ISyncAdapter.aidl b/core/java/android/content/ISyncAdapter.aidl
index 4660527..0eb581e 100644
--- a/core/java/android/content/ISyncAdapter.aidl
+++ b/core/java/android/content/ISyncAdapter.aidl
@@ -19,6 +19,7 @@
 import android.accounts.Account;
 import android.os.Bundle;
 import android.content.ISyncContext;
+import android.content.ISyncAdapterUnsyncableAccountCallback;
 
 /**
  * Interface used to control the sync activity on a SyncAdapter
@@ -26,6 +27,14 @@
  */
 oneway interface ISyncAdapter {
     /**
+     * Called before {@link #startSync}. This allows the adapter to defer syncs until the
+     * adapter is ready for the account
+     *
+     * @param cb If called back with {@code false} accounts are not synced.
+     */
+    void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb);
+
+    /**
      * Initiate a sync for this account. SyncAdapter-specific parameters may
      * be specified in extras, which is guaranteed to not be null.
      *
diff --git a/core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl b/core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl
new file mode 100644
index 0000000..a738ac2
--- /dev/null
+++ b/core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 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;
+
+/**
+ * Callback for {@link ISyncAdapter#onUnsyncableAccount}
+ * @hide
+ */
+oneway interface ISyncAdapterUnsyncableAccountCallback {
+    /**
+     * Deliver the result for {@link ISyncAdapter#onUnsyncableAccount}
+     *
+     * @param isReady Iff {@code false} account is not synced.
+     */
+    void onUnsyncableAccountDone(boolean isReady);
+}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 746a090..f6697e8 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -602,6 +602,13 @@
      */
     public static final int PRIVATE_FLAG_VENDOR = 1 << 18;
 
+    /**
+     * Value for {@linl #privateFlags}: whether this app is pre-installed on the
+     * product partition of the system image.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_PRODUCT = 1 << 19;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
             PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -619,6 +626,7 @@
             PRIVATE_FLAG_OEM,
             PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
             PRIVATE_FLAG_PRIVILEGED,
+            PRIVATE_FLAG_PRODUCT,
             PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
             PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
             PRIVATE_FLAG_VENDOR,
@@ -1699,6 +1707,11 @@
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
     }
 
+    /** @hide */
+    public boolean isProduct() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+    }
+
     /**
      * Returns whether or not this application was installed as a virtual preload.
      */
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3bb812b..d5c88aa 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6393,6 +6393,11 @@
         }
 
         /** @hide */
+        public boolean isProduct() {
+            return applicationInfo.isProduct();
+        }
+
+        /** @hide */
         public boolean isPrivileged() {
             return applicationInfo.isPrivilegedApp();
         }
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 1de8882..fdea5a2 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1246,7 +1246,7 @@
             int repeat;
             if (effect instanceof VibrationEffect.OneShot) {
                 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect;
-                pattern = new long[] { 0, oneShot.getTiming() };
+                pattern = new long[] { 0, oneShot.getDuration() };
                 repeat = -1;
             } else if (effect instanceof VibrationEffect.Waveform) {
                 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 7528bc3..a817f33 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -265,6 +265,10 @@
      */
     public static final int IME_VISIBLE = 0x2;
 
+    // Min and max values for back disposition.
+    private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
+    private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_WILL_DISMISS;
+
     InputMethodManager mImm;
     
     int mTheme = 0;
@@ -501,9 +505,8 @@
             }
             clearInsetOfPreviousIme();
             // If user uses hard keyboard, IME button should always be shown.
-            boolean showing = isInputViewShown();
             mImm.setImeWindowStatus(mToken, mStartInputToken,
-                    IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
+                    mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
             if (resultReceiver != null) {
                 resultReceiver.send(wasVis != isInputViewShown()
                         ? InputMethodManager.RESULT_SHOWN
@@ -1014,7 +1017,16 @@
     }
     
     public void setBackDisposition(int disposition) {
+        if (disposition == mBackDisposition) {
+            return;
+        }
+        if (disposition > BACK_DISPOSITION_MAX || disposition < BACK_DISPOSITION_MIN) {
+            Log.e(TAG, "Invalid back disposition value (" + disposition + ") specified.");
+            return;
+        }
         mBackDisposition = disposition;
+        mImm.setImeWindowStatus(mToken, mStartInputToken, mapToImeWindowStatus(isInputViewShown()),
+                mBackDisposition);
     }
 
     public int getBackDisposition() {
@@ -1762,7 +1774,7 @@
             startExtractingText(false);
         }
 
-        final int nextImeWindowStatus = IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
+        final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown());
         if (previousImeWindowStatus != nextImeWindowStatus) {
             mImm.setImeWindowStatus(mToken, mStartInputToken, nextImeWindowStatus,
                     mBackDisposition);
@@ -1889,6 +1901,7 @@
         mInputStarted = false;
         mStartedInputConnection = null;
         mCurCompletions = null;
+        mBackDisposition = BACK_DISPOSITION_DEFAULT;
     }
 
     void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
@@ -2104,7 +2117,11 @@
      * them to perform navigation in the underlying application.
      */
     public boolean onKeyDown(int keyCode, KeyEvent event) {
+
         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+            if (mBackDisposition == BACK_DISPOSITION_WILL_NOT_DISMISS) {
+                return false;
+            }
             final ExtractEditText eet = getExtractEditTextIfVisible();
             if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
                 return true;
@@ -2738,6 +2755,10 @@
         mImm.exposeContent(mToken, inputContentInfo, getCurrentInputEditorInfo());
     }
 
+    private static int mapToImeWindowStatus(boolean isInputViewShown) {
+        return IME_ACTIVE | (isInputViewShown ? IME_VISIBLE : 0);
+    }
+
     /**
      * Performs a dump of the InputMethodService's internal state.  Override
      * to add your own information to the dump.
diff --git a/core/java/android/net/metrics/NetworkMetrics.java b/core/java/android/net/metrics/NetworkMetrics.java
index 2b662a0..2425bba 100644
--- a/core/java/android/net/metrics/NetworkMetrics.java
+++ b/core/java/android/net/metrics/NetworkMetrics.java
@@ -96,6 +96,13 @@
         }
     }
 
+    /** Accumulate a single netd sock_diag poll result reported by netd. */
+    public void addTcpStatsResult(int sent, int lost, int rttUs, int sentAckDiffMs) {
+        pendingSummary.tcpLossRate.count(lost, sent);
+        pendingSummary.roundTripTimeUs.count(rttUs);
+        pendingSummary.sentAckTimeDiffenceMs.count(sentAckDiffMs);
+    }
+
     /** Represents running sums for dns and connect average error counts and average latencies. */
     public static class Summary {
 
@@ -109,6 +116,13 @@
         public final Metrics connectLatencies = new Metrics();
         // Blocking and non blocking connect error rate measured in percentage points.
         public final Metrics connectErrorRate = new Metrics();
+        // TCP socket packet loss stats collected from Netlink sock_diag.
+        public final Metrics tcpLossRate = new Metrics();
+        // TCP averaged microsecond round-trip-time stats collected from Netlink sock_diag.
+        public final Metrics roundTripTimeUs = new Metrics();
+        // TCP stats collected from Netlink sock_diag that averages millisecond per-socket
+        // differences between last packet sent timestamp and last ack received timestamp.
+        public final Metrics sentAckTimeDiffenceMs = new Metrics();
 
         public Summary(int netId, long transports) {
             this.netId = netId;
@@ -120,6 +134,7 @@
             dnsErrorRate.merge(that.dnsErrorRate);
             connectLatencies.merge(that.connectLatencies);
             connectErrorRate.merge(that.connectErrorRate);
+            tcpLossRate.merge(that.tcpLossRate);
         }
 
         @Override
@@ -135,6 +150,10 @@
             j.add(String.format("connect avg=%dms max=%dms err=%.1f%% tot=%d",
                     (int) connectLatencies.average(), (int) connectLatencies.max,
                     100 * connectErrorRate.average(), connectErrorRate.count));
+            j.add(String.format("tcp avg_loss=%.1f%% total_sent=%d total_lost=%d",
+                    100 * tcpLossRate.average(), tcpLossRate.count, (int) tcpLossRate.sum));
+            j.add(String.format("tcp rtt=%dms", (int) (roundTripTimeUs.average() / 1000)));
+            j.add(String.format("tcp sent-ack_diff=%dms", (int) sentAckTimeDiffenceMs.average()));
             return j.toString();
         }
     }
@@ -152,7 +171,11 @@
         }
 
         void count(double value) {
-            count++;
+            count(value, 1);
+        }
+
+        void count(double value, int subcount) {
+            count += subcount;
             sum += value;
             max = Math.max(max, value);
         }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 158041d..03203d0 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -41,6 +41,7 @@
     private static final String ENV_OEM_ROOT = "OEM_ROOT";
     private static final String ENV_ODM_ROOT = "ODM_ROOT";
     private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
+    private static final String ENV_PRODUCT_ROOT = "PRODUCT_ROOT";
 
     /** {@hide} */
     public static final String DIR_ANDROID = "Android";
@@ -62,6 +63,7 @@
     private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
     private static final File DIR_ODM_ROOT = getDirectory(ENV_ODM_ROOT, "/odm");
     private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
+    private static final File DIR_PRODUCT_ROOT = getDirectory(ENV_PRODUCT_ROOT, "/product");
 
     private static UserEnvironment sCurrentUser;
     private static boolean sUserRequired;
@@ -180,6 +182,16 @@
     }
 
     /**
+     * Return root directory of the "product" partition holding product-specific
+     * customizations if any. If present, the partition is mounted read-only.
+     *
+     * @hide
+     */
+    public static File getProductDirectory() {
+        return DIR_PRODUCT_ROOT;
+    }
+
+    /**
      * Return the system directory for a user. This is for use by system
      * services to store files relating to the user. This directory will be
      * automatically deleted when the user is removed.
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index da0ed54..b6f16a7 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -16,7 +16,9 @@
 
 package android.os;
 
+import android.hardware.vibrator.V1_0.Constants.EffectStrength;
 import android.hardware.vibrator.V1_1.Constants.Effect_1_1;
+import android.util.MathUtils;
 
 import java.util.Arrays;
 
@@ -36,6 +38,12 @@
     public static final int DEFAULT_AMPLITUDE = -1;
 
     /**
+     * The maximum amplitude value
+     * @hide
+     */
+    public static final int MAX_AMPLITUDE = 255;
+
+    /**
      * A click effect.
      *
      * @see #get(int)
@@ -198,38 +206,75 @@
     /** @hide */
     public abstract void validate();
 
+    /**
+     * Gets the estimated duration of the vibration in milliseconds.
+     *
+     * For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this
+     * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. Prebaked effects where
+     * the length is device and potentially run-time dependent), this returns -1.
+     *
+     * @hide
+     */
+    public abstract long getDuration();
+
+    /**
+     * Scale the amplitude with the given constraints.
+     *
+     * This assumes that the previous value was in the range [0, MAX_AMPLITUDE]
+     * @hide
+     */
+    protected static int scale(int amplitude, float gamma, int maxAmplitude) {
+        float val = MathUtils.pow(amplitude / (float) MAX_AMPLITUDE, gamma);
+        return (int) (val * maxAmplitude);
+    }
+
     /** @hide */
     public static class OneShot extends VibrationEffect implements Parcelable {
-        private long mTiming;
-        private int mAmplitude;
+        private final long mDuration;
+        private final int mAmplitude;
 
         public OneShot(Parcel in) {
-            this(in.readLong(), in.readInt());
+            mDuration = in.readLong();
+            mAmplitude = in.readInt();
         }
 
         public OneShot(long milliseconds, int amplitude) {
-            mTiming = milliseconds;
+            mDuration = milliseconds;
             mAmplitude = amplitude;
         }
 
-        public long getTiming() {
-            return mTiming;
+        @Override
+        public long getDuration() {
+            return mDuration;
         }
 
         public int getAmplitude() {
             return mAmplitude;
         }
 
+        /**
+         * Scale the amplitude of this effect.
+         *
+         * @param gamma the gamma adjustment to apply
+         * @param maxAmplitude the new maximum amplitude of the effect
+         *
+         * @return A {@link OneShot} effect with the same timing but scaled amplitude.
+         */
+        public VibrationEffect scale(float gamma, int maxAmplitude) {
+            int newAmplitude = scale(mAmplitude, gamma, maxAmplitude);
+            return new OneShot(mDuration, newAmplitude);
+        }
+
         @Override
         public void validate() {
             if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) {
                 throw new IllegalArgumentException(
-                        "amplitude must either be DEFAULT_AMPLITUDE, " +
-                        "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
+                        "amplitude must either be DEFAULT_AMPLITUDE, "
+                        + "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
             }
-            if (mTiming <= 0) {
+            if (mDuration <= 0) {
                 throw new IllegalArgumentException(
-                        "timing must be positive (timing=" + mTiming + ")");
+                        "duration must be positive (duration=" + mDuration + ")");
             }
         }
 
@@ -239,26 +284,26 @@
                 return false;
             }
             VibrationEffect.OneShot other = (VibrationEffect.OneShot) o;
-            return other.mTiming == mTiming && other.mAmplitude == mAmplitude;
+            return other.mDuration == mDuration && other.mAmplitude == mAmplitude;
         }
 
         @Override
         public int hashCode() {
             int result = 17;
-            result = 37 * (int) mTiming;
-            result = 37 * mAmplitude;
+            result += 37 * (int) mDuration;
+            result += 37 * mAmplitude;
             return result;
         }
 
         @Override
         public String toString() {
-            return "OneShot{mTiming=" + mTiming +", mAmplitude=" + mAmplitude + "}";
+            return "OneShot{mDuration=" + mDuration + ", mAmplitude=" + mAmplitude + "}";
         }
 
         @Override
         public void writeToParcel(Parcel out, int flags) {
             out.writeInt(PARCEL_TOKEN_ONE_SHOT);
-            out.writeLong(mTiming);
+            out.writeLong(mDuration);
             out.writeInt(mAmplitude);
         }
 
@@ -279,9 +324,9 @@
 
     /** @hide */
     public static class Waveform extends VibrationEffect implements Parcelable {
-        private long[] mTimings;
-        private int[] mAmplitudes;
-        private int mRepeat;
+        private final long[] mTimings;
+        private final int[] mAmplitudes;
+        private final int mRepeat;
 
         public Waveform(Parcel in) {
             this(in.createLongArray(), in.createIntArray(), in.readInt());
@@ -308,34 +353,68 @@
         }
 
         @Override
+        public long getDuration() {
+            if (mRepeat >= 0) {
+                return Long.MAX_VALUE;
+            }
+            long duration = 0;
+            for (long d : mTimings) {
+                duration += d;
+            }
+            return duration;
+        }
+
+        /**
+         * Scale the Waveform with the given gamma and new max amplitude.
+         *
+         * @param gamma the gamma adjustment to apply
+         * @param maxAmplitude the new maximum amplitude of the effect
+         *
+         * @return A {@link Waveform} effect with the same timings and repeat index
+         *         but scaled amplitude.
+         */
+        public VibrationEffect scale(float gamma, int maxAmplitude) {
+            if (gamma == 1.0f && maxAmplitude == MAX_AMPLITUDE) {
+                // Just return a copy of the original if there's no scaling to be done.
+                return new Waveform(mTimings, mAmplitudes, mRepeat);
+            }
+
+            int[] scaledAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length);
+            for (int i = 0; i < scaledAmplitudes.length; i++) {
+                scaledAmplitudes[i] = scale(scaledAmplitudes[i], gamma, maxAmplitude);
+            }
+            return new Waveform(mTimings, scaledAmplitudes, mRepeat);
+        }
+
+        @Override
         public void validate() {
             if (mTimings.length != mAmplitudes.length) {
                 throw new IllegalArgumentException(
-                        "timing and amplitude arrays must be of equal length" +
-                        " (timings.length=" + mTimings.length +
-                        ", amplitudes.length=" + mAmplitudes.length + ")");
+                        "timing and amplitude arrays must be of equal length"
+                        + " (timings.length=" + mTimings.length
+                        + ", amplitudes.length=" + mAmplitudes.length + ")");
             }
             if (!hasNonZeroEntry(mTimings)) {
-                throw new IllegalArgumentException("at least one timing must be non-zero" +
-                        " (timings=" + Arrays.toString(mTimings) + ")");
+                throw new IllegalArgumentException("at least one timing must be non-zero"
+                        + " (timings=" + Arrays.toString(mTimings) + ")");
             }
             for (long timing : mTimings) {
                 if (timing < 0) {
-                    throw new IllegalArgumentException("timings must all be >= 0" +
-                            " (timings=" + Arrays.toString(mTimings) + ")");
+                    throw new IllegalArgumentException("timings must all be >= 0"
+                            + " (timings=" + Arrays.toString(mTimings) + ")");
                 }
             }
             for (int amplitude : mAmplitudes) {
                 if (amplitude < -1 || amplitude > 255) {
                     throw new IllegalArgumentException(
-                            "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255" +
-                            " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
+                            "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255"
+                            + " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
                 }
             }
             if (mRepeat < -1 || mRepeat >= mTimings.length) {
                 throw new IllegalArgumentException(
-                        "repeat index must be within the bounds of the timings array" +
-                        " (timings.length=" + mTimings.length + ", index=" + mRepeat +")");
+                        "repeat index must be within the bounds of the timings array"
+                        + " (timings.length=" + mTimings.length + ", index=" + mRepeat + ")");
             }
         }
 
@@ -345,26 +424,26 @@
                 return false;
             }
             VibrationEffect.Waveform other = (VibrationEffect.Waveform) o;
-            return Arrays.equals(mTimings, other.mTimings) &&
-                Arrays.equals(mAmplitudes, other.mAmplitudes) &&
-                mRepeat == other.mRepeat;
+            return Arrays.equals(mTimings, other.mTimings)
+                && Arrays.equals(mAmplitudes, other.mAmplitudes)
+                && mRepeat == other.mRepeat;
         }
 
         @Override
         public int hashCode() {
             int result = 17;
-            result = 37 * Arrays.hashCode(mTimings);
-            result = 37 * Arrays.hashCode(mAmplitudes);
-            result = 37 * mRepeat;
+            result += 37 * Arrays.hashCode(mTimings);
+            result += 37 * Arrays.hashCode(mAmplitudes);
+            result += 37 * mRepeat;
             return result;
         }
 
         @Override
         public String toString() {
-            return "Waveform{mTimings=" + Arrays.toString(mTimings) +
-                ", mAmplitudes=" + Arrays.toString(mAmplitudes) +
-                ", mRepeat=" + mRepeat +
-                "}";
+            return "Waveform{mTimings=" + Arrays.toString(mTimings)
+                + ", mAmplitudes=" + Arrays.toString(mAmplitudes)
+                + ", mRepeat=" + mRepeat
+                + "}";
         }
 
         @Override
@@ -402,16 +481,20 @@
 
     /** @hide */
     public static class Prebaked extends VibrationEffect implements Parcelable {
-        private int mEffectId;
-        private boolean mFallback;
+        private final int mEffectId;
+        private final boolean mFallback;
+
+        private int mEffectStrength;
 
         public Prebaked(Parcel in) {
             this(in.readInt(), in.readByte() != 0);
+            mEffectStrength = in.readInt();
         }
 
         public Prebaked(int effectId, boolean fallback) {
             mEffectId = effectId;
             mFallback = fallback;
+            mEffectStrength = EffectStrength.MEDIUM;
         }
 
         public int getId() {
@@ -427,6 +510,39 @@
         }
 
         @Override
+        public long getDuration() {
+            return -1;
+        }
+
+        /**
+         * Set the effect strength of the prebaked effect.
+         */
+        public void setEffectStrength(int strength) {
+            if (!isValidEffectStrength(strength)) {
+                throw new IllegalArgumentException("Invalid effect strength: " + strength);
+            }
+            mEffectStrength = strength;
+        }
+
+        /**
+         * Set the effect strength.
+         */
+        public int getEffectStrength() {
+            return mEffectStrength;
+        }
+
+        private static boolean isValidEffectStrength(int strength) {
+            switch (strength) {
+                case EffectStrength.LIGHT:
+                case EffectStrength.MEDIUM:
+                case EffectStrength.STRONG:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        @Override
         public void validate() {
             switch (mEffectId) {
                 case EFFECT_CLICK:
@@ -437,6 +553,10 @@
                     throw new IllegalArgumentException(
                             "Unknown prebaked effect type (value=" + mEffectId + ")");
             }
+            if (!isValidEffectStrength(mEffectStrength)) {
+                throw new IllegalArgumentException(
+                        "Unknown prebaked effect strength (value=" + mEffectStrength + ")");
+            }
         }
 
         @Override
@@ -445,17 +565,25 @@
                 return false;
             }
             VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o;
-            return mEffectId == other.mEffectId && mFallback == other.mFallback;
+            return mEffectId == other.mEffectId
+                && mFallback == other.mFallback
+                && mEffectStrength == other.mEffectStrength;
         }
 
         @Override
         public int hashCode() {
-            return mEffectId;
+            int result = 17;
+            result += 37 * mEffectId;
+            result += 37 * mEffectStrength;
+            return result;
         }
 
         @Override
         public String toString() {
-            return "Prebaked{mEffectId=" + mEffectId + ", mFallback=" + mFallback + "}";
+            return "Prebaked{mEffectId=" + mEffectId
+                + ", mEffectStrength=" + mEffectStrength
+                + ", mFallback=" + mFallback
+                + "}";
         }
 
 
@@ -464,6 +592,7 @@
             out.writeInt(PARCEL_TOKEN_EFFECT);
             out.writeInt(mEffectId);
             out.writeByte((byte) (mFallback ? 1 : 0));
+            out.writeInt(mEffectStrength);
         }
 
         public static final Parcelable.Creator<Prebaked> CREATOR =
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 8078fb8..f1f6f41 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.IntDef;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.app.ActivityThread;
@@ -23,6 +24,9 @@
 import android.media.AudioAttributes;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Class that operates the vibrator on the device.
  * <p>
@@ -33,6 +37,40 @@
 public abstract class Vibrator {
     private static final String TAG = "Vibrator";
 
+    /**
+     * Vibration intensity: no vibrations.
+     * @hide
+     */
+    public static final int VIBRATION_INTENSITY_OFF = 0;
+
+    /**
+     * Vibration intensity: low.
+     * @hide
+     */
+    public static final int VIBRATION_INTENSITY_LOW = 1;
+
+    /**
+     * Vibration intensity: medium.
+     * @hide
+     */
+    public static final int VIBRATION_INTENSITY_MEDIUM = 2;
+
+    /**
+     * Vibration intensity: high.
+     * @hide
+     */
+    public static final int VIBRATION_INTENSITY_HIGH = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "VIBRATION_INTENSITY_" }, value = {
+        VIBRATION_INTENSITY_OFF,
+        VIBRATION_INTENSITY_LOW,
+        VIBRATION_INTENSITY_MEDIUM,
+        VIBRATION_INTENSITY_HIGH
+    })
+    public @interface VibrationIntensity{}
+
     private final String mPackageName;
 
     /**
@@ -50,6 +88,22 @@
     }
 
     /**
+     * Get the default vibration intensity for haptic feedback.
+     * @hide
+     */
+    public int getDefaultHapticFeedbackIntensity() {
+        return VIBRATION_INTENSITY_MEDIUM;
+    }
+
+    /**
+     * Get the default vibration intensity for notifications and ringtones.
+     * @hide
+     */
+    public int getDefaultNotificationVibrationIntensity() {
+        return VIBRATION_INTENSITY_HIGH;
+    }
+
+    /**
      * Check whether the hardware has a vibrator.
      *
      * @return True if the hardware has a vibrator, else false.
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index e7fd59e..d96316a 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -738,7 +738,9 @@
     private static final String PATH_DOCUMENT = "document";
     private static final String PATH_CHILDREN = "children";
     private static final String PATH_SEARCH = "search";
-    private static final String PATH_TREE = "tree";
+    // TODO(b/72055774): make private again once ScopedAccessProvider is refactored
+    /** {@hide} */
+    public static final String PATH_TREE = "tree";
 
     private static final String PARAM_QUERY = "query";
     private static final String PARAM_MANAGE = "manage";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e957842..26495cd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3175,6 +3175,43 @@
         private static final Validator VIBRATE_INPUT_DEVICES_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
+         * The intensity of notification vibrations, if configurable.
+         *
+         * Not all devices are capable of changing their vibration intensity; on these devices
+         * there will likely be no difference between the various vibration intensities except for
+         * intensity 0 (off) and the rest.
+         *
+         * <b>Values:</b><br/>
+         * 0 - Vibration is disabled<br/>
+         * 1 - Weak vibrations<br/>
+         * 2 - Medium vibrations<br/>
+         * 3 - Strong vibrations
+         * @hide
+         */
+        public static final String NOTIFICATION_VIBRATION_INTENSITY =
+                "notification_vibration_intensity";
+
+        /**
+         * The intensity of haptic feedback vibrations, if configurable.
+         *
+         * Not all devices are capable of changing their feedback intensity; on these devices
+         * there will likely be no difference between the various vibration intensities except for
+         * intensity 0 (off) and the rest.
+         *
+         * <b>Values:</b><br/>
+         * 0 - Vibration is disabled<br/>
+         * 1 - Weak vibrations<br/>
+         * 2 - Medium vibrations<br/>
+         * 3 - Strong vibrations
+         * @hide
+         */
+        public static final String HAPTIC_FEEDBACK_INTENSITY =
+                "haptic_feedback_intensity";
+
+        private static final Validator VIBRATION_INTENSITY_VALIDATOR =
+                new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+
+        /**
          * Ringer volume. This is used internally, changing this value will not
          * change the volume. See AudioManager.
          *
@@ -3995,7 +4032,9 @@
             LOCK_TO_APP_ENABLED,
             NOTIFICATION_SOUND,
             ACCELEROMETER_ROTATION,
-            SHOW_BATTERY_PERCENT
+            SHOW_BATTERY_PERCENT,
+            NOTIFICATION_VIBRATION_INTENSITY,
+            HAPTIC_FEEDBACK_INTENSITY,
         };
 
         /**
@@ -4136,6 +4175,8 @@
             VALIDATORS.put(MODE_RINGER_STREAMS_AFFECTED, MODE_RINGER_STREAMS_AFFECTED_VALIDATOR);
             VALIDATORS.put(MUTE_STREAMS_AFFECTED, MUTE_STREAMS_AFFECTED_VALIDATOR);
             VALIDATORS.put(VIBRATE_ON, VIBRATE_ON_VALIDATOR);
+            VALIDATORS.put(NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
+            VALIDATORS.put(HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
             VALIDATORS.put(RINGTONE, RINGTONE_VALIDATOR);
             VALIDATORS.put(NOTIFICATION_SOUND, NOTIFICATION_SOUND_VALIDATOR);
             VALIDATORS.put(ALARM_ALERT, ALARM_ALERT_VALIDATOR);
diff --git a/core/java/android/provider/SettingsSlicesContract.java b/core/java/android/provider/SettingsSlicesContract.java
new file mode 100644
index 0000000..f79d852
--- /dev/null
+++ b/core/java/android/provider/SettingsSlicesContract.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 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.provider;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+
+/**
+ * Provides a contract for platform-supported Settings {@link android.app.slice.Slice Slices}.
+ * <p>
+ * Contains definitions for the supported {@link android.app.slice.SliceProvider SliceProvider}
+ * authority, authority {@link Uri}, and key constants.
+ * <p>
+ * {@link android.app.slice.Slice Slice} presenters interested in learning meta-data about the
+ * {@link android.app.slice.Slice Slice} should read the {@link android.app.slice.Slice Slice}
+ * object at runtime.
+ * <p>
+ * {@link Uri} builder example:
+ * <pre>
+ * Uri wifiActionUri = AUTHORITY_URI
+ *         .buildUpon()
+ *         .appendPath(PATH_SETTING_ACTION)
+ *         .appendPath(KEY_WIFI)
+ *         .build();
+ * Uri bluetoothIntentUri = AUTHORITY_URI
+ *         .buildUpon()
+ *         .appendPath(PATH_SETTING_INTENT)
+ *         .appendPath(KEY_BLUETOOTH)
+ *         .build();
+ * </pre>
+ */
+public class SettingsSlicesContract {
+    private SettingsSlicesContract() {
+    }
+
+    /**
+     * Authority for platform Settings Slices.
+     */
+    public static final String AUTHORITY = "android.settings.slices";
+
+    /**
+     * A content:// style uri to the Settings Slices authority, {@link #AUTHORITY}.
+     */
+    public static final Uri BASE_URI = new Uri.Builder()
+            .scheme(ContentResolver.SCHEME_CONTENT)
+            .authority(AUTHORITY)
+            .build();
+
+    /**
+     * {@link Uri} path indicating that the requested {@link android.app.slice.Slice Slice} should
+     * have inline controls for the corresponding setting.
+     * <p>
+     * This path will only contain Slices defined by keys in this class.
+     */
+    public static final String PATH_SETTING_ACTION = "action";
+
+    /**
+     * {@link Uri} path indicating that the requested {@link android.app.slice.Slice Slice} should
+     * be {@link android.content.Intent Intent}-only.
+     * <p>
+     * {@link android.app.slice.Slice Slices} with actions should use the {@link
+     * #PATH_SETTING_ACTION} path.
+     * <p>
+     * This path will only contain Slices defined by keys in this class
+     */
+    public static final String PATH_SETTING_INTENT = "intent";
+
+    /**
+     * {@link Uri} key for the Airplane Mode setting.
+     */
+    public static final String KEY_AIRPLANE_MODE = "airplane_mode";
+
+    /**
+     * {@link Uri} key for the Battery Saver setting.
+     */
+    public static final String KEY_BATTERY_SAVER = "battery_saver";
+
+    /**
+     * {@link Uri} key for the Bluetooth setting.
+     */
+    public static final String KEY_BLUETOOTH = "bluetooth";
+
+    /**
+     * {@link Uri} key for the Location setting.
+     */
+    public static final String KEY_LOCATION = "location";
+
+    /**
+     * {@link Uri} key for the Wi-fi setting.
+     */
+    public static final String KEY_WIFI = "wifi";
+}
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
new file mode 100644
index 0000000..5607b11
--- /dev/null
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 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.view;
+
+import android.app.ActivityManager;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.graphics.GraphicBuffer;
+
+/**
+ * Passed to the {@link IRecentsAnimationRunner} in order for the runner to control to let the
+ * runner control certain aspects of the recents animation, and to notify window manager when the
+ * animation has completed.
+ *
+ * {@hide}
+ */
+interface IRecentsAnimationController {
+
+    /**
+     * Takes a screenshot of the task associated with the given {@param taskId}. Only valid for the
+     * current set of task ids provided to the handler.
+     */
+    ActivityManager.TaskSnapshot screenshotTask(int taskId);
+
+    /**
+     * Notifies to the system that the animation into Recents should end, and all leashes associated
+     * with remote animation targets should be relinquished. If {@param moveHomeToTop} is true, then
+     * the home activity should be moved to the top. Otherwise, the home activity is hidden and the
+     * user is returned to the app.
+     */
+    void finish(boolean moveHomeToTop);
+
+    /**
+     * Called by the handler to indicate that the recents animation input consumer should be
+     * enabled. This is currently used to work around an issue where registering an input consumer
+     * mid-animation causes the existing motion event chain to be canceled. Instead, the caller
+     * may register the recents animation input consumer prior to starting the recents animation
+     * and then enable it mid-animation to start receiving touch events.
+     */
+    void setInputConsumerEnabled(boolean enabled);
+}
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl
new file mode 100644
index 0000000..ea6226b
--- /dev/null
+++ b/core/java/android/view/IRecentsAnimationRunner.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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.view;
+
+import android.view.RemoteAnimationTarget;
+import android.view.IRecentsAnimationController;
+
+/**
+ * Interface that is used to callback from window manager to the process that runs a recents
+ * animation to start or cancel it.
+ *
+ * {@hide}
+ */
+oneway interface IRecentsAnimationRunner {
+
+    /**
+     * Called when the system is ready for the handler to start animating all the visible tasks.
+     */
+    void onAnimationStart(in IRecentsAnimationController controller,
+            in RemoteAnimationTarget[] apps);
+
+    /**
+     * Called when the system needs to cancel the current animation. This can be due to the
+     * wallpaper not drawing in time, or the handler not finishing the animation within a predefined
+     * amount of time.
+     */
+    void onAnimationCanceled();
+}
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index f39e618..c28c389 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.annotation.IntDef;
+import android.app.WindowConfiguration;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Parcel;
@@ -98,8 +99,14 @@
      */
     public final Rect sourceContainerBounds;
 
+    /**
+     * The window configuration for the target.
+     */
+    public final WindowConfiguration windowConfiguration;
+
     public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
-            Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds) {
+            Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds,
+            WindowConfiguration windowConfig) {
         this.mode = mode;
         this.taskId = taskId;
         this.leash = leash;
@@ -108,6 +115,7 @@
         this.prefixOrderIndex = prefixOrderIndex;
         this.position = new Point(position);
         this.sourceContainerBounds = new Rect(sourceContainerBounds);
+        this.windowConfiguration = windowConfig;
     }
 
     public RemoteAnimationTarget(Parcel in) {
@@ -119,6 +127,7 @@
         prefixOrderIndex = in.readInt();
         position = in.readParcelable(null);
         sourceContainerBounds = in.readParcelable(null);
+        windowConfiguration = in.readParcelable(null);
     }
 
     @Override
@@ -136,6 +145,7 @@
         dest.writeInt(prefixOrderIndex);
         dest.writeParcelable(position, 0 /* flags */);
         dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
+        dest.writeParcelable(windowConfiguration, 0 /* flags */);
     }
 
     public static final Creator<RemoteAnimationTarget> CREATOR
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 3bb3a4c..1c5e871 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -98,11 +98,13 @@
     int DOCKED_BOTTOM = 4;
 
     /** @hide */
-    final static String INPUT_CONSUMER_PIP = "pip_input_consumer";
+    String INPUT_CONSUMER_PIP = "pip_input_consumer";
     /** @hide */
-    final static String INPUT_CONSUMER_NAVIGATION = "nav_input_consumer";
+    String INPUT_CONSUMER_NAVIGATION = "nav_input_consumer";
     /** @hide */
-    final static String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer";
+    String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer";
+    /** @hide */
+    String INPUT_CONSUMER_RECENTS_ANIMATION = "recents_animation_input_consumer";
 
     /**
      * Not set up for a transition.
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index 5d091c9..b0e9f01 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -17,10 +17,10 @@
 package android.webkit;
 
 import android.annotation.SystemApi;
+import android.content.pm.Signature;
 import android.os.Parcel;
 import android.os.Parcelable;
-
-import java.util.Arrays;
+import android.util.Base64;
 
 /**
  * @hide
@@ -34,7 +34,14 @@
         this.description = description;
         this.availableByDefault = availableByDefault;
         this.isFallback = isFallback;
-        this.signatures = signatures;
+        if (signatures == null) {
+            this.signatures = new Signature[0];
+        } else {
+            this.signatures = new Signature[signatures.length];
+            for (int n = 0; n < signatures.length; n++) {
+                this.signatures[n] = new Signature(Base64.decode(signatures[n], Base64.DEFAULT));
+            }
+        }
     }
 
     // aidl stuff
@@ -54,7 +61,7 @@
         description = in.readString();
         availableByDefault = (in.readInt() > 0);
         isFallback = (in.readInt() > 0);
-        signatures = in.createStringArray();
+        signatures = in.createTypedArray(Signature.CREATOR);
     }
 
     @Override
@@ -68,7 +75,7 @@
         out.writeString(description);
         out.writeInt(availableByDefault ? 1 : 0);
         out.writeInt(isFallback ? 1 : 0);
-        out.writeStringArray(signatures);
+        out.writeTypedArray(signatures, 0);
     }
 
     // fields read from framework resource
@@ -76,5 +83,5 @@
     public final String description;
     public final boolean availableByDefault;
     public final boolean isFallback;
-    public final String[] signatures;
+    public final Signature[] signatures;
 }
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 8650c0a..7f0c086 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -23,6 +23,7 @@
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.MediaPlayerBase;
+import android.media.session.MediaController;
 import android.media.update.ApiLoader;
 import android.media.update.VideoView2Provider;
 import android.media.update.ViewProvider;
@@ -76,8 +77,8 @@
  * If a developer wants to attach a customed MediaControlView2, then set enableControlView attribute
  * to false and assign the customed media control widget using {@link #setMediaControlView2}.
  * <li> VideoView2 is integrated with MediaPlayer2 while VideoView is integrated with MediaPlayer.
- * <li> VideoView2 is integrated with MediaSession2 and so it responses with media key events.
- * A VideoView2 keeps a MediaSession2 instance internally and connects it to a corresponding
+ * <li> VideoView2 is integrated with MediaSession and so it responses with media key events.
+ * A VideoView2 keeps a MediaSession instance internally and connects it to a corresponding
  * MediaControlView2 instance.
  * </p>
  * </ul>
@@ -159,60 +160,18 @@
         return mProvider.getMediaControlView2_impl();
     }
 
-    /**
-     * Starts playback with the media contents specified by {@link #setVideoURI} and
-     * {@link #setVideoPath}.
-     * If it has been paused, this method will resume playback from the current position.
-     */
-    public void start() {
-        mProvider.start_impl();
-    }
 
     /**
-     * Pauses playback.
+     * Returns MediaController instance which is connected with MediaSession that VideoView2 is
+     * using. This method should be called when VideoView2 is attached to window, or it throws
+     * IllegalStateException, since internal MediaSession instance is not available until
+     * this view is attached to window. Please check {@link android.view.View#isAttachedToWindow}
+     * before calling this method.
+     *
+     * @throws IllegalStateException if interal MediaSession is not created yet.
      */
-    public void pause() {
-        mProvider.pause_impl();
-    }
-
-    /**
-     * Gets the duration of the media content specified by #setVideoURI and #setVideoPath
-     * in milliseconds.
-     */
-    public int getDuration() {
-        return mProvider.getDuration_impl();
-    }
-
-    /**
-     * Gets current playback position in milliseconds.
-     */
-    public int getCurrentPosition() {
-        return mProvider.getCurrentPosition_impl();
-    }
-
-    // TODO: mention about key-frame related behavior.
-    /**
-     * Moves the media by specified time position.
-     * @param msec the offset in milliseconds from the start to seek to.
-     */
-    public void seekTo(int msec) {
-        mProvider.seekTo_impl(msec);
-    }
-
-    /**
-     * Says if the media is currently playing.
-     * @return true if the media is playing, false if it is not (eg. paused or stopped).
-     */
-    public boolean isPlaying() {
-        return mProvider.isPlaying_impl();
-    }
-
-    // TODO: check what will return if it is a local media.
-    /**
-     * Gets the percentage (0-100) of the content that has been buffered or played so far.
-     */
-    public int getBufferPercentage() {
-        return mProvider.getBufferPercentage_impl();
+    public MediaController getMediaController() {
+        return mProvider.getMediaController_impl();
     }
 
     /**
@@ -244,7 +203,6 @@
         mProvider.setFullScreen_impl(fullScreen);
     }
 
-    // TODO: This should be revised after integration with MediaPlayer2.
     /**
      * Sets playback speed.
      *
@@ -254,21 +212,12 @@
      * be reset to the normal speed 1.0f.
      * @param speed the playback speed. It should be positive.
      */
+    // TODO: Support this via MediaController2.
     public void setSpeed(float speed) {
         mProvider.setSpeed_impl(speed);
     }
 
     /**
-     * Returns current speed setting.
-     *
-     * If setSpeed() has never been called, returns the default value 1.0f.
-     * @return current speed setting
-     */
-    public float getSpeed() {
-        return mProvider.getSpeed_impl();
-    }
-
-    /**
      * Sets which type of audio focus will be requested during the playback, or configures playback
      * to not request audio focus. Valid values for focus requests are
      * {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT},
@@ -367,14 +316,6 @@
     }
 
     /**
-     * Stops playback and release all the resources. This should be called whenever a VideoView2
-     * instance is no longer to be used.
-     */
-    public void stopPlayback() {
-        mProvider.stopPlayback_impl();
-    }
-
-    /**
      * Registers a callback to be invoked when the media file is loaded and ready to go.
      *
      * @param l the callback that will be run.
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index c5af897..111934f 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -149,6 +149,9 @@
     final ArrayMap<String, ArraySet<String>> mVendorPrivAppPermissions = new ArrayMap<>();
     final ArrayMap<String, ArraySet<String>> mVendorPrivAppDenyPermissions = new ArrayMap<>();
 
+    final ArrayMap<String, ArraySet<String>> mProductPrivAppPermissions = new ArrayMap<>();
+    final ArrayMap<String, ArraySet<String>> mProductPrivAppDenyPermissions = new ArrayMap<>();
+
     final ArrayMap<String, ArrayMap<String, Boolean>> mOemPermissions = new ArrayMap<>();
 
     public static SystemConfig getInstance() {
@@ -240,6 +243,14 @@
         return mVendorPrivAppDenyPermissions.get(packageName);
     }
 
+    public ArraySet<String> getProductPrivAppPermissions(String packageName) {
+        return mProductPrivAppPermissions.get(packageName);
+    }
+
+    public ArraySet<String> getProductPrivAppDenyPermissions(String packageName) {
+        return mProductPrivAppDenyPermissions.get(packageName);
+    }
+
     public Map<String, Boolean> getOemPermissions(String packageName) {
         final Map<String, Boolean> oemPermissions = mOemPermissions.get(packageName);
         if (oemPermissions != null) {
@@ -278,6 +289,14 @@
                 Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);
         readPermissions(Environment.buildPath(
                 Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag);
+
+        // Allow Product to customize system configs around libs, features, permissions and apps
+        int productPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS |
+                ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS;
+        readPermissions(Environment.buildPath(
+                Environment.getProductDirectory(), "etc", "sysconfig"), productPermissionFlag);
+        readPermissions(Environment.buildPath(
+                Environment.getProductDirectory(), "etc", "permissions"), productPermissionFlag);
     }
 
     void readPermissions(File libraryDir, int permissionFlag) {
@@ -598,15 +617,20 @@
                     }
                     XmlUtils.skipCurrentTag(parser);
                 } else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {
-                    // privapp permissions from system and vendor partitions are stored
+                    // privapp permissions from system, vendor and product partitions are stored
                     // separately. This is to prevent xml files in the vendor partition from
                     // granting permissions to priv apps in the system partition and vice
                     // versa.
                     boolean vendor = permFile.toPath().startsWith(
                             Environment.getVendorDirectory().toPath());
+                    boolean product = permFile.toPath().startsWith(
+                            Environment.getProductDirectory().toPath());
                     if (vendor) {
                         readPrivAppPermissions(parser, mVendorPrivAppPermissions,
                                 mVendorPrivAppDenyPermissions);
+                    } else if (product) {
+                        readPrivAppPermissions(parser, mProductPrivAppPermissions,
+                                mProductPrivAppDenyPermissions);
                     } else {
                         readPrivAppPermissions(parser, mPrivAppPermissions,
                                 mPrivAppDenyPermissions);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 102ff95..5751fc9 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -163,6 +163,7 @@
         "android_media_AudioTrack.cpp",
         "android_media_DeviceCallback.cpp",
         "android_media_JetPlayer.cpp",
+        "android_media_MediaMetricsJNI.cpp",
         "android_media_RemoteDisplay.cpp",
         "android_media_ToneGenerator.cpp",
         "android_hardware_Camera.cpp",
@@ -261,6 +262,7 @@
         "libselinux",
         "libicuuc",
         "libmedia",
+        "libmediametrics",
         "libaudioclient",
         "libjpeg",
         "libusbhost",
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index e4da3c6..ebd16c7 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -31,6 +31,7 @@
 #include "android_media_AudioFormat.h"
 #include "android_media_AudioErrors.h"
 #include "android_media_DeviceCallback.h"
+#include "android_media_MediaMetricsJNI.h"
 
 // ----------------------------------------------------------------------------
 
@@ -751,6 +752,39 @@
 }
 
 // ----------------------------------------------------------------------------
+static jobject
+android_media_AudioRecord_native_getMetrics(JNIEnv *env, jobject thiz)
+{
+    ALOGV("android_media_AudioRecord_native_getMetrics");
+
+    sp<AudioRecord> lpRecord = getAudioRecord(env, thiz);
+
+    if (lpRecord == NULL) {
+        ALOGE("Unable to retrieve AudioRecord pointer for getMetrics()");
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return (jobject) NULL;
+    }
+
+    // get what we have for the metrics from the record session
+    MediaAnalyticsItem *item = NULL;
+
+    status_t err = lpRecord->getMetrics(item);
+    if (err != OK) {
+        ALOGE("getMetrics failed");
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return (jobject) NULL;
+    }
+
+    jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL /* mybundle */);
+
+    // housekeeping
+    delete item;
+    item = NULL;
+
+    return mybundle;
+}
+
+// ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 static const JNINativeMethod gMethods[] = {
     // name,               signature,  funcPtr
@@ -781,6 +815,8 @@
                              "()I",    (void *)android_media_AudioRecord_get_pos_update_period},
     {"native_get_min_buff_size",
                              "(III)I",   (void *)android_media_AudioRecord_get_min_buff_size},
+    {"native_getMetrics",    "()Landroid/os/PersistableBundle;",
+                                         (void *)android_media_AudioRecord_native_getMetrics},
     {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
     {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
     {"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback},
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 11011b1..afbc579 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -34,6 +34,7 @@
 
 #include "android_media_AudioFormat.h"
 #include "android_media_AudioErrors.h"
+#include "android_media_MediaMetricsJNI.h"
 #include "android_media_PlaybackParams.h"
 #include "android_media_DeviceCallback.h"
 #include "android_media_VolumeShaper.h"
@@ -1011,6 +1012,39 @@
     return (jint) nativeToJavaStatus(status);
 }
 
+// ----------------------------------------------------------------------------
+static jobject
+android_media_AudioTrack_native_getMetrics(JNIEnv *env, jobject thiz)
+{
+    ALOGD("android_media_AudioTrack_native_getMetrics");
+
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+
+    if (lpTrack == NULL) {
+        ALOGE("Unable to retrieve AudioTrack pointer for getMetrics()");
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return (jobject) NULL;
+    }
+
+    // get what we have for the metrics from the track
+    MediaAnalyticsItem *item = NULL;
+
+    status_t err = lpTrack->getMetrics(item);
+    if (err != OK) {
+        ALOGE("getMetrics failed");
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return (jobject) NULL;
+    }
+
+    jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL /* mybundle */);
+
+    // housekeeping
+    delete item;
+    item = NULL;
+
+    return mybundle;
+}
+
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_set_loop(JNIEnv *env,  jobject thiz,
@@ -1275,6 +1309,8 @@
     {"native_get_underrun_count", "()I",      (void *)android_media_AudioTrack_get_underrun_count},
     {"native_get_flags",     "()I",      (void *)android_media_AudioTrack_get_flags},
     {"native_get_timestamp", "([J)I",    (void *)android_media_AudioTrack_get_timestamp},
+    {"native_getMetrics",    "()Landroid/os/PersistableBundle;",
+                                         (void *)android_media_AudioTrack_native_getMetrics},
     {"native_set_loop",      "(III)I",   (void *)android_media_AudioTrack_set_loop},
     {"native_reload_static", "()I",      (void *)android_media_AudioTrack_reload},
     {"native_get_output_sample_rate",
diff --git a/media/jni/android_media_MediaMetricsJNI.cpp b/core/jni/android_media_MediaMetricsJNI.cpp
similarity index 100%
rename from media/jni/android_media_MediaMetricsJNI.cpp
rename to core/jni/android_media_MediaMetricsJNI.cpp
diff --git a/media/jni/android_media_MediaMetricsJNI.h b/core/jni/android_media_MediaMetricsJNI.h
similarity index 100%
rename from media/jni/android_media_MediaMetricsJNI.h
rename to core/jni/android_media_MediaMetricsJNI.h
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 403937b..7e17a49 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -174,6 +174,10 @@
           argv[argc++] = AssetManager::OVERLAY_DIR;
       }
 
+      if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
+          argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR;
+      }
+
       // Finally, invoke idmap (if any overlay directory exists)
       if (argc > 5) {
         execv(AssetManager::IDMAP_BIN, (char* const*)argv);
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 956b724..3b7b14c 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -85,11 +85,15 @@
   static const char* kOverlayDir = "/system/vendor/overlay/";
   static const char* kVendorOverlayDir = "/vendor/overlay";
   static const char* kOverlaySubdir = "/system/vendor/overlay-subdir/";
+  static const char* kSystemProductOverlayDir = "/system/product/overlay/";
+  static const char* kProductOverlayDir = "/product/overlay";
   static const char* kApkSuffix = ".apk";
 
   if ((android::base::StartsWith(path, kOverlayDir)
        || android::base::StartsWith(path, kOverlaySubdir)
-       || android::base::StartsWith(path, kVendorOverlayDir))
+       || android::base::StartsWith(path, kVendorOverlayDir)
+       || android::base::StartsWith(path, kSystemProductOverlayDir)
+       || android::base::StartsWith(path, kProductOverlayDir))
       && android::base::EndsWith(path, kApkSuffix)
       && path.find("/../") == std::string::npos) {
     return true;
diff --git a/core/proto/android/app/activitymanager.proto b/core/proto/android/app/activitymanager.proto
index 03f8204..4756c13 100644
--- a/core/proto/android/app/activitymanager.proto
+++ b/core/proto/android/app/activitymanager.proto
@@ -21,65 +21,6 @@
 option java_multiple_files = true;
 option java_outer_classname = "ActivityManagerProto";
 
-// ActivityManager.java PROCESS_STATEs
-enum ProcessState {
-  // Order matters for process states, so values have been spaced to provide
-  // room for future additions.
-
-  // Not a real process state.
-  PROCESS_STATE_UNKNOWN = -100;
-  // Process is a persistent system process.
-  PROCESS_STATE_PERSISTENT = 0;
-  // Process is a persistent system process and is doing UI.
-  PROCESS_STATE_PERSISTENT_UI = 100;
-  // Process is hosting the current top activities. Note that this covers
-  // all activities that are visible to the user.
-  PROCESS_STATE_TOP = 200;
-  // Process is hosting a foreground service due to a system binding.
-  PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 300;
-  // Process is hosting a foreground service.
-  PROCESS_STATE_FOREGROUND_SERVICE = 400;
-  // Process is important to the user, and something they are aware of.
-  PROCESS_STATE_IMPORTANT_FOREGROUND = 500;
-  // Process is important to the user, but not something they are aware of.
-  PROCESS_STATE_IMPORTANT_BACKGROUND = 600;
-  // Process is in the background transient so we will try to keep running.
-  PROCESS_STATE_TRANSIENT_BACKGROUND = 700;
-  // Process is in the background running a backup/restore operation.
-  PROCESS_STATE_BACKUP = 800;
-  // Process is in the background running a service. Unlike oom_adj, this
-  // level is used for both the normal running in background state and the
-  // executing operations state.
-  PROCESS_STATE_SERVICE = 900;
-  // Process is in the background running a receiver. Note that from the
-  // perspective of oom_adj, receivers run at a higher foreground level, but
-  // for our prioritization here that is not necessary and putting them
-  // below services means many fewer changes in some process states as they
-  // receive broadcasts.
-  PROCESS_STATE_RECEIVER = 1000;
-  // Same as PROCESS_STATE_TOP but while device is sleeping.
-  PROCESS_STATE_TOP_SLEEPING = 1100;
-  // Process is in the background, but it can't restore its state so we want
-  // to try to avoid killing it.
-  PROCESS_STATE_HEAVY_WEIGHT = 1200;
-  // Process is in the background but hosts the home activity.
-  PROCESS_STATE_HOME = 1300;
-  // Process is in the background but hosts the last shown activity.
-  PROCESS_STATE_LAST_ACTIVITY = 1400;
-  // Process is being cached for later use and contains activities.
-  PROCESS_STATE_CACHED_ACTIVITY = 1500;
-  // Process is being cached for later use and is a client of another cached
-  // process that contains activities.
-  PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 1600;
-  // Process is being cached for later use and has an activity that corresponds
-  // to an existing recent task.
-  PROCESS_STATE_CACHED_RECENT = 1700;
-  // Process is being cached for later use and is empty.
-  PROCESS_STATE_CACHED_EMPTY = 1800;
-  // Process does not exist.
-  PROCESS_STATE_NONEXISTENT = 1900;
-}
-
 // ActivityManager.java UID_OBSERVERs flags
 enum UidObserverFlag {
   // report changes in process state, original value is 1 << 0
diff --git a/core/proto/android/app/enums.proto b/core/proto/android/app/enums.proto
new file mode 100644
index 0000000..2de2574
--- /dev/null
+++ b/core/proto/android/app/enums.proto
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.app;
+
+option java_outer_classname = "AppProtoEnums";
+option java_multiple_files = true;
+
+// ActivityManager.java PROCESS_STATEs
+enum ProcessStateEnum {
+    // Unlike the ActivityManager PROCESS_STATE values, the ordering and numerical values
+    // here are completely fixed and arbitrary. Order is irrelevant.
+    // No attempt need be made to keep them in sync.
+    // The values here must not be modified. Any new process states can be appended to the end.
+
+    // Process state that is unknown to this proto file (i.e. is not mapped
+    // by ActivityManager.processStateAmToProto()). Can only happen if there's a bug in the mapping.
+    PROCESS_STATE_UNKNOWN_TO_PROTO = 998;
+    // Not a real process state.
+    PROCESS_STATE_UNKNOWN = 999;
+    // Process is a persistent system process.
+    PROCESS_STATE_PERSISTENT = 1000;
+    // Process is a persistent system process and is doing UI.
+    PROCESS_STATE_PERSISTENT_UI = 1001;
+    // Process is hosting the current top activities. Note that this covers
+    // all activities that are visible to the user.
+    PROCESS_STATE_TOP = 1002;
+    // Process is hosting a foreground service.
+    PROCESS_STATE_FOREGROUND_SERVICE = 1003;
+    // Process is hosting a foreground service due to a system binding.
+    PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 1004;
+    // Process is important to the user, and something they are aware of.
+    PROCESS_STATE_IMPORTANT_FOREGROUND = 1005;
+    // Process is important to the user, but not something they are aware of.
+    PROCESS_STATE_IMPORTANT_BACKGROUND = 1006;
+    // Process is in the background transient so we will try to keep running.
+    PROCESS_STATE_TRANSIENT_BACKGROUND = 1007;
+    // Process is in the background running a backup/restore operation.
+    PROCESS_STATE_BACKUP = 1008;
+    // Process is in the background running a service. Unlike oom_adj, this
+    // level is used for both the normal running in background state and the
+    // executing operations state.
+    PROCESS_STATE_SERVICE = 1009;
+    // Process is in the background running a receiver. Note that from the
+    // perspective of oom_adj, receivers run at a higher foreground level, but
+    // for our prioritization here that is not necessary and putting them
+    // below services means many fewer changes in some process states as they
+    // receive broadcasts.
+    PROCESS_STATE_RECEIVER = 1010;
+    // Same as PROCESS_STATE_TOP but while device is sleeping.
+    PROCESS_STATE_TOP_SLEEPING = 1011;
+    // Process is in the background, but it can't restore its state so we want
+    // to try to avoid killing it.
+    PROCESS_STATE_HEAVY_WEIGHT = 1012;
+    // Process is in the background but hosts the home activity.
+    PROCESS_STATE_HOME = 1013;
+    // Process is in the background but hosts the last shown activity.
+    PROCESS_STATE_LAST_ACTIVITY = 1014;
+    // Process is being cached for later use and contains activities.
+    PROCESS_STATE_CACHED_ACTIVITY = 1015;
+    // Process is being cached for later use and is a client of another cached
+    // process that contains activities.
+    PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 1016;
+    // Process is being cached for later use and has an activity that corresponds
+    // to an existing recent task.
+    PROCESS_STATE_CACHED_RECENT = 1017;
+    // Process is being cached for later use and is empty.
+    PROCESS_STATE_CACHED_EMPTY = 1018;
+    // Process does not exist.
+    PROCESS_STATE_NONEXISTENT = 1019;
+}
+
diff --git a/core/proto/android/content/clipdata.proto b/core/proto/android/content/clipdata.proto
index 6967b69..aeeef97 100644
--- a/core/proto/android/content/clipdata.proto
+++ b/core/proto/android/content/clipdata.proto
@@ -21,13 +21,18 @@
 
 import "frameworks/base/core/proto/android/content/clipdescription.proto";
 import "frameworks/base/core/proto/android/content/intent.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 // An android.content.ClipData object.
 message ClipDataProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional android.content.ClipDescriptionProto description = 1;
 
     // Custom dump of an android.graphics.Bitmap object.
     message Icon {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional int32 width = 1;
         optional int32 height = 2;
     }
@@ -35,6 +40,8 @@
 
     // An android.content.ClipData.Item object.
     message Item {
+        option (.android.msg_privacy).dest = DEST_EXPLICIT;
+
         oneof data {
             string html_text = 1;
             string text = 2;
diff --git a/core/proto/android/content/clipdescription.proto b/core/proto/android/content/clipdescription.proto
index 40f4ad3..bc0e940 100644
--- a/core/proto/android/content/clipdescription.proto
+++ b/core/proto/android/content/clipdescription.proto
@@ -20,11 +20,14 @@
 option java_multiple_files = true;
 
 import "frameworks/base/core/proto/android/os/persistablebundle.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 // An android.content.ClipDescription object.
 message ClipDescriptionProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     repeated string mime_types = 1;
-    optional string label = 2;
+    optional string label = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
     optional android.os.PersistableBundleProto extras = 3;
     optional int64 timestamp_ms = 4;
 }
diff --git a/core/proto/android/content/component_name.proto b/core/proto/android/content/component_name.proto
index fc0c8c5..4e49cf2 100644
--- a/core/proto/android/content/component_name.proto
+++ b/core/proto/android/content/component_name.proto
@@ -20,10 +20,14 @@
 
 package android.content;
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
 /**
  * An android.content.ComponentName object.
  */
 message ComponentNameProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional string package_name = 1;
     optional string class_name = 2;
 }
diff --git a/core/proto/android/content/intent.proto b/core/proto/android/content/intent.proto
index c25b46d..1737b62 100644
--- a/core/proto/android/content/intent.proto
+++ b/core/proto/android/content/intent.proto
@@ -51,14 +51,14 @@
 
     optional string action = 1;
     repeated string categories = 2;
-    optional string data = 3  [ (.android.privacy).dest = DEST_EXPLICIT ];
+    optional string data = 3 [ (.android.privacy).dest = DEST_EXPLICIT ];
     optional string type = 4;
     optional string flag = 5;
     optional string package = 6;
     optional string component = 7;
     optional string source_bounds = 8;
-    optional string clip_data = 9  [ (.android.privacy).dest = DEST_EXPLICIT ];
-    optional string extras = 10;
+    optional string clip_data = 9 [ (.android.privacy).dest = DEST_EXPLICIT ];
+    optional string extras = 10 [ (.android.privacy).dest = DEST_EXPLICIT ];
     optional int32 content_user_hint = 11;
     optional string selector = 12;
 }
diff --git a/core/proto/android/net/network.proto b/core/proto/android/net/network.proto
index 9c7ea5d..e13ca9f 100644
--- a/core/proto/android/net/network.proto
+++ b/core/proto/android/net/network.proto
@@ -19,9 +19,14 @@
 
 package android.net;
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
 /**
  * An android.net.Network object.
  */
 message NetworkProto {
-    optional int32 net_id = 1;
+    // The netId is an implementation detail which might be changed in the
+    // future, or which alone (i.e. in the absence of some additional context)
+    // might not be sufficient to fully identify a Network.
+    optional int32 net_id = 1 [ (.android.privacy).dest = DEST_AUTOMATIC ];
 }
diff --git a/core/proto/android/net/networkcapabilities.proto b/core/proto/android/net/networkcapabilities.proto
index e1c2af1..0338bf8 100644
--- a/core/proto/android/net/networkcapabilities.proto
+++ b/core/proto/android/net/networkcapabilities.proto
@@ -20,10 +20,14 @@
 
 option java_multiple_files = true;
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
 /**
  * An android.net.NetworkCapabilities object.
  */
 message NetworkCapabilitiesProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     enum Transport {
         // Indicates this network uses a Cellular transport.
         TRANSPORT_CELLULAR = 0;
@@ -118,7 +122,7 @@
     optional int32 link_up_bandwidth_kbps = 3;
     optional int32 link_down_bandwidth_kbps = 4;
 
-    optional string network_specifier = 5;
+    optional string network_specifier = 5 [ (.android.privacy).dest = DEST_EXPLICIT ];
 
     // True if this object specifies a signal strength.
     optional bool can_report_signal_strength = 6;
diff --git a/core/proto/android/net/networkrequest.proto b/core/proto/android/net/networkrequest.proto
index 9884464..d260b13 100644
--- a/core/proto/android/net/networkrequest.proto
+++ b/core/proto/android/net/networkrequest.proto
@@ -21,11 +21,14 @@
 option java_multiple_files = true;
 
 import "frameworks/base/core/proto/android/net/networkcapabilities.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 /**
  * An android.net.NetworkRequest object.
  */
 message NetworkRequestProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     enum Type {
         TYPE_UNKNOWN = 0;
         // Only used by applications. When an application creates a
diff --git a/core/proto/android/os/bundle.proto b/core/proto/android/os/bundle.proto
index 6990281..5556936 100644
--- a/core/proto/android/os/bundle.proto
+++ b/core/proto/android/os/bundle.proto
@@ -19,10 +19,14 @@
 
 option java_multiple_files = true;
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
 // An android.os.Bundle object.
 message BundleProto {
     oneof data {
-        int32 parcelled_data_size = 1;
-        string map_data = 2;
+        int32 parcelled_data_size = 1 [
+            (.android.privacy).dest = DEST_AUTOMATIC
+        ];
+        string map_data = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
     }
 }
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 828a55f..3ec6f05 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -123,7 +123,7 @@
 
     // Linux services
     optional Procrank procrank = 2000 [
-        (section).type = SECTION_COMMAND,
+        (section).type = SECTION_NONE, // disable procrank until figure out permission
         (section).args = "/system/xbin/procrank"
     ];
 
diff --git a/core/proto/android/os/persistablebundle.proto b/core/proto/android/os/persistablebundle.proto
index 75ff787..712f87c 100644
--- a/core/proto/android/os/persistablebundle.proto
+++ b/core/proto/android/os/persistablebundle.proto
@@ -19,10 +19,14 @@
 
 option java_multiple_files = true;
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
 // An android.os.PersistableBundle object.
 message PersistableBundleProto {
     oneof data {
-        int32 parcelled_data_size = 1;
-        string map_data = 2;
+        int32 parcelled_data_size = 1 [
+            (.android.privacy).dest = DEST_AUTOMATIC
+        ];
+        string map_data = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
     }
 }
diff --git a/core/proto/android/os/powermanager.proto b/core/proto/android/os/powermanager.proto
index 8e0a607..fecade4 100644
--- a/core/proto/android/os/powermanager.proto
+++ b/core/proto/android/os/powermanager.proto
@@ -20,6 +20,7 @@
 option java_multiple_files = true;
 
 import "frameworks/base/core/proto/android/os/worksource.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 message PowerManagerProto {
     /* User activity events in PowerManager.java. */
@@ -90,6 +91,8 @@
 
     // WakeLock class in android.os.PowerManager, it is the one used by sdk
     message WakeLockProto {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional string hex_string = 1;
         optional bool held = 2;
         optional int32 internal_count = 3;
diff --git a/core/proto/android/os/worksource.proto b/core/proto/android/os/worksource.proto
index 2f8b2fb..0a9c2ed 100644
--- a/core/proto/android/os/worksource.proto
+++ b/core/proto/android/os/worksource.proto
@@ -17,15 +17,23 @@
 syntax = "proto2";
 package android.os;
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
 option java_multiple_files = true;
 
 message WorkSourceProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     message WorkSourceContentProto {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional int32 uid = 1;
         optional string name = 2;
     }
 
     message WorkChain {
+      option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
       repeated WorkSourceContentProto nodes = 1;
     }
 
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 39c5ec7..788d901 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -19,6 +19,7 @@
 package com.android.server.am.proto;
 
 import "frameworks/base/core/proto/android/app/activitymanager.proto";
+import "frameworks/base/core/proto/android/app/enums.proto";
 import "frameworks/base/core/proto/android/app/notification.proto";
 import "frameworks/base/core/proto/android/app/profilerinfo.proto";
 import "frameworks/base/core/proto/android/content/component_name.proto";
@@ -129,6 +130,8 @@
   repeated StickyBroadcastProto sticky_broadcasts = 4;
 
   message MainHandler {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional string handler = 1;
     optional .android.os.LooperProto looper = 2;
   }
@@ -662,7 +665,7 @@
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
     optional .android.os.PowerManagerInternalProto.Wakefulness wakefulness = 1;
-    repeated string sleep_tokens = 2;
+    repeated string sleep_tokens = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
     optional bool sleeping = 3;
     optional bool shutting_down = 4;
     optional bool test_pss_mode = 5;
@@ -670,7 +673,7 @@
   optional SleepStatus sleep_status = 27;
 
   message VoiceProto {
-    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+    option (.android.msg_privacy).dest = DEST_EXPLICIT;
 
     optional string session = 1;
     optional .android.os.PowerManagerProto.WakeLockProto wakelock = 2;
@@ -724,7 +727,7 @@
       option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
       optional string proc_name = 1;
-      optional string file = 2  [ (.android.privacy).dest = DEST_EXPLICIT ];
+      optional string file = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
       optional int32 pid = 3;
       optional int32 uid = 4;
     }
@@ -797,7 +800,7 @@
 
   optional string hex_hash = 1;
   optional int32 uid = 2;
-  optional .android.app.ProcessState current = 3;
+  optional .android.app.ProcessStateEnum current = 3;
   optional bool ephemeral = 4;
   optional bool fg_services = 5;
   optional bool whilelist = 6;
@@ -855,7 +858,7 @@
     bool services = 6;
   }
 
-  optional .android.app.ProcessState state = 7;
+  optional .android.app.ProcessStateEnum state = 7;
   optional int32 trim_memory_level = 8;
   optional ProcessRecordProto proc = 9;
   optional string adj_type = 10;
@@ -878,8 +881,8 @@
     optional int32 set_raw_adj = 3;
     optional int32 cur_adj = 4;
     optional int32 set_adj = 5;
-    optional .android.app.ProcessState current_state = 7;
-    optional .android.app.ProcessState set_state = 8;
+    optional .android.app.ProcessStateEnum current_state = 7;
+    optional .android.app.ProcessStateEnum set_state = 8;
     optional string last_pss = 9;
     optional string last_swap_pss = 10;
     optional string last_cached_pss = 11;
@@ -997,6 +1000,8 @@
   optional int64 total_duration_ms = 2;
 
   message PackageTime {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional string package = 1;
     optional int64 duration_ms = 2;
   }
diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto
index c9f7d52..5657f96 100644
--- a/core/proto/android/server/forceappstandbytracker.proto
+++ b/core/proto/android/server/forceappstandbytracker.proto
@@ -20,8 +20,12 @@
 
 option java_multiple_files = true;
 
-// Dump from ForceAppStandbyTracker.
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+// Dump from com.android.server.ForceAppStandbyTracker.
 message ForceAppStandbyTrackerProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
   // Whether all apps are forced standby or not.
   optional bool force_all_apps_standby = 1;
 
@@ -35,10 +39,11 @@
   repeated int32 temp_power_save_whitelist_app_ids = 4;
 
   message RunAnyInBackgroundRestrictedPackages {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional int32 uid = 1;
     optional string package_name = 2;
   }
-
   // Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND.
   repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5;
 
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 739fca3..304e63f 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -29,13 +29,18 @@
 import "frameworks/base/core/proto/android/os/bundle.proto";
 import "frameworks/base/core/proto/android/os/persistablebundle.proto";
 import "frameworks/base/core/proto/android/server/forceappstandbytracker.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 message JobSchedulerServiceDumpProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional ConstantsProto settings = 1;
 
     repeated int32 started_users = 2;
 
     message RegisteredJob {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional JobStatusShortInfoProto info = 1;
         optional JobStatusDumpProto dump = 2;
 
@@ -56,6 +61,8 @@
 
     // Which uids are currently in the foreground.
     message PriorityOverride {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional int32 uid = 1;
         // Use sint32 instead of an enum since priorities can technically be
         // negative.
@@ -71,6 +78,8 @@
     optional JobPackageTrackerDumpProto package_tracker = 8;
 
     message PendingJob {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional JobStatusShortInfoProto info = 1;
         optional JobStatusDumpProto dump = 2;
         optional sint32 evaluated_priority = 3;
@@ -81,12 +90,18 @@
 
     // From a service that has currently active or pending jobs.
     message ActiveJob {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         message InactiveJob {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional int64 time_since_stopped_ms = 1;
             // This is not always provided.
             optional string stopped_reason = 2;
         }
         message RunningJob {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional JobStatusShortInfoProto info = 1;
             // How long this job has been running for.
             optional int64 running_duration_ms = 2;
@@ -119,6 +134,8 @@
 
 // A com.android.server.job.JobSchedulerService.Constants object.
 message ConstantsProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Minimum # of idle jobs that must be ready in order to force the JMS to
     // schedule things early.
     optional int32 min_idle_count = 1;
@@ -187,10 +204,16 @@
 }
 
 message StateControllerProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     message AppIdleController {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional bool is_parole_on = 1;
 
         message TrackedJob {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional JobStatusShortInfoProto info = 1;
             optional int32 source_uid = 2;
             optional string source_package_name = 3;
@@ -201,9 +224,13 @@
         repeated TrackedJob tracked_jobs = 2;
     }
     message BackgroundJobsController {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional com.android.server.ForceAppStandbyTrackerProto force_app_standby_tracker = 1;
 
         message TrackedJob {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional JobStatusShortInfoProto info = 1;
             optional int32 source_uid = 2;
             optional string source_package_name = 3;
@@ -217,6 +244,8 @@
         repeated TrackedJob tracked_jobs = 2;
     }
     message BatteryController {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional bool is_on_stable_power = 1;
         optional bool is_battery_not_low = 2;
 
@@ -226,15 +255,21 @@
         optional int32 last_broadcast_sequence_number = 4;
 
         message TrackedJob {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional JobStatusShortInfoProto info = 1;
             optional int32 source_uid = 2;
         }
         repeated TrackedJob tracked_jobs = 5;
     }
     message ConnectivityController {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional bool is_connected = 1;
 
         message TrackedJob {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional JobStatusShortInfoProto info = 1;
             optional int32 source_uid = 2;
             optional .android.net.NetworkRequestProto required_network = 3;
@@ -242,31 +277,47 @@
         repeated TrackedJob tracked_jobs = 2;
     }
     message ContentObserverController {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         message TrackedJob {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional JobStatusShortInfoProto info = 1;
             optional int32 source_uid = 2;
         }
         repeated TrackedJob tracked_jobs = 1;
 
         message Observer {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional int32 user_id = 1;
 
             message TriggerContentData {
-                optional string uri = 1;
+                option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+                optional string uri = 1 [
+                    (.android.privacy).dest = DEST_EXPLICIT
+                ];
                 optional int32 flags = 2;
 
                 // A
                 // com.android.server.job.controllers.ContentObserverController.JobInstance
                 // object.
                 message JobInstance {
+                    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
                     optional JobStatusShortInfoProto info = 1;
                     optional int32 source_uid = 2;
 
                     optional int64 trigger_content_update_delay_ms = 3;
                     optional int64 trigger_content_max_delay_ms = 4;
 
-                    repeated string changed_authorities = 5;
-                    repeated string changed_uris = 6;
+                    repeated string changed_authorities = 5 [
+                        (.android.privacy).dest = DEST_EXPLICIT
+                    ];
+                    repeated string changed_uris = 6 [
+                        (.android.privacy).dest = DEST_EXPLICIT
+                    ];
                 }
                 repeated JobInstance jobs = 3;
             }
@@ -275,10 +326,14 @@
         repeated Observer observers = 2;
     }
     message DeviceIdleJobsController {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         // True when in device idle mode.
         optional bool is_device_idle_mode = 1;
 
         message TrackedJob {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional JobStatusShortInfoProto info = 1;
             optional int32 source_uid = 2;
             optional string source_package_name = 3;
@@ -293,30 +348,42 @@
         repeated TrackedJob tracked_jobs = 2;
     }
     message IdleController {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional bool is_idle = 1;
 
         message TrackedJob {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional JobStatusShortInfoProto info = 1;
             optional int32 source_uid = 2;
         }
         repeated TrackedJob tracked_jobs = 2;
     }
     message StorageController {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional bool is_storage_not_low = 1;
         optional int32 last_broadcast_sequence_number = 2;
 
         message TrackedJob {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional JobStatusShortInfoProto info = 1;
             optional int32 source_uid = 2;
         }
         repeated TrackedJob tracked_jobs = 3;
     }
     message TimeController {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional int64 now_elapsed_realtime = 1;
         optional int64 time_until_next_delay_alarm_ms = 2;
         optional int64 time_until_next_deadline_alarm_ms = 3;
 
         message TrackedJob {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional JobStatusShortInfoProto info = 1;
             optional int32 source_uid = 2;
 
@@ -347,6 +414,8 @@
 
 // A com.android.server.job.JobPackageTracker.DataSet object.
 message DataSetProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional int64 start_clock_time_ms = 1;
     // How much time has elapsed since the DataSet was instantiated.
     optional int64 elapsed_time_ms = 2;
@@ -355,10 +424,14 @@
     // Represents a com.android.server.job.JobPackageTracker.PackageEntry
     // object, but with some extra data.
     message PackageEntryProto {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional int32 uid = 1;
         optional string package_name = 2;
 
         message State {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional int64 duration_ms = 1;
             optional int32 count = 2;
         }
@@ -377,6 +450,8 @@
         optional bool active_top = 8;
 
         message StopReasonCount {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             optional .android.app.JobParametersProto.CancelReason reason = 1;
             optional int32 count = 2;
         }
@@ -390,19 +465,27 @@
 
 // Dump from com.android.server.job.GrantedUriPermissions.
 message GrantedUriPermissionsDumpProto {
-    optional int32 flags = 1;
-    optional int32 source_user_id = 2;
+    option (.android.msg_privacy).dest = DEST_EXPLICIT;
+
+    optional int32 flags = 1 [ (.android.privacy).dest = DEST_AUTOMATIC ];
+    optional int32 source_user_id = 2 [
+        (.android.privacy).dest = DEST_AUTOMATIC
+    ];
     optional string tag = 3;
     optional string permission_owner = 4;
     repeated string uris = 5;
 }
 
 message JobPackageTrackerDumpProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     repeated DataSetProto historical_stats = 1;
     optional DataSetProto current_stats = 2;
 }
 
 message JobPackageHistoryProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     enum Event {
         UNKNOWN = 0;
         START_JOB = 1;
@@ -411,12 +494,14 @@
         STOP_PERIODIC_JOB = 4;
     }
     message HistoryEvent {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional Event event = 1;
         optional int64 time_since_event_ms = 2;
         optional int32 uid = 3;
         // Job IDs can technically be negative.
         optional int32 job_id = 4;
-        optional string tag = 5;
+        optional string tag = 5 [ (.android.privacy).dest = DEST_EXPLICIT ];
         // Only valid for STOP_JOB or STOP_PERIODIC_JOB Events.
         optional .android.app.JobParametersProto.CancelReason stop_reason = 6;
     }
@@ -424,17 +509,23 @@
 }
 
 message JobStatusShortInfoProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional int32 calling_uid = 1;
     // Job IDs can technically be negative.
     optional int32 job_id = 2;
-    optional string battery_name = 3;
+    optional string battery_name = 3 [
+        (.android.privacy).dest = DEST_EXPLICIT
+    ];
 }
 
 // Dump from a com.android.server.job.controllers.JobStatus object.
 message JobStatusDumpProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // The UID that scheduled the job.
     optional int32 calling_uid = 1;
-    optional string tag = 2;
+    optional string tag = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
 
     // The UID for which the job is being run.
     optional int32 source_uid = 3;
@@ -444,6 +535,8 @@
 
     // Custom dump of android.app.job.JobInfo object.
     message JobInfo {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional .android.content.ComponentNameProto service = 1;
 
         optional bool is_periodic = 2;
@@ -461,8 +554,10 @@
         optional bool requires_device_idle = 10;
 
         message TriggerContentUri {
-            optional int32 flags = 1;
-            optional string uri = 2;
+            optional int32 flags = 1 [
+                (.android.privacy).dest = DEST_AUTOMATIC
+            ];
+            optional string uri = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
         }
         repeated TriggerContentUri trigger_content_uris = 11;
         optional int64 trigger_content_update_delay_ms = 12;
@@ -482,6 +577,8 @@
         optional int64 max_execution_delay_ms = 21;
 
         message Backoff {
+            option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
             enum Policy {
                 BACKOFF_POLICY_LINEAR = 0;
                 BACKOFF_POLICY_EXPONENTIAL = 1;
@@ -524,13 +621,19 @@
     // Controllers that are currently tracking the job.
     repeated TrackingController tracking_controllers = 11;
 
-    repeated string changed_authorities = 12;
-    repeated string changed_uris = 13;
+    repeated string changed_authorities = 12 [
+        (.android.privacy).dest = DEST_EXPLICIT
+    ];
+    repeated string changed_uris = 13 [
+        (.android.privacy).dest = DEST_EXPLICIT
+    ];
 
     optional .android.net.NetworkProto network = 14;
 
     // Only the desired data from an android.app.job.JobWorkItem object.
     message JobWorkItem {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional int32 work_id = 1;
         optional int32 delivery_count = 2;
         optional .android.content.IntentProto intent = 3;
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index f3ebd41..babbef0 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -19,7 +19,7 @@
 
 option java_multiple_files = true;
 
-import "frameworks/base/core/proto/android/app/activitymanager.proto";
+import "frameworks/base/core/proto/android/app/enums.proto";
 import "frameworks/base/core/proto/android/content/intent.proto";
 import "frameworks/base/core/proto/android/os/batterymanager.proto";
 import "frameworks/base/core/proto/android/os/looper.proto";
@@ -67,8 +67,7 @@
         optional string uid_string = 2;
         optional bool is_active = 3;
         optional int32 num_wake_locks = 4;
-        optional bool is_process_state_unknown = 5;
-        optional .android.app.ProcessState process_state = 6;
+        optional .android.app.ProcessStateEnum process_state = 5;
     }
 
     optional ConstantsProto constants = 1;
diff --git a/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml b/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml
index 3254ebb..6f3dc8c 100644
--- a/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml
+++ b/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml
@@ -20,19 +20,54 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android"
      android:shareInterpolator="false" android:zAdjustment="top">
 
-    <alpha android:fromAlpha="0" android:toAlpha="1.0"
-           android:startOffset="300"
-           android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-           android:interpolator="@interpolator/decelerate_quart"
-           android:duration="167"/>
+    <alpha
+        android:fromAlpha="1"
+        android:toAlpha="1.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/linear"
+        android:startOffset="67"
+        android:duration="217"/>
 
-    <translate android:fromYDelta="110%" android:toYDelta="0"
-               android:startOffset="300"
-               android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-               android:interpolator="@interpolator/decelerate_quint"
-               android:duration="417"/>
+    <translate
+        android:fromXDelta="-105%"
+        android:toXDelta="0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/aggressive_ease"
+        android:startOffset="50"
+        android:duration="383"/>
 
-    <!-- To keep the thumbnail around longer -->
+    <scale
+        android:fromXScale="1.0526"
+        android:toXScale="1"
+        android:fromYScale="1.0526"
+        android:toYScale="1"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="283"/>
+
+    <scale
+        android:fromXScale="0.95"
+        android:toXScale="1"
+        android:fromYScale="0.95"
+        android:toYScale="1"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:startOffset="283"
+        android:duration="317"/>
+
+    <!-- To keep the thumbnail around longer and fade out the thumbnail -->
     <alpha android:fromAlpha="1.0" android:toAlpha="0"
            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
            android:interpolator="@interpolator/decelerate_quint"
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index 2ee7cd8..0e66eda 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -14,7 +14,9 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
---><!-- This should in sync with task_open_enter_cross_profile_apps.xml -->
+-->
+<!-- This should in sync with task_open_enter_cross_profile_apps.xml -->
+<!-- This should in sync with cross_profile_apps_thumbnail_enter.xml -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="false"
     android:zAdjustment="top">
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 0485625..a5698af 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -73,6 +73,7 @@
 const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
 const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
 const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
+const char* AssetManager::PRODUCT_OVERLAY_DIR = "/product/overlay";
 const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme";
 const char* AssetManager::TARGET_PACKAGE_NAME = "android";
 const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index ecc5dc1..08da731 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -60,6 +60,7 @@
     static const char* RESOURCES_FILENAME;
     static const char* IDMAP_BIN;
     static const char* OVERLAY_DIR;
+    static const char* PRODUCT_OVERLAY_DIR;
     /*
      * If OVERLAY_THEME_DIR_PROPERTY is set, search for runtime resource overlay
      * APKs in OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 27784e9..eb6e830 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -32,6 +32,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.ArrayMap;
@@ -1314,6 +1315,23 @@
         return native_read_in_direct_buffer(audioBuffer, sizeInBytes, readMode == READ_BLOCKING);
     }
 
+    /**
+     *  Return Metrics data about the current AudioTrack instance.
+     *
+     * @return a {@link PersistableBundle} containing the set of attributes and values
+     * available for the media being handled by this instance of AudioRecord
+     * The attributes are descibed in {@link MetricsConstants}.
+     *
+     * Additional vendor-specific fields may also be present in
+     * the return value.
+     */
+    public PersistableBundle getMetrics() {
+        PersistableBundle bundle = native_getMetrics();
+        return bundle;
+    }
+
+    private native PersistableBundle native_getMetrics();
+
     //--------------------------------------------------------------------------
     // Initialization / configuration
     //--------------------
@@ -1739,4 +1757,46 @@
     private static void loge(String msg) {
         Log.e(TAG, msg);
     }
+
+    public static final class MetricsConstants
+    {
+        private MetricsConstants() {}
+
+        /**
+         * Key to extract the output format being recorded
+         * from the {@link AudioRecord#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String ENCODING = "android.media.audiorecord.encoding";
+
+        /**
+         * Key to extract the Source Type for this track
+         * from the {@link AudioRecord#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String SOURCE = "android.media.audiorecord.source";
+
+        /**
+         * Key to extract the estimated latency through the recording pipeline
+         * from the {@link AudioRecord#getMetrics} return value.
+         * This is in units of milliseconds.
+         * The value is an integer.
+         */
+        public static final String LATENCY = "android.media.audiorecord.latency";
+
+        /**
+         * Key to extract the sink sample rate for this record track in Hz
+         * from the {@link AudioRecord#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String SAMPLERATE = "android.media.audiorecord.samplerate";
+
+        /**
+         * Key to extract the number of channels being recorded in this record track
+         * from the {@link AudioRecord#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String CHANNELS = "android.media.audiorecord.channels";
+
+    }
 }
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 5928d03..4e9ce8e 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -36,6 +36,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -1718,6 +1719,23 @@
          return ret;
      }
 
+    /**
+     *  Return Metrics data about the current AudioTrack instance.
+     *
+     * @return a {@link PersistableBundle} containing the set of attributes and values
+     * available for the media being handled by this instance of AudioTrack
+     * The attributes are descibed in {@link MetricsConstants}.
+     *
+     * Additional vendor-specific fields may also be present in
+     * the return value.
+     */
+    public PersistableBundle getMetrics() {
+        PersistableBundle bundle = native_getMetrics();
+        return bundle;
+    }
+
+    private native PersistableBundle native_getMetrics();
+
     //--------------------------------------------------------------------------
     // Initialization / configuration
     //--------------------
@@ -3239,4 +3257,46 @@
     private static void loge(String msg) {
         Log.e(TAG, msg);
     }
+
+    public final static class MetricsConstants
+    {
+        private MetricsConstants() {}
+
+        /**
+         * Key to extract the Stream Type for this track
+         * from the {@link AudioTrack#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String STREAMTYPE = "android.media.audiotrack.streamtype";
+
+        /**
+         * Key to extract the Content Type for this track
+         * from the {@link AudioTrack#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String CONTENTTYPE = "android.media.audiotrack.type";
+
+        /**
+         * Key to extract the Content Type for this track
+         * from the {@link AudioTrack#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String USAGE = "android.media.audiotrack.usage";
+
+        /**
+         * Key to extract the sample rate for this track in Hz
+         * from the {@link AudioTrack#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String SAMPLERATE = "android.media.audiorecord.samplerate";
+
+        /**
+         * Key to extract the channel mask information for this track
+         * from the {@link AudioTrack#getMetrics} return value.
+         *
+         * The value is a Long integer.
+         */
+        public static final String CHANNELMASK = "android.media.audiorecord.channelmask";
+
+    }
 }
diff --git a/media/java/android/media/MediaActionSound.java b/media/java/android/media/MediaActionSound.java
index 983ca75..dcd4dce 100644
--- a/media/java/android/media/MediaActionSound.java
+++ b/media/java/android/media/MediaActionSound.java
@@ -47,11 +47,16 @@
     private SoundPool mSoundPool;
     private SoundState[] mSounds;
 
+    private static final String[] SOUND_DIRS = {
+        "/product/media/audio/ui/",
+        "/system/media/audio/ui/",
+    };
+
     private static final String[] SOUND_FILES = {
-        "/system/media/audio/ui/camera_click.ogg",
-        "/system/media/audio/ui/camera_focus.ogg",
-        "/system/media/audio/ui/VideoRecord.ogg",
-        "/system/media/audio/ui/VideoStop.ogg"
+        "camera_click.ogg",
+        "camera_focus.ogg",
+        "VideoRecord.ogg",
+        "VideoStop.ogg"
     };
 
     private static final String TAG = "MediaActionSound";
@@ -132,12 +137,16 @@
     }
 
     private int loadSound(SoundState sound) {
-        int id = mSoundPool.load(SOUND_FILES[sound.name], 1);
-        if (id > 0) {
-            sound.state = STATE_LOADING;
-            sound.id = id;
+        final String soundFileName = SOUND_FILES[sound.name];
+        for (String soundDir : SOUND_DIRS) {
+            int id = mSoundPool.load(soundDir + soundFileName, 1);
+            if (id > 0) {
+                sound.state = STATE_LOADING;
+                sound.id = id;
+                return id;
+            }
         }
-        return id;
+        return 0;
     }
 
     /**
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 2c1b4b3..174d6a3 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -626,6 +626,12 @@
      */
     public native long getSampleTime();
 
+    /**
+     * @return size of the current sample in bytes or -1 if no more
+     * samples are available.
+     */
+    public native long getSampleSize();
+
     // Keep these in sync with their equivalents in NuMediaExtractor.h
     /**
      * The sample is a sync sample (or in {@link MediaCodec}'s terminology
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index cb4e46f..c309038 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -158,6 +158,7 @@
     public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
     public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
     private static final String SYSTEM_SOUNDS_DIR = "/system/media/audio";
+    private static final String PRODUCT_SOUNDS_DIR = "/product/media/audio";
     private static String sLastInternalScanFingerprint;
 
     private static final String[] ID3_GENRES = {
@@ -1153,7 +1154,10 @@
     private static boolean isSystemSoundWithMetadata(String path) {
         if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR)
                 || path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR)
-                || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
+                || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
+                || path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR)
+                || path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR)
+                || path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
             return true;
         }
         return false;
diff --git a/media/java/android/media/update/VideoView2Provider.java b/media/java/android/media/update/VideoView2Provider.java
index 416ea98..5c05ce4 100644
--- a/media/java/android/media/update/VideoView2Provider.java
+++ b/media/java/android/media/update/VideoView2Provider.java
@@ -18,6 +18,7 @@
 
 import android.media.AudioAttributes;
 import android.media.MediaPlayerBase;
+import android.media.session.MediaController;
 import android.net.Uri;
 import android.widget.MediaControlView2;
 import android.widget.VideoView2;
@@ -41,20 +42,14 @@
 // TODO @SystemApi
 public interface VideoView2Provider extends ViewProvider {
     void setMediaControlView2_impl(MediaControlView2 mediaControlView);
+    MediaController getMediaController_impl();
     MediaControlView2 getMediaControlView2_impl();
-    void start_impl();
-    void pause_impl();
-    int getDuration_impl();
-    int getCurrentPosition_impl();
-    void seekTo_impl(int msec);
-    boolean isPlaying_impl();
-    int getBufferPercentage_impl();
     int getAudioSessionId_impl();
     void showSubtitle_impl();
     void hideSubtitle_impl();
     void setFullScreen_impl(boolean fullScreen);
+    // TODO: remove setSpeed_impl once MediaController2 is ready.
     void setSpeed_impl(float speed);
-    float getSpeed_impl();
     void setAudioFocusRequest_impl(int focusGain);
     void setAudioAttributes_impl(AudioAttributes attributes);
     void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player);
@@ -63,7 +58,6 @@
     void setVideoURI_impl(Uri uri, Map<String, String> headers);
     void setViewType_impl(int viewType);
     int getViewType_impl();
-    void stopPlayback_impl();
     void setOnPreparedListener_impl(VideoView2.OnPreparedListener l);
     void setOnCompletionListener_impl(VideoView2.OnCompletionListener l);
     void setOnErrorListener_impl(VideoView2.OnErrorListener l);
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 4b4a2556..051c802 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -12,7 +12,6 @@
         "android_media_MediaDrm.cpp",
         "android_media_MediaExtractor.cpp",
         "android_media_MediaHTTPConnection.cpp",
-        "android_media_MediaMetricsJNI.cpp",
         "android_media_MediaMetadataRetriever.cpp",
         "android_media_MediaMuxer.cpp",
         "android_media_MediaPlayer.cpp",
@@ -93,7 +92,6 @@
         "android_media_MediaCrypto.cpp",
         "android_media_Media2DataSource.cpp",
         "android_media_MediaDrm.cpp",
-        "android_media_MediaMetricsJNI.cpp",
         "android_media_MediaPlayer2.cpp",
         "android_media_SyncParams.cpp",
     ],
@@ -110,6 +108,9 @@
         "libhidlbase",  // VNDK???
         "libmediandk",  // NDK
         "libpowermanager",  // for JWakeLock. to be removed
+
+        "libutils",  // Have to use shared lib to make libandroid_runtime behave correctly.
+                     // Otherwise, AndroidRuntime::getJNIEnv() will return NULL.
     ],
 
     header_libs: ["libhardware_headers"],
@@ -143,7 +144,6 @@
         "libstagefright_rtsp",
         "libstagefright_timedtext",
         "libunwindstack",
-        "libutils",
         "libutilscallstack",
         "libvndksupport",
         "libz",
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 5c90d00..a855526 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -244,6 +244,10 @@
     return mImpl->getSampleTime(sampleTimeUs);
 }
 
+status_t JMediaExtractor::getSampleSize(size_t *sampleSize) {
+    return mImpl->getSampleSize(sampleSize);
+}
+
 status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
     *sampleFlags = 0;
 
@@ -505,6 +509,28 @@
     return (jlong) sampleTimeUs;
 }
 
+static jlong android_media_MediaExtractor_getSampleSize(
+        JNIEnv *env, jobject thiz) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return -1ll;
+    }
+
+    size_t sampleSize;
+    status_t err = extractor->getSampleSize(&sampleSize);
+
+    if (err == ERROR_END_OF_STREAM) {
+        return -1ll;
+    } else if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return -1ll;
+    }
+
+    return (jlong) sampleSize;
+}
+
 static jint android_media_MediaExtractor_getSampleFlags(
         JNIEnv *env, jobject thiz) {
     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
@@ -884,6 +910,9 @@
     { "getSampleTime", "()J",
         (void *)android_media_MediaExtractor_getSampleTime },
 
+    { "getSampleSize", "()J",
+        (void *)android_media_MediaExtractor_getSampleSize },
+
     { "getSampleFlags", "()I",
         (void *)android_media_MediaExtractor_getSampleFlags },
 
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index a4638ac..aaa8421 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -60,6 +60,7 @@
     status_t readSampleData(jobject byteBuf, size_t offset, size_t *sampleSize);
     status_t getSampleTrackIndex(size_t *trackIndex);
     status_t getSampleTime(int64_t *sampleTimeUs);
+    status_t getSampleSize(size_t *sampleSize);
     status_t getSampleFlags(uint32_t *sampleFlags);
     status_t getSampleMeta(sp<MetaData> *sampleMeta);
     status_t getMetrics(Parcel *reply) const;
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 3bf0b37..90ee8a6 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -1165,51 +1165,6 @@
     ;
 }
 
-// Pass through the arguments to the MediaServer player implementation.
-static jint android_media_MediaPlayer2_applyVolumeShaper(JNIEnv *env, jobject thiz,
-        jobject jconfig, jobject joperation) {
-    // NOTE: hard code here to prevent platform issues. Must match VolumeShaper.java
-    const int VOLUME_SHAPER_INVALID_OPERATION = -38;
-
-    sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
-    if (mp == nullptr) {
-        return (jint)VOLUME_SHAPER_INVALID_OPERATION;
-    }
-
-    sp<VolumeShaper::Configuration> configuration;
-    sp<VolumeShaper::Operation> operation;
-    if (jconfig != nullptr) {
-        configuration = VolumeShaperHelper::convertJobjectToConfiguration(
-                env, gVolumeShaperFields, jconfig);
-        ALOGV("applyVolumeShaper configuration: %s", configuration->toString().c_str());
-    }
-    if (joperation != nullptr) {
-        operation = VolumeShaperHelper::convertJobjectToOperation(
-                env, gVolumeShaperFields, joperation);
-        ALOGV("applyVolumeShaper operation: %s", operation->toString().c_str());
-    }
-    VolumeShaper::Status status = mp->applyVolumeShaper(configuration, operation);
-    if (status == INVALID_OPERATION) {
-        status = VOLUME_SHAPER_INVALID_OPERATION;
-    }
-    return (jint)status; // if status < 0 an error, else a VolumeShaper id
-}
-
-// Pass through the arguments to the MediaServer player implementation.
-static jobject android_media_MediaPlayer2_getVolumeShaperState(JNIEnv *env, jobject thiz,
-        jint id) {
-    sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
-    if (mp == nullptr) {
-        return (jobject)nullptr;
-    }
-
-    sp<VolumeShaper::State> state = mp->getVolumeShaperState((int)id);
-    if (state.get() == nullptr) {
-        return (jobject)nullptr;
-    }
-    return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
-}
-
 /////////////////////////////////////////////////////////////////////////////////////
 // Modular DRM begin
 
@@ -1465,12 +1420,6 @@
     {"attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer2_attachAuxEffect},
     {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I",  (void *)android_media_MediaPlayer2_setRetransmitEndpoint},
     {"setNextMediaPlayer",  "(Landroid/media/MediaPlayer2;)V",  (void *)android_media_MediaPlayer2_setNextMediaPlayer},
-    {"native_applyVolumeShaper",
-                            "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
-                                                                (void *)android_media_MediaPlayer2_applyVolumeShaper},
-    {"native_getVolumeShaperState",
-                            "(I)Landroid/media/VolumeShaper$State;",
-                                                                (void *)android_media_MediaPlayer2_getVolumeShaperState},
     // Modular DRM
     { "_prepareDrm", "([B[B)V",                                 (void *)android_media_MediaPlayer2_prepareDrm },
     { "_releaseDrm", "()V",                                     (void *)android_media_MediaPlayer2_releaseDrm },
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index cc4bc58..da50776 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -29,4 +29,9 @@
      */
     GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer,
             int maxLayer, boolean useIdentityTransform, int rotation);
+
+    /**
+     * Called when the overview service has started the recents animation.
+     */
+    void onRecentsAnimationStarted();
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index c9a6ea9..f9e1069 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -32,6 +32,7 @@
 import android.app.IAssistDataReceiver;
 import android.app.WindowConfiguration.ActivityType;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -48,7 +49,10 @@
 import android.os.UserHandle;
 import android.util.IconDrawableFactory;
 import android.util.Log;
+import android.view.IRecentsAnimationController;
+import android.view.IRecentsAnimationRunner;
 
+import android.view.RemoteAnimationTarget;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -243,10 +247,9 @@
     /**
      * Starts the recents activity. The caller should manage the thread on which this is called.
      */
-    public void startRecentsActivity(AssistDataReceiverCompat assistDataReceiver, Bundle options,
-            ActivityOptions opts, int userId, Consumer<Boolean> resultCallback,
+    public void startRecentsActivity(Intent intent, AssistDataReceiver assistDataReceiver,
+            RecentsAnimationListener animationHandler, Consumer<Boolean> resultCallback,
             Handler resultCallbackHandler) {
-        Bundle activityOptions = opts != null ? opts.toBundle() : null;
         try {
             IAssistDataReceiver receiver = null;
             if (assistDataReceiver != null) {
@@ -259,8 +262,24 @@
                     }
                 };
             }
-            ActivityManager.getService().startRecentsActivity(receiver, options, activityOptions,
-                    userId);
+            IRecentsAnimationRunner runner = null;
+            if (animationHandler != null) {
+                runner = new IRecentsAnimationRunner.Stub() {
+                    public void onAnimationStart(IRecentsAnimationController controller,
+                            RemoteAnimationTarget[] apps) {
+                        final RecentsAnimationControllerCompat controllerCompat =
+                                new RecentsAnimationControllerCompat(controller);
+                        final RemoteAnimationTargetCompat[] appsCompat =
+                                RemoteAnimationTargetCompat.wrap(apps);
+                        animationHandler.onAnimationStart(controllerCompat, appsCompat);
+                    }
+
+                    public void onAnimationCanceled() {
+                        animationHandler.onAnimationCanceled();
+                    }
+                };
+            }
+            ActivityManager.getService().startRecentsActivity(intent, receiver, runner);
             if (resultCallback != null) {
                 resultCallbackHandler.post(new Runnable() {
                     @Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
similarity index 80%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
index cd943f6..7cd6c51 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
@@ -22,7 +22,7 @@
 /**
  * Abstract class for assist data receivers.
  */
-public abstract class AssistDataReceiverCompat {
-    public abstract void onHandleAssistData(Bundle resultData);
-    public abstract void onHandleAssistScreenshot(Bitmap screenshot);
+public abstract class AssistDataReceiver {
+    public void onHandleAssistData(Bundle resultData) {}
+    public void onHandleAssistScreenshot(Bitmap screenshot) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
index db4f988..38b8ae8 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone;
+package com.android.systemui.shared.system;
 
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 
 import android.os.Binder;
 import android.os.IBinder;
@@ -29,11 +30,12 @@
 import android.view.InputEvent;
 import android.view.IWindowManager;
 import android.view.MotionEvent;
+import android.view.WindowManagerGlobal;
 
 import java.io.PrintWriter;
 
 /**
- * Manages the input consumer that allows the SystemUI to control the PiP.
+ * Manages the input consumer that allows the SystemUI to directly receive touch input.
  */
 public class InputConsumerController {
 
@@ -55,12 +57,12 @@
     }
 
     /**
-     * Input handler used for the PiP input consumer. Input events are batched and consumed with the
+     * Input handler used for the input consumer. Input events are batched and consumed with the
      * SurfaceFlinger vsync.
      */
-    private final class PipInputEventReceiver extends BatchedInputEventReceiver {
+    private final class InputEventReceiver extends BatchedInputEventReceiver {
 
-        public PipInputEventReceiver(InputChannel inputChannel, Looper looper) {
+        public InputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper, Choreographer.getSfInstance());
         }
 
@@ -68,7 +70,6 @@
         public void onInputEvent(InputEvent event, int displayId) {
             boolean handled = true;
             try {
-                // To be implemented for input handling over Pip windows
                 if (mListener != null && event instanceof MotionEvent) {
                     MotionEvent ev = (MotionEvent) event;
                     handled = mListener.onTouchEvent(ev);
@@ -81,15 +82,35 @@
 
     private final IWindowManager mWindowManager;
     private final IBinder mToken;
+    private final String mName;
 
-    private PipInputEventReceiver mInputEventReceiver;
+    private InputEventReceiver mInputEventReceiver;
     private TouchListener mListener;
     private RegistrationListener mRegistrationListener;
 
-    public InputConsumerController(IWindowManager windowManager) {
+    /**
+     * @param name the name corresponding to the input consumer that is defined in the system.
+     */
+    public InputConsumerController(IWindowManager windowManager, String name) {
         mWindowManager = windowManager;
         mToken = new Binder();
-        registerInputConsumer();
+        mName = name;
+    }
+
+    /**
+     * @return A controller for the pip input consumer.
+     */
+    public static InputConsumerController getPipInputConsumer() {
+        return new InputConsumerController(WindowManagerGlobal.getWindowManagerService(),
+                INPUT_CONSUMER_PIP);
+    }
+
+    /**
+     * @return A controller for the recents animation input consumer.
+     */
+    public static InputConsumerController getRecentsAnimationInputConsumer() {
+        return new InputConsumerController(WindowManagerGlobal.getWindowManagerService(),
+                INPUT_CONSUMER_RECENTS_ANIMATION);
     }
 
     /**
@@ -125,12 +146,12 @@
         if (mInputEventReceiver == null) {
             final InputChannel inputChannel = new InputChannel();
             try {
-                mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
-                mWindowManager.createInputConsumer(mToken, INPUT_CONSUMER_PIP, inputChannel);
+                mWindowManager.destroyInputConsumer(mName);
+                mWindowManager.createInputConsumer(mToken, mName, inputChannel);
             } catch (RemoteException e) {
-                Log.e(TAG, "Failed to create PIP input consumer", e);
+                Log.e(TAG, "Failed to create input consumer", e);
             }
-            mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
+            mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper());
             if (mRegistrationListener != null) {
                 mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
             }
@@ -143,9 +164,9 @@
     public void unregisterInputConsumer() {
         if (mInputEventReceiver != null) {
             try {
-                mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+                mWindowManager.destroyInputConsumer(mName);
             } catch (RemoteException e) {
-                Log.e(TAG, "Failed to destroy PIP input consumer", e);
+                Log.e(TAG, "Failed to destroy input consumer", e);
             }
             mInputEventReceiver.dispose();
             mInputEventReceiver = null;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
new file mode 100644
index 0000000..9a7abf8
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.app.ActivityManager.TaskSnapshot;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IRecentsAnimationController;
+
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+public class RecentsAnimationControllerCompat {
+
+    private static final String TAG = RecentsAnimationControllerCompat.class.getSimpleName();
+
+    private IRecentsAnimationController mAnimationController;
+
+    public RecentsAnimationControllerCompat(IRecentsAnimationController animationController) {
+        mAnimationController = animationController;
+    }
+
+    public ThumbnailData screenshotTask(int taskId) {
+        try {
+            TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId);
+            return snapshot != null ? new ThumbnailData(snapshot) : new ThumbnailData();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to screenshot task", e);
+            return new ThumbnailData();
+        }
+    }
+
+    public void setInputConsumerEnabled(boolean enabled) {
+        try {
+            mAnimationController.setInputConsumerEnabled(enabled);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to set input consumer enabled state", e);
+        }
+    }
+
+    public void finish(boolean toHome) {
+        try {
+            mAnimationController.finish(toHome);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to finish recents animation", e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
similarity index 60%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java
copy to packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index cd943f6..bf6179d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiverCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -16,13 +16,16 @@
 
 package com.android.systemui.shared.system;
 
-import android.graphics.Bitmap;
-import android.os.Bundle;
+public interface RecentsAnimationListener {
 
-/**
- * Abstract class for assist data receivers.
- */
-public abstract class AssistDataReceiverCompat {
-    public abstract void onHandleAssistData(Bundle resultData);
-    public abstract void onHandleAssistScreenshot(Bitmap screenshot);
-}
+    /**
+     * Called when the animation into Recents can start. This call is made on the binder thread.
+     */
+    void onAnimationStart(RecentsAnimationControllerCompat controller,
+            RemoteAnimationTargetCompat[] apps);
+
+    /**
+     * Called when the animation into Recents was canceled. This call is made on the binder thread.
+     */
+    void onAnimationCanceled();
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 9ff6815..cb732c4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -51,6 +51,8 @@
 import androidx.app.slice.Slice;
 import androidx.app.slice.SliceItem;
 import androidx.app.slice.core.SliceQuery;
+import androidx.app.slice.widget.ListContent;
+import androidx.app.slice.widget.RowContent;
 import androidx.app.slice.widget.SliceLiveData;
 
 /**
@@ -115,25 +117,17 @@
 
     private void showSlice(Slice slice) {
 
-        // Main area
-        SliceItem mainItem = SliceQuery.find(slice, android.app.slice.SliceItem.FORMAT_SLICE,
-                null /* hints */, new String[]{android.app.slice.Slice.HINT_LIST_ITEM});
-        mHasHeader = mainItem != null;
-
-        List<SliceItem> subItems = SliceQuery.findAll(slice,
-                android.app.slice.SliceItem.FORMAT_SLICE,
-                new String[]{android.app.slice.Slice.HINT_LIST_ITEM},
-                null /* nonHints */);
-
+        ListContent lc = new ListContent(slice);
+        mHasHeader = lc.hasHeader();
+        List<SliceItem> subItems = lc.getRowItems();
         if (!mHasHeader) {
             mTitle.setVisibility(GONE);
         } else {
             mTitle.setVisibility(VISIBLE);
-            SliceItem mainTitle = SliceQuery.find(mainItem.getSlice(),
-                    android.app.slice.SliceItem.FORMAT_TEXT,
-                    new String[]{android.app.slice.Slice.HINT_TITLE},
-                    null /* nonHints */);
-            CharSequence title = mainTitle.getText();
+            // If there's a header it'll be the first subitem
+            RowContent header = new RowContent(subItems.get(0), true /* showStartItem */);
+            SliceItem mainTitle = header.getTitleItem();
+            CharSequence title = mainTitle != null ? mainTitle.getText() : null;
             mTitle.setText(title);
 
             // Check if we're already ellipsizing the text.
@@ -152,9 +146,10 @@
         mClickActions.clear();
         final int subItemsCount = subItems.size();
         final int blendedColor = getTextColor();
-
-        for (int i = 0; i < subItemsCount; i++) {
+        final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it
+        for (int i = startIndex; i < subItemsCount; i++) {
             SliceItem item = subItems.get(i);
+            RowContent rc = new RowContent(item, true /* showStartItem */);
             final Uri itemTag = item.getSlice().getUri();
             // Try to reuse the view if already exists in the layout
             KeyguardSliceButton button = mRow.findViewWithTag(itemTag);
@@ -168,20 +163,13 @@
             button.setHasDivider(i < subItemsCount - 1);
             mRow.addView(button, i);
 
-            PendingIntent pendingIntent;
-            try {
-                pendingIntent = item.getAction();
-            } catch (RuntimeException e) {
-                Log.w(TAG, "Cannot retrieve action from keyguard slice", e);
-                pendingIntent = null;
+            PendingIntent pendingIntent = null;
+            if (rc.getContentIntent() != null) {
+                pendingIntent = rc.getContentIntent().getAction();
             }
             mClickActions.put(button, pendingIntent);
 
-            SliceItem title = SliceQuery.find(item.getSlice(),
-                    android.app.slice.SliceItem.FORMAT_TEXT,
-                    new String[]{android.app.slice.Slice.HINT_TITLE},
-                    null /* nonHints */);
-            button.setText(title.getText());
+            button.setText(rc.getTitleItem().getText());
 
             Drawable iconDrawable = null;
             SliceItem icon = SliceQuery.find(item.getSlice(),
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 244c1b9..b6e49ae 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -77,6 +77,15 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        public void onRecentsAnimationStarted() {
+            long token = Binder.clearCallingIdentity();
+            try {
+                notifyRecentsAnimationStarted();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     };
 
     private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() {
@@ -214,6 +223,12 @@
         }
     }
 
+    private void notifyRecentsAnimationStarted() {
+        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+            mConnectionCallbacks.get(i).onRecentsAnimationStarted();
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println(TAG_OPS + " state:");
@@ -224,6 +239,7 @@
     }
 
     public interface OverviewProxyListener {
-        void onConnectionChanged(boolean isConnected);
+        default void onConnectionChanged(boolean isConnected) {}
+        default void onRecentsAnimationStarted() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 36531bb..24d0126 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -16,12 +16,10 @@
 
 package com.android.systemui.pip.phone;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 
 import android.app.ActivityManager;
-import android.app.ActivityManager.StackInfo;
 import android.app.IActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -43,6 +41,7 @@
 import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputConsumerController;
 
 import java.io.PrintWriter;
 
@@ -174,7 +173,8 @@
         }
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
 
-        mInputConsumerController = new InputConsumerController(mWindowManager);
+        mInputConsumerController = InputConsumerController.getPipInputConsumer();
+        mInputConsumerController.registerInputConsumer();
         mMediaController = new PipMediaController(context, mActivityManager);
         mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController,
                 mInputConsumerController);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 9fb201b..26fced3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -23,7 +23,6 @@
 import android.app.ActivityOptions;
 import android.app.IActivityManager;
 import android.app.RemoteAction;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
@@ -43,6 +42,7 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.component.HidePipMenuEvent;
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.shared.system.InputConsumerController;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index c0fed34..b253517 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -46,6 +46,7 @@
 import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.policy.PipSnapAlgorithm;
 import com.android.systemui.R;
+import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 
 import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 1056ecc..b3f68d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -1472,7 +1472,6 @@
         return mPrivateLayout.getActiveRemoteInputText();
     }
 
-
     public void animateTranslateNotification(final float leftTarget) {
         if (mTranslateAnim != null) {
             mTranslateAnim.cancel();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 8336d29..907af69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.graphics.Matrix;
 import android.graphics.Rect;
@@ -35,10 +36,11 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationListContainer;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarWindowView;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import java.util.function.Consumer;
 
@@ -57,16 +59,17 @@
     private final NotificationPanelView mNotificationPanel;
     private final NotificationListContainer mNotificationContainer;
     private final StatusBarWindowView mStatusBarWindow;
-    private final Consumer<Boolean> mPanelCollapser;
+    private final StatusBar mStatusBar;
+    private boolean mAnimationPending;
 
     public ActivityLaunchAnimator(StatusBarWindowView statusBarWindow,
-            Consumer<Boolean> panelCollapser,
+            StatusBar statusBar,
             NotificationPanelView notificationPanel,
             NotificationListContainer container) {
         mNotificationPanel = notificationPanel;
         mNotificationContainer = container;
         mStatusBarWindow = statusBarWindow;
-        mPanelCollapser = panelCollapser;
+        mStatusBar = statusBar;
     }
 
     public ActivityOptions getLaunchAnimation(
@@ -76,6 +79,21 @@
                 new RemoteAnimationAdapter(animationRunner, 1000 /* Duration */, 0 /* delay */));
     }
 
+    public boolean isAnimationPending() {
+        return mAnimationPending;
+    }
+
+    public void setLaunchResult(int launchResult) {
+        setAnimationPending((launchResult == ActivityManager.START_TASK_TO_FRONT
+                || launchResult == ActivityManager.START_SUCCESS)
+                        && mStatusBar.getBarState() == StatusBarState.SHADE);
+    }
+
+    private void setAnimationPending(boolean pending) {
+        mAnimationPending = pending;
+        mStatusBarWindow.setExpandAnimationPending(pending);
+    }
+
     class AnimationRunner extends IRemoteAnimationRunner.Stub {
 
         private final ExpandableNotificationRow mSourceNotification;
@@ -94,7 +112,6 @@
                 IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
                     throws RemoteException {
             mSourceNotification.post(() -> {
-                boolean first = true;
                 for (RemoteAnimationTarget app : remoteAnimationTargets) {
                     if (app.mode == RemoteAnimationTarget.MODE_OPENING) {
                         setExpandAnimationRunning(true);
@@ -139,7 +156,7 @@
                             public void onAnimationEnd(Animator animation) {
                                 setExpandAnimationRunning(false);
                                 if (mInstantCollapsePanel) {
-                                    mPanelCollapser.accept(false /* animate */);
+                                    mStatusBar.collapsePanel(false /* animate */);
                                 }
                                 try {
                                     iRemoteAnimationFinishedCallback.onAnimationFinished();
@@ -152,6 +169,7 @@
                         break;
                     }
                 }
+                setAnimationPending(false);
             });
         }
 
@@ -198,6 +216,10 @@
 
         @Override
         public void onAnimationCancelled() throws RemoteException {
+            mSourceNotification.post(() -> {
+                setAnimationPending(false);
+                mStatusBar.onLaunchAnimationCancelled();
+            });
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index dec5303..918b6ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -137,8 +137,12 @@
         final View transformedView = mTransformedView;
         boolean transformX = (transformationFlags & TRANSFORM_X) != 0;
         boolean transformY = (transformationFlags & TRANSFORM_Y) != 0;
-        boolean differentHeight = otherState.getViewHeight() != getViewHeight();
-        boolean differentWidth = otherState.getViewWidth() != getViewWidth();
+        int viewHeight = getViewHeight();
+        int otherHeight = otherState.getViewHeight();
+        boolean differentHeight = otherHeight != viewHeight && otherHeight != 0 && viewHeight != 0;
+        int viewWidth = getViewWidth();
+        int otherWidth = otherState.getViewWidth();
+        boolean differentWidth = otherWidth != viewWidth && otherWidth != 0 && viewWidth != 0;
         boolean transformScale = transformScale(otherState) && (differentHeight || differentWidth);
         // lets animate the positions correctly
         if (transformationAmount == 0.0f
@@ -165,15 +169,15 @@
                 // we also want to animate the scale if we're the same
                 View otherView = otherState.getTransformedView();
                 if (transformScale && differentWidth) {
-                    setTransformationStartScaleX(otherState.getViewWidth() * otherView.getScaleX()
-                            / (float) getViewWidth());
+                    setTransformationStartScaleX(otherWidth * otherView.getScaleX()
+                            / (float) viewWidth);
                     transformedView.setPivotX(0);
                 } else {
                     setTransformationStartScaleX(UNDEFINED);
                 }
                 if (transformScale && differentHeight) {
-                    setTransformationStartScaleY(otherState.getViewHeight() * otherView.getScaleY()
-                            / (float) getViewHeight());
+                    setTransformationStartScaleY(otherHeight * otherView.getScaleY()
+                            / (float) viewHeight);
                     transformedView.setPivotY(0);
                 } else {
                     setTransformationStartScaleY(UNDEFINED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 6063151..5401e74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -225,7 +225,7 @@
         mNavigationBarView = (NavigationBarView) view;
 
         mNavigationBarView.setDisabledFlags(mDisabledFlags1);
-        mNavigationBarView.setComponents(mRecents, mDivider);
+        mNavigationBarView.setComponents(mRecents, mDivider, mStatusBar.getPanel());
         mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
         mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
         if (savedInstanceState != null) {
@@ -300,7 +300,7 @@
             boolean showImeSwitcher) {
         boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
         int hints = mNavigationIconHints;
-        if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
+        if (imeShown && backDisposition != InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS) {
             hints |= NAVIGATION_HINT_BACK_ALT;
         } else {
             hints &= ~NAVIGATION_HINT_BACK_ALT;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index ff923e5..0954fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -34,6 +34,7 @@
 import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.utilities.Utilities;
@@ -78,6 +79,7 @@
     private final int mScrollTouchSlop;
     private final Matrix mTransformGlobalMatrix = new Matrix();
     private final Matrix mTransformLocalMatrix = new Matrix();
+    private final StatusBar mStatusBar;
     private int mTouchDownX;
     private int mTouchDownY;
     private boolean mDownOnRecents;
@@ -90,6 +92,7 @@
 
     public NavigationBarGestureHelper(Context context) {
         mContext = context;
+        mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
         Resources r = context.getResources();
         mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance);
         mQuickScrubController = new QuickScrubController(context);
@@ -146,7 +149,8 @@
                 break;
             }
         }
-        if (!mQuickScrubController.onInterceptTouchEvent(event)) {
+        if (mStatusBar.isPresenterFullyCollapsed()
+                && !mQuickScrubController.onInterceptTouchEvent(event)) {
             proxyMotionEvents(event);
             return false;
         }
@@ -304,7 +308,8 @@
     }
 
     public boolean onTouchEvent(MotionEvent event) {
-        boolean result = mQuickScrubController.onTouchEvent(event) || proxyMotionEvents(event);
+        boolean result = mStatusBar.isPresenterFullyCollapsed()
+                && (mQuickScrubController.onTouchEvent(event) || proxyMotionEvents(event));
         if (mDockWindowEnabled) {
             result |= handleDockWindowEvent(event);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 9bef0ee..b5fa523 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -104,6 +104,7 @@
     private DeadZone mDeadZone;
     private final NavigationBarTransitions mBarTransitions;
     private final OverviewProxyService mOverviewProxyService;
+    private boolean mRecentsAnimationStarted;
 
     // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288)
     final static boolean WORKAROUND_INVALID_LAYOUT = true;
@@ -126,6 +127,7 @@
     private RecentsComponent mRecentsComponent;
     private Divider mDivider;
     private SwipeUpOnboarding mSwipeUpOnboarding;
+    private NotificationPanelView mPanelView;
 
     private class NavTransitionListener implements TransitionListener {
         private boolean mBackTransitioning;
@@ -205,10 +207,18 @@
         }
     }
 
-    private final OverviewProxyListener mOverviewProxyListener = isConnected -> {
-        setSlippery(!isConnected);
-        setDisabledFlags(mDisabledFlags, true);
-        setUpSwipeUpOnboarding(isConnected);
+    private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
+        @Override
+        public void onConnectionChanged(boolean isConnected) {
+            updateSlippery();
+            setDisabledFlags(mDisabledFlags, true);
+            setUpSwipeUpOnboarding(isConnected);
+        }
+
+        @Override
+        public void onRecentsAnimationStarted() {
+            mRecentsAnimationStarted = true;
+        }
     };
 
     public NavigationBarView(Context context, AttributeSet attrs) {
@@ -251,9 +261,11 @@
         return mBarTransitions.getLightTransitionsController();
     }
 
-    public void setComponents(RecentsComponent recentsComponent, Divider divider) {
+    public void setComponents(RecentsComponent recentsComponent, Divider divider,
+            NotificationPanelView panel) {
         mRecentsComponent = recentsComponent;
         mDivider = divider;
+        mPanelView = panel;
         if (mGestureHelper instanceof NavigationBarGestureHelper) {
             ((NavigationBarGestureHelper) mGestureHelper).setComponents(
                     recentsComponent, divider, this);
@@ -270,12 +282,26 @@
         if (mGestureHelper.onTouchEvent(event)) {
             return true;
         }
-        return super.onTouchEvent(event);
+        return mRecentsAnimationStarted || super.onTouchEvent(event);
     }
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        return mGestureHelper.onInterceptTouchEvent(event);
+        int action = event.getActionMasked();
+        if (action == MotionEvent.ACTION_DOWN) {
+            mRecentsAnimationStarted = false;
+        } else if (action == MotionEvent.ACTION_UP) {
+            // If the overview proxy service has not started the recents animation then clean up
+            // after it to ensure that the nav bar buttons still work
+            if (mOverviewProxyService.getProxy() != null && !mRecentsAnimationStarted) {
+                try {
+                    ActivityManager.getService().cancelRecentsAnimation();
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Could not cancel recents animation");
+                }
+            }
+        }
+        return mRecentsAnimationStarted || mGestureHelper.onInterceptTouchEvent(event);
     }
 
     public void abortCurrentGesture() {
@@ -571,6 +597,14 @@
         }
     }
 
+    public void onPanelExpandedChange(boolean expanded) {
+        updateSlippery();
+    }
+
+    private void updateSlippery() {
+        setSlippery(mOverviewProxyService.getProxy() != null && mPanelView.isFullyExpanded());
+    }
+
     private void setSlippery(boolean slippery) {
         boolean changed = false;
         final ViewGroup navbarView = ((ViewGroup) getParent());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index a62a424..2b7e474 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -950,7 +950,7 @@
     }
 
     public boolean isCollapsing() {
-        return mClosing;
+        return mClosing || mLaunchingNotification;
     }
 
     public boolean isTracking() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index b181212..cc5a93c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -250,6 +250,9 @@
         super.panelExpansionChanged(frac, expanded);
         mPanelFraction = frac;
         updateScrimFraction();
+        if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
+            mBar.getNavigationBarView().onPanelExpandedChange(expanded);
+        }
     }
 
     private void updateScrimFraction() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
index ee1d088..001a1a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
+import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -82,7 +83,10 @@
     private boolean mDragPositive;
     private boolean mIsVertical;
     private boolean mIsRTL;
-    private float mMaxTrackPaintAlpha;
+    private float mTrackAlpha;
+    private int mLightTrackColor;
+    private int mDarkTrackColor;
+    private float mDarkIntensity;
 
     private final Handler mHandler = new Handler();
     private final Interpolator mQuickScrubEndInterpolator = new DecelerateInterpolator();
@@ -98,9 +102,10 @@
     private final ValueAnimator mButtonAnimator;
     private final AnimatorSet mQuickScrubEndAnimator;
     private final Context mContext;
+    private final ArgbEvaluator mTrackColorEvaluator = new ArgbEvaluator();
 
     private final AnimatorUpdateListener mTrackAnimatorListener = valueAnimator -> {
-        mTrackPaint.setAlpha(Math.round((float) valueAnimator.getAnimatedValue() * 255));
+        mTrackAlpha = (float) valueAnimator.getAnimatedValue();
         mNavigationBarView.invalidate();
     };
 
@@ -167,6 +172,7 @@
         mGestureDetector = new GestureDetector(mContext, mGestureListener);
         mTrackThickness = getDimensionPixelSize(mContext, R.dimen.nav_quick_scrub_track_thickness);
         mTrackPadding = getDimensionPixelSize(mContext, R.dimen.nav_quick_scrub_track_edge_padding);
+        mTrackPaint.setAlpha(0);
 
         mTrackAnimator = ObjectAnimator.ofFloat();
         mTrackAnimator.addUpdateListener(mTrackAnimatorListener);
@@ -291,6 +297,10 @@
 
     @Override
     public void onDraw(Canvas canvas) {
+        int color = (int) mTrackColorEvaluator.evaluate(mDarkIntensity, mLightTrackColor,
+                mDarkTrackColor);
+        mTrackPaint.setColor(color);
+        mTrackPaint.setAlpha((int) (mTrackPaint.getAlpha() * mTrackAlpha));
         canvas.drawRect(mTrackRect, mTrackPaint);
     }
 
@@ -326,13 +336,8 @@
 
     @Override
     public void onDarkIntensityChange(float intensity) {
-        if (intensity == 0) {
-            mTrackPaint.setColor(mContext.getColor(R.color.quick_step_track_background_light));
-        } else if (intensity == 1) {
-            mTrackPaint.setColor(mContext.getColor(R.color.quick_step_track_background_dark));
-        }
-        mMaxTrackPaintAlpha = mTrackPaint.getAlpha() * 1f / 255;
-        mTrackPaint.setAlpha(0);
+        mDarkIntensity = intensity;
+        mNavigationBarView.invalidate();
     }
 
     @Override
@@ -365,7 +370,9 @@
     private void startQuickScrub() {
         if (!mQuickScrubActive) {
             mQuickScrubActive = true;
-            mTrackAnimator.setFloatValues(0, mMaxTrackPaintAlpha);
+            mLightTrackColor = mContext.getColor(R.color.quick_step_track_background_light);
+            mDarkTrackColor = mContext.getColor(R.color.quick_step_track_background_dark);
+            mTrackAnimator.setFloatValues(0, 1);
             mTrackAnimator.start();
             try {
                 mOverviewEventSender.getProxy().onQuickScrubStart();
@@ -382,7 +389,7 @@
         mHandler.removeCallbacks(mLongPressRunnable);
         if (mDraggingActive || mQuickScrubActive) {
             mButtonAnimator.setIntValues((int) mTranslation, 0);
-            mTrackAnimator.setFloatValues(mTrackPaint.getAlpha() * 1f / 255, 0);
+            mTrackAnimator.setFloatValues(mTrackAlpha, 0);
             mQuickScrubEndAnimator.start();
             try {
                 mOverviewEventSender.getProxy().onQuickScrubEnd();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 7d84550..b519824 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -100,7 +100,6 @@
 import android.service.notification.StatusBarNotification;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
-import android.text.SpannedString;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -589,7 +588,7 @@
 
     private NavigationBarFragment mNavigationBar;
     private View mNavigationBarView;
-    private ActivityLaunchAnimator mActivityLaunchAnimator;
+    protected ActivityLaunchAnimator mActivityLaunchAnimator;
 
     @Override
     public void start() {
@@ -760,7 +759,7 @@
         mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
         mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
         mActivityLaunchAnimator = new ActivityLaunchAnimator(mStatusBarWindow,
-                this::collapsePanel,
+                this,
                 mNotificationPanel,
                 mStackScroller);
         mGutsManager.setUpWithPresenter(this, mEntryManager, mStackScroller, mCheckSaveListener,
@@ -2054,6 +2053,12 @@
         }
     }
 
+    public void onLaunchAnimationCancelled() {
+        if (!isCollapsing()) {
+            onClosingFinished();
+        }
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
@@ -3418,7 +3423,7 @@
     }
 
     public boolean isCollapsing() {
-        return mNotificationPanel.isCollapsing();
+        return mNotificationPanel.isCollapsing() || mActivityLaunchAnimator.isAnimationPending();
     }
 
     public void addPostCollapseAction(Runnable r) {
@@ -4959,6 +4964,7 @@
                     try {
                         launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
                                 null, null, getActivityOptions(row));
+                        mActivityLaunchAnimator.setLaunchResult(launchResult);
                     } catch (PendingIntent.CanceledException e) {
                         // the stack trace isn't very helpful here.
                         // Just log the exception message.
@@ -4970,7 +4976,7 @@
                         mAssistManager.hideAssist();
                     }
                 }
-                if (shouldCollapse(launchResult)) {
+                if (shouldCollapse()) {
                     if (Looper.getMainLooper().isCurrentThread()) {
                         collapsePanel();
                     } else {
@@ -5003,17 +5009,8 @@
         }, afterKeyguardGone);
     }
 
-    private boolean shouldCollapse(int launchResult) {
-        return mState != StatusBarState.SHADE
-                || (launchResult != ActivityManager.START_TASK_TO_FRONT
-                        && launchResult != ActivityManager.START_SUCCESS);
-    }
-
-    public void onExpandAnimationFinished() {
-        if (!isPresenterFullyCollapsed()) {
-            instantCollapseNotificationPanel();
-            visibilityChanged(false);
-        }
+    private boolean shouldCollapse() {
+        return mState != StatusBarState.SHADE || !mActivityLaunchAnimator.isAnimationPending();
     }
 
     public void collapsePanel(boolean animate) {
@@ -5128,7 +5125,8 @@
                         .addNextIntentWithParentStack(intent)
                         .startActivities(getActivityOptions(row),
                                 new UserHandle(UserHandle.getUserId(appUid)));
-                if (shouldCollapse(launchResult)) {
+                mActivityLaunchAnimator.setLaunchResult(launchResult);
+                if (shouldCollapse()) {
                     // Putting it back on the main thread, since we're touching views
                     mStatusBarWindow.post(() -> animateCollapsePanels(
                             CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index f7d0967..e32914f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -89,6 +89,7 @@
     private boolean mTouchCancelled;
     private boolean mTouchActive;
     private boolean mExpandAnimationRunning;
+    private boolean mExpandAnimationPending;
 
     public StatusBarWindowView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -268,7 +269,7 @@
                 || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
             setTouchActive(false);
         }
-        if (mTouchCancelled || mExpandAnimationRunning) {
+        if (mTouchCancelled || mExpandAnimationRunning || mExpandAnimationPending) {
             return false;
         }
         mFalsingManager.onTouchEvent(ev, getWidth(), getHeight());
@@ -393,6 +394,10 @@
         mExpandAnimationRunning = expandAnimationRunning;
     }
 
+    public void setExpandAnimationPending(boolean pending) {
+        mExpandAnimationPending = pending;
+    }
+
     public class LayoutParams extends FrameLayout.LayoutParams {
 
         public boolean ignoreRightInset;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 99202f4..bdf9b1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -84,6 +84,7 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -188,7 +189,8 @@
                 mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
                 mPowerManager, mNotificationPanelView, mBarService, mNotificationListener,
                 mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager,
-                mEntryManager, mScrimController, mFingerprintUnlockController);
+                mEntryManager, mScrimController, mFingerprintUnlockController,
+                mock(ActivityLaunchAnimator.class));
         mStatusBar.mContext = mContext;
         mStatusBar.mComponents = mContext.getComponents();
         mEntryManager.setUpForTest(mStatusBar, mStackScroller, mStatusBar, mHeadsUpManager,
@@ -593,7 +595,8 @@
                 VisualStabilityManager visualStabilityManager,
                 NotificationViewHierarchyManager viewHierarchyManager,
                 TestableNotificationEntryManager entryManager, ScrimController scrimController,
-                FingerprintUnlockController fingerprintUnlockController) {
+                FingerprintUnlockController fingerprintUnlockController,
+                ActivityLaunchAnimator launchAnimator) {
             mStatusBarKeyguardViewManager = man;
             mUnlockMethodCache = unlock;
             mKeyguardIndicationController = key;
@@ -610,6 +613,7 @@
             mEntryManager = entryManager;
             mScrimController = scrimController;
             mFingerprintUnlockController = fingerprintUnlockController;
+            mActivityLaunchAnimator = launchAnimator;
         }
 
         private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
index de99d71..c18ed73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
@@ -28,11 +28,12 @@
 
 import android.bluetooth.BluetoothProfile;
 import android.net.wifi.WifiManager;
-import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.media.MediaRouter;
 import android.telecom.TelecomManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.widget.TextView;
 
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -49,7 +50,8 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class OutputChooserDialogTest extends SysuiTestCase {
 
     OutputChooserDialog mDialog;
@@ -68,7 +70,6 @@
 
 
     @Before
-    @UiThreadTest
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
 
@@ -82,8 +83,12 @@
         mDialog = new OutputChooserDialog(getContext(), mRouter);
     }
 
+    @After
+    public void tearDown() throws Exception {
+        TestableLooper.get(this).processAllMessages();
+    }
+
     @Test
-    @UiThreadTest
     public void testClickMediaRouterItemConnectsMedia() {
         mDialog.show();
 
@@ -100,7 +105,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void testClickBtItemConnectsBt() {
         mDialog.show();
 
@@ -116,7 +120,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void testTitleNotInCall() {
         mDialog.show();
 
@@ -126,7 +129,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void testTitleInCall() {
         mDialog.setIsInCall(true);
         mDialog.show();
@@ -137,7 +139,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void testNoMediaScanIfInCall() {
         mDialog.setIsInCall(true);
         mDialog.onAttachedToWindow();
@@ -146,7 +147,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void testMediaScanIfNotInCall() {
         mDialog.setIsInCall(false);
         mDialog.onAttachedToWindow();
@@ -155,7 +155,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void testRegisterCallbacks() {
         mDialog.setIsInCall(false);
         mDialog.onAttachedToWindow();
@@ -166,7 +165,6 @@
     }
 
     @Test
-    @UiThreadTest
     public void testUnregisterCallbacks() {
         mDialog.setIsInCall(false);
         mDialog.onDetachedFromWindow();
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index 7cd3406..39fc019 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -701,7 +701,8 @@
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        if (args.length == 0) {  // Dump all users' data
+        if (args.length == 0 || (args.length == 1 && args[0].equals("-a"))) {
+            // Dump all users' data
             synchronized (mLock) {
                 pw.println("Current Text Services Manager state:");
                 pw.println("  Users:");
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index c1cda98..48b5a58 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -73,6 +73,17 @@
 
     private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = { 0, 30, 100, 30 };
 
+    private static final float GAMMA_SCALE_FACTOR_MINIMUM = 2.0f;
+    private static final float GAMMA_SCALE_FACTOR_LOW = 1.5f;
+    private static final float GAMMA_SCALE_FACTOR_HIGH = 0.5f;
+    private static final float GAMMA_SCALE_FACTOR_NONE = 1.0f;
+
+    private static final int MAX_AMPLITUDE_MINIMUM_INTENSITY = 168; // 2/3 * 255
+    private static final int MAX_AMPLITUDE_LOW_INTENSITY = 192; // 3/4 * 255
+
+    // If a vibration is playing for longer than 5s, it's probably not haptic feedback.
+    private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000;
+
     private final LinkedList<VibrationInfo> mPreviousVibrations;
     private final int mPreviousVibrationsLimit;
     private final boolean mAllowPriorityVibrationsInLowPowerMode;
@@ -89,6 +100,8 @@
     private final IBatteryStats mBatteryStatsService;
     private PowerManagerInternal mPowerManagerInternal;
     private InputManager mIm;
+    private Vibrator mVibrator;
+    private SettingsObserver mSettingObserver;
 
     private volatile VibrateThread mThread;
 
@@ -101,7 +114,8 @@
     private Vibration mCurrentVibration;
     private int mCurVibUid = -1;
     private boolean mLowPowerMode;
-    private SettingsObserver mSettingObserver;
+    private int mHapticFeedbackIntensity;
+    private int mNotificationIntensity;
 
     native static boolean vibratorExists();
     native static void vibratorInit();
@@ -112,27 +126,33 @@
     native static long vibratorPerformEffect(long effect, long strength);
 
     private class Vibration implements IBinder.DeathRecipient {
-        private final IBinder mToken;
-        private final VibrationEffect mEffect;
+        public final IBinder token;
         // Start time in CLOCK_BOOTTIME base.
-        private final long mStartTime;
+        public final long startTime;
         // Start time in unix epoch time. Only to be used for debugging purposes and to correlate
-        // with other system events, any duration calculations should be done use mStartTime so as
+        // with other system events, any duration calculations should be done use startTime so as
         // not to be affected by discontinuities created by RTC adjustments.
-        private final long mStartTimeDebug;
-        private final int mUsageHint;
-        private final int mUid;
-        private final String mOpPkg;
+        public final long startTimeDebug;
+        public final int usageHint;
+        public final int uid;
+        public final String opPkg;
+
+        // The actual effect to be played.
+        public VibrationEffect effect;
+        // The original effect that was requested. This is non-null only when the original effect
+        // differs from the effect that's being played. Typically these two things differ because
+        // the effect was scaled based on the users vibration intensity settings.
+        public VibrationEffect originalEffect;
 
         private Vibration(IBinder token, VibrationEffect effect,
                 int usageHint, int uid, String opPkg) {
-            mToken = token;
-            mEffect = effect;
-            mStartTime = SystemClock.elapsedRealtime();
-            mStartTimeDebug = System.currentTimeMillis();
-            mUsageHint = usageHint;
-            mUid = uid;
-            mOpPkg = opPkg;
+            this.token = token;
+            this.effect = effect;
+            this.startTime = SystemClock.elapsedRealtime();
+            this.startTimeDebug = System.currentTimeMillis();
+            this.usageHint = usageHint;
+            this.uid = uid;
+            this.opPkg = opPkg;
         }
 
         public void binderDied() {
@@ -143,43 +163,68 @@
             }
         }
 
-        public boolean hasLongerTimeout(long millis) {
-            // If the current effect is a one shot vibration that will end after the given timeout
-            // for the new one shot vibration, then just let the current vibration finish. All
-            // other effect types will get pre-empted.
-            if (mEffect instanceof VibrationEffect.OneShot) {
-                VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) mEffect;
-                return mStartTime + oneShot.getTiming() > SystemClock.uptimeMillis() + millis;
-            }
-            return false;
+        public boolean hasTimeoutLongerThan(long millis) {
+            final long duration = effect.getDuration();
+            return duration >= 0 && duration > millis;
         }
 
-        public boolean isSystemHapticFeedback() {
-            boolean repeating = false;
-            if (mEffect instanceof VibrationEffect.Waveform) {
-                VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) mEffect;
-                repeating = (waveform.getRepeatIndex() < 0);
+        public boolean isHapticFeedback() {
+            if (effect instanceof VibrationEffect.Prebaked) {
+                VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
+                switch (prebaked.getId()) {
+                    case VibrationEffect.EFFECT_CLICK:
+                    case VibrationEffect.EFFECT_DOUBLE_CLICK:
+                    case VibrationEffect.EFFECT_TICK:
+                        return true;
+                    default:
+                        Slog.w(TAG, "Unknown prebaked vibration effect, "
+                                + "assuming it isn't haptic feedback.");
+                        return false;
+                }
             }
-            return (mUid == Process.SYSTEM_UID || mUid == 0 || SYSTEM_UI_PACKAGE.equals(mOpPkg))
-                    && !repeating;
+            final long duration = effect.getDuration();
+            return duration >= 0 && duration < MAX_HAPTIC_FEEDBACK_DURATION;
+        }
+
+        public boolean isNotification() {
+            switch (usageHint) {
+                case AudioAttributes.USAGE_NOTIFICATION:
+                case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+                case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+                case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        public boolean isRingtone() {
+            return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
+        }
+
+        public boolean isFromSystem() {
+            return uid == Process.SYSTEM_UID || uid == 0 || SYSTEM_UI_PACKAGE.equals(opPkg);
         }
 
         public VibrationInfo toInfo() {
-            return new VibrationInfo(mStartTimeDebug, mEffect, mUsageHint, mUid, mOpPkg);
+            return new VibrationInfo(
+                    startTimeDebug, effect, originalEffect, usageHint, uid, opPkg);
         }
     }
 
     private static class VibrationInfo {
         private final long mStartTimeDebug;
         private final VibrationEffect mEffect;
+        private final VibrationEffect mOriginalEffect;
         private final int mUsageHint;
         private final int mUid;
         private final String mOpPkg;
 
         public VibrationInfo(long startTimeDebug, VibrationEffect effect,
-                int usageHint, int uid, String opPkg) {
+                VibrationEffect originalEffect, int usageHint, int uid, String opPkg) {
             mStartTimeDebug = startTimeDebug;
             mEffect = effect;
+            mOriginalEffect = originalEffect;
             mUsageHint = usageHint;
             mUid = uid;
             mOpPkg = opPkg;
@@ -192,6 +237,8 @@
                     .append(DateFormat.getDateTimeInstance().format(new Date(mStartTimeDebug)))
                     .append(", effect: ")
                     .append(mEffect)
+                    .append(", originalEffect: ")
+                    .append(mOriginalEffect)
                     .append(", usageHint: ")
                     .append(mUsageHint)
                     .append(", uid: ")
@@ -245,6 +292,7 @@
         VibrationEffect tickEffect = createEffect(tickEffectTimings);
 
         mFallbackEffects = new VibrationEffect[] { clickEffect, doubleClickEffect, tickEffect };
+
     }
 
     private static VibrationEffect createEffect(long[] timings) {
@@ -259,6 +307,7 @@
 
     public void systemReady() {
         mIm = mContext.getSystemService(InputManager.class);
+        mVibrator = mContext.getSystemService(Vibrator.class);
         mSettingObserver = new SettingsObserver(mH);
 
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
@@ -279,6 +328,14 @@
                 Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
                 true, mSettingObserver, UserHandle.USER_ALL);
 
+        mContext.getContentResolver().registerContentObserver(
+                Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY),
+                true, mSettingObserver, UserHandle.USER_ALL);
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY),
+                true, mSettingObserver, UserHandle.USER_ALL);
+
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
@@ -380,11 +437,11 @@
         // then just let the current one finish.
         if (effect instanceof VibrationEffect.OneShot
                 && mCurrentVibration != null
-                && mCurrentVibration.mEffect instanceof VibrationEffect.OneShot) {
+                && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
             VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
             VibrationEffect.OneShot currentOneShot =
-                    (VibrationEffect.OneShot) mCurrentVibration.mEffect;
-            if (mCurrentVibration.hasLongerTimeout(newOneShot.getTiming())
+                    (VibrationEffect.OneShot) mCurrentVibration.effect;
+            if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
                     && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
                 if (DEBUG) {
                     Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration");
@@ -398,7 +455,7 @@
         // to grab the attention of the user, like ringtones and alarms, in favor of one-shot
         // vibrations that are likely quite short.
         if (!isRepeatingVibration(effect)
-                && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.mEffect)) {
+                && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.effect)) {
             if (DEBUG) {
                 Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
             }
@@ -421,13 +478,7 @@
     }
 
     private static boolean isRepeatingVibration(VibrationEffect effect) {
-        if (effect instanceof VibrationEffect.Waveform) {
-            final VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
-            if (waveform.getRepeatIndex() >= 0) {
-                return true;
-            }
-        }
-        return false;
+        return effect.getDuration() == Long.MAX_VALUE;
     }
 
     private void addToPreviousVibrationsLocked(Vibration vib) {
@@ -444,7 +495,7 @@
                 "cancelVibrate");
 
         synchronized (mLock) {
-            if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
+            if (mCurrentVibration != null && mCurrentVibration.token == token) {
                 if (DEBUG) {
                     Slog.d(TAG, "Canceling vibration.");
                 }
@@ -488,15 +539,16 @@
     }
 
     private void startVibrationLocked(final Vibration vib) {
-        if (!isAllowedToVibrate(vib)) {
-            if (DEBUG) {
-                Slog.e(TAG, "Vibrate ignored, low power mode");
-            }
+        if (!isAllowedToVibrateLocked(vib)) {
             return;
         }
 
-        if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE &&
-                !shouldVibrateForRingtone()) {
+        final int intensity = getCurrentIntensityLocked(vib);
+        if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+            return;
+        }
+
+        if (vib.isRingtone() && !shouldVibrateForRingtone()) {
             if (DEBUG) {
                 Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
             }
@@ -508,26 +560,27 @@
             if (mode == AppOpsManager.MODE_ERRORED) {
                 // We might be getting calls from within system_server, so we don't actually want
                 // to throw a SecurityException here.
-                Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
+                Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
             }
             return;
         }
+        applyVibrationIntensityScalingLocked(vib, intensity);
         startVibrationInnerLocked(vib);
     }
 
     private void startVibrationInnerLocked(Vibration vib) {
         mCurrentVibration = vib;
-        if (vib.mEffect instanceof VibrationEffect.OneShot) {
-            VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.mEffect;
-            doVibratorOn(oneShot.getTiming(), oneShot.getAmplitude(), vib.mUid, vib.mUsageHint);
-            mH.postDelayed(mVibrationEndRunnable, oneShot.getTiming());
-        } else if (vib.mEffect instanceof VibrationEffect.Waveform) {
+        if (vib.effect instanceof VibrationEffect.OneShot) {
+            VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
+            doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.usageHint);
+            mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
+        } else if (vib.effect instanceof VibrationEffect.Waveform) {
             // mThread better be null here. doCancelVibrate should always be
             // called before startNextVibrationLocked or startVibrationLocked.
-            VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.mEffect;
-            mThread = new VibrateThread(waveform, vib.mUid, vib.mUsageHint);
+            VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
+            mThread = new VibrateThread(waveform, vib.uid, vib.usageHint);
             mThread.start();
-        } else if (vib.mEffect instanceof VibrationEffect.Prebaked) {
+        } else if (vib.effect instanceof VibrationEffect.Prebaked) {
             long timeout = doVibratorPrebakedEffectLocked(vib);
             if (timeout > 0) {
                 mH.postDelayed(mVibrationEndRunnable, timeout);
@@ -537,28 +590,91 @@
         }
     }
 
-    private boolean isAllowedToVibrate(Vibration vib) {
+    private boolean isAllowedToVibrateLocked(Vibration vib) {
         if (!mLowPowerMode) {
             return true;
         }
-        if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
+
+        if (vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
             return true;
         }
-        if (!mAllowPriorityVibrationsInLowPowerMode) {
-            return false;
-        }
-        if (vib.mUsageHint == AudioAttributes.USAGE_ALARM ||
-            vib.mUsageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY ||
-            vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
 
+        if (vib.usageHint == AudioAttributes.USAGE_ALARM ||
+                vib.usageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY ||
+                vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
             return true;
         }
 
         return false;
     }
 
+    private int getCurrentIntensityLocked(Vibration vib) {
+        if (vib.isNotification() || vib.isRingtone()){
+            return mNotificationIntensity;
+        } else if (vib.isHapticFeedback()) {
+            return mHapticFeedbackIntensity;
+        } else {
+            return Vibrator.VIBRATION_INTENSITY_MEDIUM;
+        }
+    }
+
+    /**
+     * Scale the vibration effect by the intensity as appropriate based its intent.
+     */
+    private void applyVibrationIntensityScalingLocked(Vibration vib, int intensity) {
+        if (vib.effect instanceof VibrationEffect.Prebaked) {
+            // Prebaked effects are always just a direct translation from intensity to
+            // EffectStrength.
+            VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked)vib.effect;
+            prebaked.setEffectStrength(intensityToEffectStrength(intensity));
+            return;
+        }
+
+        final float gamma;
+        final int maxAmplitude;
+        if (vib.isNotification() || vib.isRingtone()) {
+            if (intensity == Vibrator.VIBRATION_INTENSITY_LOW) {
+                gamma = GAMMA_SCALE_FACTOR_MINIMUM;
+                maxAmplitude = MAX_AMPLITUDE_MINIMUM_INTENSITY;
+            } else if (intensity == Vibrator.VIBRATION_INTENSITY_MEDIUM) {
+                gamma = GAMMA_SCALE_FACTOR_LOW;
+                maxAmplitude = MAX_AMPLITUDE_LOW_INTENSITY;
+            } else {
+                gamma = GAMMA_SCALE_FACTOR_NONE;
+                maxAmplitude = VibrationEffect.MAX_AMPLITUDE;
+            }
+        } else {
+            if (intensity == Vibrator.VIBRATION_INTENSITY_LOW) {
+                gamma = GAMMA_SCALE_FACTOR_LOW;
+                maxAmplitude = MAX_AMPLITUDE_LOW_INTENSITY;
+            } else if (intensity == Vibrator.VIBRATION_INTENSITY_HIGH) {
+                gamma = GAMMA_SCALE_FACTOR_HIGH;
+                maxAmplitude = VibrationEffect.MAX_AMPLITUDE;
+            } else {
+                gamma = GAMMA_SCALE_FACTOR_NONE;
+                maxAmplitude = VibrationEffect.MAX_AMPLITUDE;
+            }
+        }
+
+        VibrationEffect scaledEffect = null;
+        if (vib.effect instanceof VibrationEffect.OneShot) {
+            VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
+            scaledEffect = oneShot.scale(gamma, maxAmplitude);
+        } else if (vib.effect instanceof VibrationEffect.Waveform) {
+            VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
+            scaledEffect = waveform.scale(gamma, maxAmplitude);
+        } else {
+            Slog.w(TAG, "Unable to apply intensity scaling, unknown VibrationEffect type");
+        }
+
+        if (scaledEffect != null) {
+            vib.originalEffect = vib.effect;
+            vib.effect = scaledEffect;
+        }
+    }
+
     private boolean shouldVibrateForRingtone() {
-        AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        AudioManager audioManager = mContext.getSystemService(AudioManager.class);
         int ringerMode = audioManager.getRingerModeInternal();
         // "Also vibrate for calls" Setting in Sound
         if (Settings.System.getInt(
@@ -573,10 +689,10 @@
         int mode;
         try {
             mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
-                    vib.mUsageHint, vib.mUid, vib.mOpPkg);
+                    vib.usageHint, vib.uid, vib.opPkg);
             if (mode == AppOpsManager.MODE_ALLOWED) {
                 mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
-                    AppOpsManager.OP_VIBRATE, vib.mUid, vib.mOpPkg);
+                    AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to get appop mode for vibration!", e);
@@ -589,8 +705,8 @@
         if (mCurrentVibration != null) {
             try {
                 mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
-                        AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
-                        mCurrentVibration.mOpPkg);
+                        AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
+                        mCurrentVibration.opPkg);
             } catch (RemoteException e) { }
             unlinkVibration(mCurrentVibration);
             mCurrentVibration = null;
@@ -600,9 +716,9 @@
     private void linkVibration(Vibration vib) {
         // Only link against waveforms since they potentially don't have a finish if
         // they're repeating. Let other effects just play out until they're done.
-        if (vib.mEffect instanceof VibrationEffect.Waveform) {
+        if (vib.effect instanceof VibrationEffect.Waveform) {
             try {
-                vib.mToken.linkToDeath(vib, 0);
+                vib.token.linkToDeath(vib, 0);
             } catch (RemoteException e) {
                 return;
             }
@@ -610,8 +726,8 @@
     }
 
     private void unlinkVibration(Vibration vib) {
-        if (vib.mEffect instanceof VibrationEffect.Waveform) {
-            vib.mToken.unlinkToDeath(vib, 0);
+        if (vib.effect instanceof VibrationEffect.Waveform) {
+            vib.token.unlinkToDeath(vib, 0);
         }
     }
 
@@ -619,6 +735,7 @@
         synchronized (mLock) {
             boolean devicesUpdated = updateInputDeviceVibratorsLocked();
             boolean lowPowerModeUpdated = updateLowPowerModeLocked();
+            updateVibrationIntensityLocked();
 
             if (devicesUpdated || lowPowerModeUpdated) {
                 // If the state changes out from under us then just reset.
@@ -678,6 +795,15 @@
         return false;
     }
 
+    private void updateVibrationIntensityLocked() {
+        mHapticFeedbackIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+                mVibrator.getDefaultHapticFeedbackIntensity(), UserHandle.USER_CURRENT);
+        mNotificationIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                mVibrator.getDefaultNotificationVibrationIntensity(), UserHandle.USER_CURRENT);
+    }
+
     @Override
     public void onInputDeviceAdded(int deviceId) {
         updateVibrators();
@@ -755,28 +881,32 @@
     }
 
     private long doVibratorPrebakedEffectLocked(Vibration vib) {
+        final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
+        final boolean usingInputDeviceVibrators;
         synchronized (mInputDeviceVibrators) {
-            VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.mEffect;
-            // Input devices don't support prebaked effect, so skip trying it with them.
-            if (mInputDeviceVibrators.isEmpty()) {
-                long timeout = vibratorPerformEffect(prebaked.getId(), EffectStrength.MEDIUM);
-                if (timeout > 0) {
-                    noteVibratorOnLocked(vib.mUid, timeout);
-                    return timeout;
-                }
-            }
-            if (!prebaked.shouldFallback()) {
-                return 0;
-            }
-            VibrationEffect effect = getFallbackEffect(prebaked.getId());
-            if (effect == null) {
-                Slog.w(TAG, "Failed to play prebaked effect, no fallback");
-                return 0;
-            }
-            Vibration fallbackVib =
-                    new Vibration(vib.mToken, effect, vib.mUsageHint, vib.mUid, vib.mOpPkg);
-            startVibrationInnerLocked(fallbackVib);
+            usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
         }
+        // Input devices don't support prebaked effect, so skip trying it with them.
+        if (!usingInputDeviceVibrators) {
+            long timeout = vibratorPerformEffect(prebaked.getId(), prebaked.getEffectStrength());
+            if (timeout > 0) {
+                noteVibratorOnLocked(vib.uid, timeout);
+                return timeout;
+            }
+        }
+        if (!prebaked.shouldFallback()) {
+            return 0;
+        }
+        VibrationEffect effect = getFallbackEffect(prebaked.getId());
+        if (effect == null) {
+            Slog.w(TAG, "Failed to play prebaked effect, no fallback");
+            return 0;
+        }
+        Vibration fallbackVib =
+                new Vibration(vib.token, effect, vib.usageHint, vib.uid, vib.opPkg);
+        final int intensity = getCurrentIntensityLocked(fallbackVib);
+        applyVibrationIntensityScalingLocked(fallbackVib, intensity);
+        startVibrationInnerLocked(fallbackVib);
         return 0;
     }
 
@@ -787,6 +917,25 @@
         return mFallbackEffects[effectId];
     }
 
+    /**
+     * Return the current desired effect strength.
+     *
+     * If the returned value is &lt; 0 then the vibration shouldn't be played at all.
+     */
+    private static int intensityToEffectStrength(int intensity) {
+        switch (intensity) {
+            case Vibrator.VIBRATION_INTENSITY_LOW:
+                return EffectStrength.LIGHT;
+            case Vibrator.VIBRATION_INTENSITY_MEDIUM:
+                return EffectStrength.MEDIUM;
+            case Vibrator.VIBRATION_INTENSITY_HIGH:
+                return EffectStrength.STRONG;
+            default:
+                Slog.w(TAG, "Got unexpected vibration intensity: " + intensity);
+                return EffectStrength.STRONG;
+        }
+    }
+
     private void noteVibratorOnLocked(int uid, long millis) {
         try {
             mBatteryStatsService.noteVibratorOn(uid, millis);
@@ -945,7 +1094,8 @@
                     // haptic feedback as part of the transition.  So we don't cancel
                     // system vibrations.
                     if (mCurrentVibration != null
-                            && !mCurrentVibration.isSystemHapticFeedback()) {
+                            && !(mCurrentVibration.isHapticFeedback()
+                                && mCurrentVibration.isFromSystem())) {
                         doCancelVibrateLocked();
                     }
                 }
@@ -957,10 +1107,21 @@
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        pw.println("Previous vibrations:");
+        pw.println("Vibrator Service:");
         synchronized (mLock) {
+            pw.print("  mCurrentVibration=");
+            if (mCurrentVibration != null) {
+                pw.println(mCurrentVibration.toInfo().toString());
+            } else {
+                pw.println("null");
+            }
+            pw.println("  mLowPowerMode=" + mLowPowerMode);
+            pw.println("  mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
+            pw.println("  mNotificationIntensity=" + mNotificationIntensity);
+            pw.println("");
+            pw.println("  Previous vibrations:");
             for (VibrationInfo info : mPreviousVibrations) {
-                pw.print("  ");
+                pw.print("    ");
                 pw.println(info.toString());
             }
         }
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index db21ef1..220014f 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -50,7 +51,9 @@
 import android.view.Display;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.wm.ConfigurationContainer;
+import com.android.server.wm.DisplayWindowController;
 
+import com.android.server.wm.WindowContainerListener;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
@@ -58,7 +61,8 @@
  * Exactly one of these classes per Display in the system. Capable of holding zero or more
  * attached {@link ActivityStack}s.
  */
-class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
+class ActivityDisplay extends ConfigurationContainer<ActivityStack>
+        implements WindowContainerListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_AM;
     private static final String TAG_STACK = TAG + POSTFIX_STACK;
 
@@ -100,6 +104,8 @@
     // Used in updating the display size
     private Point mTmpDisplaySize = new Point();
 
+    private DisplayWindowController mWindowContainerController;
+
     ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
         mSupervisor = supervisor;
         mDisplayId = displayId;
@@ -108,10 +114,15 @@
             throw new IllegalStateException("Display does not exist displayId=" + displayId);
         }
         mDisplay = display;
+        mWindowContainerController = createWindowContainerController();
 
         updateBounds();
     }
 
+    protected DisplayWindowController createWindowContainerController() {
+        return new DisplayWindowController(mDisplayId, this);
+    }
+
     void updateBounds() {
         mDisplay.getSize(mTmpDisplaySize);
         setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
@@ -148,7 +159,10 @@
 
     private void positionChildAt(ActivityStack stack, int position) {
         mStacks.remove(stack);
-        mStacks.add(getTopInsertPosition(stack, position), stack);
+        final int insertPosition = getTopInsertPosition(stack, position);
+        mStacks.add(insertPosition, stack);
+        mWindowContainerController.positionChildAt(stack.getWindowContainerController(),
+                insertPosition);
     }
 
     private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
@@ -410,7 +424,7 @@
             }
         } finally {
             final ActivityStack topFullscreenStack =
-                    getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+                    getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
             if (topFullscreenStack != null && mHomeStack != null && !isTopStack(mHomeStack)) {
                 // Whenever split-screen is dismissed we want the home stack directly behind the
                 // current top fullscreen stack so it shows up when the top stack is finished.
@@ -566,6 +580,16 @@
         return false;
     }
 
+    ActivityStack getTopStackInWindowingMode(int windowingMode) {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final ActivityStack current = mStacks.get(i);
+            if (windowingMode == current.getWindowingMode()) {
+                return current;
+            }
+        }
+        return null;
+    }
+
     int getIndexOf(ActivityStack stack) {
         return mStacks.indexOf(stack);
     }
@@ -651,6 +675,64 @@
                 && (mSupervisor.mService.mRunningVoice == null);
     }
 
+    /**
+     * @return the stack currently above the home stack.  Can be null if there is no home stack, or
+     *         the home stack is already on top.
+     */
+    ActivityStack getStackAboveHome() {
+        if (mHomeStack == null) {
+            // Skip if there is no home stack
+            return null;
+        }
+
+        final int stackIndex = mStacks.indexOf(mHomeStack) + 1;
+        return (stackIndex < mStacks.size()) ? mStacks.get(stackIndex) : null;
+    }
+
+    /**
+     * Adjusts the home stack behind the last visible stack in the display if necessary. Generally
+     * used in conjunction with {@link #moveHomeStackBehindStack}.
+     */
+    void moveHomeStackBehindBottomMostVisibleStack() {
+        if (mHomeStack == null) {
+            // Skip if there is no home stack
+            return;
+        }
+
+        // Move the home stack to the bottom to not affect the following visibility checks
+        positionChildAtBottom(mHomeStack);
+
+        // Find the next position where the homes stack should be placed
+        final int numStacks = mStacks.size();
+        for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
+            final ActivityStack stack = mStacks.get(stackNdx);
+            if (stack == mHomeStack) {
+                continue;
+            }
+            final int winMode = stack.getWindowingMode();
+            final boolean isValidWindowingMode = winMode == WINDOWING_MODE_FULLSCREEN ||
+                    winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+            if (stack.shouldBeVisible(null) && isValidWindowingMode) {
+                // Move the home stack to behind this stack
+                positionChildAt(mHomeStack, Math.max(0, stackNdx - 1));
+                break;
+            }
+        }
+    }
+
+    /**
+     * Moves the home stack behind the given {@param stack} if possible. If {@param stack} is not
+     * currently in the display, then then the home stack is moved to the back. Generally used in
+     * conjunction with {@link #moveHomeStackBehindBottomMostVisibleStack}.
+     */
+    void moveHomeStackBehindStack(ActivityStack behindStack) {
+        if (behindStack == null) {
+            return;
+        }
+
+        positionChildAt(mHomeStack, Math.max(0, mStacks.indexOf(behindStack) - 1));
+    }
+
     boolean isSleeping() {
         return mSleeping;
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 364d5d5..796290f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -39,6 +39,7 @@
 import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
 import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
 import static android.app.AppOpsManager.OP_NONE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -46,6 +47,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -228,6 +230,7 @@
 import android.app.BroadcastOptions;
 import android.app.ContentProviderHolder;
 import android.app.Dialog;
+import android.app.GrantedUriPermission;
 import android.app.IActivityController;
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
@@ -376,6 +379,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
 import android.view.Gravity;
+import android.view.IRecentsAnimationRunner;
 import android.view.LayoutInflater;
 import android.view.RemoteAnimationDefinition;
 import android.view.View;
@@ -449,6 +453,7 @@
 import com.android.server.utils.PriorityDump;
 import com.android.server.vr.VrManagerInternal;
 import com.android.server.wm.PinnedStackWindowController;
+import com.android.server.wm.RecentsAnimationController;
 import com.android.server.wm.WindowManagerService;
 
 import dalvik.system.VMRuntime;
@@ -1633,6 +1638,14 @@
     String mProfileApp = null;
     ProcessRecord mProfileProc = null;
     ProfilerInfo mProfilerInfo = null;
+
+    /**
+     * Stores a map of process name -> agent string. When a process is started and mAgentAppMap
+     * is not null, this map is checked and the mapped agent installed during bind-time. Note:
+     * A non-null agent in mProfileInfo overrides this.
+     */
+    private @Nullable Map<String, String> mAppAgentMap = null;
+
     int mProfileType = 0;
     final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
     String mMemWatchDumpProcName;
@@ -5095,23 +5108,16 @@
     }
 
     @Override
-    public int startRecentsActivity(IAssistDataReceiver assistDataReceiver, Bundle options,
-            Bundle activityOptions, int userId) {
-        if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) {
-            String msg = "Permission Denial: startRecentsActivity() from pid="
-                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
-                    + " not recent tasks package";
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-
-        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
-        final int recentsUid = mRecentTasks.getRecentsComponentUid();
-        final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
-        final String recentsPackage = recentsComponent.getPackageName();
+    public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
+                IRecentsAnimationRunner recentsAnimationRunner) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
+                final int recentsUid = mRecentTasks.getRecentsComponentUid();
+                final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
+                final String recentsPackage = recentsComponent.getPackageName();
+
                 // If provided, kick off the request for the assist data in the background before
                 // starting the activity
                 if (assistDataReceiver != null) {
@@ -5128,17 +5134,24 @@
                             recentsUid, recentsPackage);
                 }
 
-                final Intent intent = new Intent();
-                intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
-                intent.setComponent(recentsComponent);
-                intent.putExtras(options);
+                // Start a new recents animation
+                final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
+                        mActivityStartController, mWindowManager, mUserController);
+                anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
+                        recentsUid);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
 
-                return mActivityStartController.obtainStarter(intent, "startRecentsActivity")
-                        .setCallingUid(recentsUid)
-                        .setCallingPackage(recentsPackage)
-                        .setActivityOptions(safeOptions)
-                        .setMayWait(userId)
-                        .execute();
+    @Override
+    public void cancelRecentsAnimation() {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                mWindowManager.cancelRecentsAnimation();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -7452,25 +7465,6 @@
                 }
             }
 
-            ProfilerInfo profilerInfo = null;
-            String preBindAgent = null;
-            if (mProfileApp != null && mProfileApp.equals(processName)) {
-                mProfileProc = app;
-                if (mProfilerInfo != null) {
-                    // Send a profiler info object to the app if either a file is given, or
-                    // an agent should be loaded at bind-time.
-                    boolean needsInfo = mProfilerInfo.profileFile != null
-                            || mProfilerInfo.attachAgentDuringBind;
-                    profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
-                    if (!mProfilerInfo.attachAgentDuringBind) {
-                        preBindAgent = mProfilerInfo.agent;
-                    }
-                }
-            } else if (app.instr != null && app.instr.mProfileFile != null) {
-                profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
-                        null, false);
-            }
-
             boolean enableTrackAllocation = false;
             if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
                 enableTrackAllocation = true;
@@ -7495,6 +7489,39 @@
             ApplicationInfo appInfo = app.instr != null ? app.instr.mTargetInfo : app.info;
             app.compat = compatibilityInfoForPackageLocked(appInfo);
 
+            ProfilerInfo profilerInfo = null;
+            String preBindAgent = null;
+            if (mProfileApp != null && mProfileApp.equals(processName)) {
+                mProfileProc = app;
+                if (mProfilerInfo != null) {
+                    // Send a profiler info object to the app if either a file is given, or
+                    // an agent should be loaded at bind-time.
+                    boolean needsInfo = mProfilerInfo.profileFile != null
+                            || mProfilerInfo.attachAgentDuringBind;
+                    profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
+                    if (mProfilerInfo.agent != null) {
+                        preBindAgent = mProfilerInfo.agent;
+                    }
+                }
+            } else if (app.instr != null && app.instr.mProfileFile != null) {
+                profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
+                        null, false);
+            }
+            if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
+                // We need to do a debuggable check here. See setAgentApp for why the check is
+                // postponed to here.
+                if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+                    String agent = mAppAgentMap.get(processName);
+                    // Do not overwrite already requested agent.
+                    if (profilerInfo == null) {
+                        profilerInfo = new ProfilerInfo(null, null, 0, false, false,
+                                mAppAgentMap.get(processName), true);
+                    } else if (profilerInfo.agent == null) {
+                        profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
+                    }
+                }
+            }
+
             if (profilerInfo != null && profilerInfo.profileFd != null) {
                 profilerInfo.profileFd = profilerInfo.profileFd.dup();
             }
@@ -10204,7 +10231,8 @@
                 if (perms == null) {
                     Slog.w(TAG, "No permission grants found for " + packageName);
                 } else {
-                    for (UriPermission perm : perms.values()) {
+                    for (int j = 0; j < perms.size(); j++) {
+                        final UriPermission perm = perms.valueAt(j);
                         if (packageName.equals(perm.targetPkg) && perm.persistedModeFlags != 0) {
                             result.add(perm.buildPersistedPublicApiObject());
                         }
@@ -10215,7 +10243,8 @@
                 for (int i = 0; i < size; i++) {
                     final ArrayMap<GrantUri, UriPermission> perms =
                             mGrantedUriPermissions.valueAt(i);
-                    for (UriPermission perm : perms.values()) {
+                    for (int j = 0; j < perms.size(); j++) {
+                        final UriPermission perm = perms.valueAt(j);
                         if (packageName.equals(perm.sourcePkg) && perm.persistedModeFlags != 0) {
                             result.add(perm.buildPersistedPublicApiObject());
                         }
@@ -10227,25 +10256,27 @@
     }
 
     @Override
-    public ParceledListSlice<android.content.UriPermission> getGrantedUriPermissions(
-            String packageName, int userId) {
+    public ParceledListSlice<GrantedUriPermission> getGrantedUriPermissions(
+            @Nullable String packageName, int userId) {
         enforceCallingPermission(android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS,
                 "getGrantedUriPermissions");
 
-        final ArrayList<android.content.UriPermission> result = Lists.newArrayList();
+        final List<GrantedUriPermission> result = new ArrayList<>();
         synchronized (this) {
             final int size = mGrantedUriPermissions.size();
             for (int i = 0; i < size; i++) {
                 final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
-                for (UriPermission perm : perms.values()) {
-                    if (packageName.equals(perm.targetPkg) && perm.targetUserId == userId
+                for (int j = 0; j < perms.size(); j++) {
+                    final UriPermission perm = perms.valueAt(j);
+                    if ((packageName == null || packageName.equals(perm.targetPkg))
+                            && perm.targetUserId == userId
                             && perm.persistedModeFlags != 0) {
-                        result.add(perm.buildPersistedPublicApiObject());
+                        result.add(perm.buildGrantedUriPermission());
                     }
                 }
             }
         }
-        return new ParceledListSlice<android.content.UriPermission>(result);
+        return new ParceledListSlice<>(result);
     }
 
     @Override
@@ -11037,8 +11068,20 @@
                 }
 
                 if (toTop) {
+                    // Caller wants the current split-screen primary stack to be the top stack after
+                    // it goes fullscreen, so move it to the front.
                     stack.moveToFront("dismissSplitScreenMode");
+                } else if (mStackSupervisor.isFocusedStack(stack)) {
+                    // In this case the current split-screen primary stack shouldn't be the top
+                    // stack after it goes fullscreen, but it current has focus, so we move the
+                    // focus to the top-most split-screen secondary stack next to it.
+                    final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
+                            WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                    if (otherStack != null) {
+                        otherStack.moveToFront("dismissSplitScreenMode_other");
+                    }
                 }
+
                 stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
             }
         } finally {
@@ -13147,6 +13190,52 @@
         }
     }
 
+    /**
+     * Set or remove an agent to be run whenever an app with the given process name starts.
+     *
+     * This method will not check whether the given process name matches a debuggable app. That
+     * would require scanning all current packages, and a rescan when new packages are installed
+     * or updated.
+     *
+     * Instead, do the check when an application is started and matched to a stored agent.
+     *
+     * @param packageName the process name of the app.
+     * @param agent the agent string to be used, or null to remove any previously set agent.
+     */
+    @Override
+    public void setAgentApp(@NonNull String packageName, @Nullable String agent) {
+        synchronized (this) {
+            // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+            // its own permission.
+            if (checkCallingPermission(
+                    android.Manifest.permission.SET_ACTIVITY_WATCHER) !=
+                        PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException(
+                        "Requires permission " + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+            }
+
+            if (agent == null) {
+                if (mAppAgentMap != null) {
+                    mAppAgentMap.remove(packageName);
+                    if (mAppAgentMap.isEmpty()) {
+                        mAppAgentMap = null;
+                    }
+                }
+            } else {
+                if (mAppAgentMap == null) {
+                    mAppAgentMap = new HashMap<>();
+                }
+                if (mAppAgentMap.size() >= 100) {
+                    // Limit the size of the map, to avoid OOMEs.
+                    Slog.e(TAG, "App agent map has too many entries, cannot add " + packageName
+                            + "/" + agent);
+                    return;
+                }
+                mAppAgentMap.put(packageName, agent);
+            }
+        }
+    }
+
     void setTrackAllocationApp(ApplicationInfo app, String processName) {
         synchronized (this) {
             boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
@@ -25537,11 +25626,14 @@
             // "= 0" is needed because otherwise catch(RemoteException) would make it look like
             // packageUid may not be initialized.
             int packageUid = 0;
+            final long ident = Binder.clearCallingIdentity();
             try {
                 packageUid = AppGlobals.getPackageManager().getPackageUid(
                         packageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
             } catch (RemoteException e) {
                 // Shouldn't happen.
+            } finally {
+                Binder.restoreCallingIdentity(ident);
             }
 
             synchronized (ActivityManagerService.this) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 1240f5e..f0c90e0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -160,6 +160,8 @@
                     return runDumpHeap(pw);
                 case "set-debug-app":
                     return runSetDebugApp(pw);
+                case "set-agent-app":
+                    return runSetAgentApp(pw);
                 case "clear-debug-app":
                     return runClearDebugApp(pw);
                 case "set-watch-heap":
@@ -873,6 +875,13 @@
         return 0;
     }
 
+    int runSetAgentApp(PrintWriter pw) throws RemoteException {
+        String pkg = getNextArgRequired();
+        String agent = getNextArg();
+        mInterface.setAgentApp(pkg, agent);
+        return 0;
+    }
+
     int runClearDebugApp(PrintWriter pw) throws RemoteException {
         mInterface.setDebugApp(null, false, true);
         return 0;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 9d06b0d..4485590 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -642,7 +642,10 @@
         // TODO: We should probably resolve the windowing mode for the stack on the new display here
         // so that it end up in a compatible mode in the new display. e.g. split-screen secondary.
         removeFromDisplay();
+        // Reparent the window container before we try to update the position when adding it to
+        // the new display below
         mTmpRect2.setEmpty();
+        mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop);
         postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
         adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */);
         mStackSupervisor.resumeFocusedStackTopActivityLocked();
@@ -650,7 +653,6 @@
         // windows that are no longer visible.
         mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
                 !PRESERVE_WINDOWS);
-        mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop);
     }
 
     /**
@@ -994,12 +996,6 @@
             insertTaskAtTop(task, null);
             return;
         }
-
-        task = topTask();
-        if (task != null) {
-            mWindowContainerController.positionChildAtTop(task.getWindowContainerController(),
-                    true /* includingParents */);
-        }
     }
 
     /**
@@ -1024,12 +1020,6 @@
         if (task != null) {
             insertTaskAtBottom(task);
             return;
-        } else {
-            task = bottomTask();
-            if (task != null) {
-                mWindowContainerController.positionChildAtBottom(
-                        task.getWindowContainerController(), true /* includingParents */);
-            }
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index bfb563f..510a3fa 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -297,6 +297,7 @@
     private RunningTasks mRunningTasks;
 
     final ActivityStackSupervisorHandler mHandler;
+    final Looper mLooper;
 
     /** Short cut */
     WindowManagerService mWindowManager;
@@ -581,6 +582,7 @@
 
     public ActivityStackSupervisor(ActivityManagerService service, Looper looper) {
         mService = service;
+        mLooper = looper;
         mHandler = new ActivityStackSupervisorHandler(looper);
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 4dc30dd..8fd754a 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -302,6 +302,7 @@
         SafeActivityOptions activityOptions;
         boolean ignoreTargetSecurity;
         boolean componentSpecified;
+        boolean avoidMoveToFront;
         ActivityRecord[] outActivity;
         TaskRecord inTask;
         String reason;
@@ -356,6 +357,7 @@
             userId = 0;
             waitResult = null;
             mayWait = false;
+            avoidMoveToFront = false;
         }
 
         /**
@@ -390,6 +392,7 @@
             userId = request.userId;
             waitResult = request.waitResult;
             mayWait = request.mayWait;
+            avoidMoveToFront = request.avoidMoveToFront;
         }
     }
 
@@ -1485,19 +1488,23 @@
             mDoResume = false;
         }
 
-        if (mOptions != null && mOptions.getLaunchTaskId() != -1
-                && mOptions.getTaskOverlay()) {
-            r.mTaskOverlay = true;
-            if (!mOptions.canTaskOverlayResume()) {
-                final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
-                final ActivityRecord top = task != null ? task.getTopActivity() : null;
-                if (top != null && top.state != RESUMED) {
+        if (mOptions != null) {
+            if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
+                r.mTaskOverlay = true;
+                if (!mOptions.canTaskOverlayResume()) {
+                    final TaskRecord task = mSupervisor.anyTaskForIdLocked(
+                            mOptions.getLaunchTaskId());
+                    final ActivityRecord top = task != null ? task.getTopActivity() : null;
+                    if (top != null && top.state != RESUMED) {
 
-                    // The caller specifies that we'd like to be avoided to be moved to the front,
-                    // so be it!
-                    mDoResume = false;
-                    mAvoidMoveToFront = true;
+                        // The caller specifies that we'd like to be avoided to be moved to the
+                        // front, so be it!
+                        mDoResume = false;
+                        mAvoidMoveToFront = true;
+                    }
                 }
+            } else if (mOptions.getAvoidMoveToFront()) {
+                mAvoidMoveToFront = true;
             }
         }
 
@@ -1838,7 +1845,7 @@
         // Need to update mTargetStack because if task was moved out of it, the original stack may
         // be destroyed.
         mTargetStack = intentActivity.getStack();
-        if (!mMovedToFront && mDoResume) {
+        if (!mAvoidMoveToFront && !mMovedToFront && mDoResume) {
             if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack
                     + " from " + intentActivity);
             mTargetStack.moveToFront("intentActivityFound");
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 6b380f1..0d96468 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.app.ActivityManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -353,10 +354,12 @@
         }
     }
 
+    /** @param state Process state from ActivityManager.java. */
     void noteUidProcessState(int uid, int state) {
         synchronized (mStats) {
             // TODO: remove this once we figure out properly where and how
-            StatsLog.write(StatsLog.UID_PROCESS_STATE_CHANGED, uid, state);
+            StatsLog.write(StatsLog.UID_PROCESS_STATE_CHANGED, uid,
+                    ActivityManager.processStateAmToProto(state));
 
             mStats.noteUidProcessStateLocked(uid, state);
         }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 29bfebe..a50d069 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -24,7 +24,7 @@
 import java.nio.ByteBuffer;
 
 import android.app.ActivityManager;
-import android.app.ActivityManagerProto;
+import android.app.AppProtoEnums;
 import android.os.Build;
 import android.os.SystemClock;
 import com.android.internal.util.MemInfoReader;
@@ -420,47 +420,49 @@
     public static int makeProcStateProtoEnum(int curProcState) {
         switch (curProcState) {
             case ActivityManager.PROCESS_STATE_PERSISTENT:
-                return ActivityManagerProto.PROCESS_STATE_PERSISTENT;
+                return AppProtoEnums.PROCESS_STATE_PERSISTENT;
             case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
-                return ActivityManagerProto.PROCESS_STATE_PERSISTENT_UI;
+                return AppProtoEnums.PROCESS_STATE_PERSISTENT_UI;
             case ActivityManager.PROCESS_STATE_TOP:
-                return ActivityManagerProto.PROCESS_STATE_TOP;
+                return AppProtoEnums.PROCESS_STATE_TOP;
             case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
-                return ActivityManagerProto.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+                return AppProtoEnums.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
             case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
-                return ActivityManagerProto.PROCESS_STATE_FOREGROUND_SERVICE;
+                return AppProtoEnums.PROCESS_STATE_FOREGROUND_SERVICE;
             case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
-                return ActivityManagerProto.PROCESS_STATE_TOP_SLEEPING;
+                return AppProtoEnums.PROCESS_STATE_TOP_SLEEPING;
             case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
-                return ActivityManagerProto.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                return AppProtoEnums.PROCESS_STATE_IMPORTANT_FOREGROUND;
             case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
-                return ActivityManagerProto.PROCESS_STATE_IMPORTANT_BACKGROUND;
+                return AppProtoEnums.PROCESS_STATE_IMPORTANT_BACKGROUND;
             case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
-                return ActivityManagerProto.PROCESS_STATE_TRANSIENT_BACKGROUND;
+                return AppProtoEnums.PROCESS_STATE_TRANSIENT_BACKGROUND;
             case ActivityManager.PROCESS_STATE_BACKUP:
-                return ActivityManagerProto.PROCESS_STATE_BACKUP;
+                return AppProtoEnums.PROCESS_STATE_BACKUP;
             case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
-                return ActivityManagerProto.PROCESS_STATE_HEAVY_WEIGHT;
+                return AppProtoEnums.PROCESS_STATE_HEAVY_WEIGHT;
             case ActivityManager.PROCESS_STATE_SERVICE:
-                return ActivityManagerProto.PROCESS_STATE_SERVICE;
+                return AppProtoEnums.PROCESS_STATE_SERVICE;
             case ActivityManager.PROCESS_STATE_RECEIVER:
-                return ActivityManagerProto.PROCESS_STATE_RECEIVER;
+                return AppProtoEnums.PROCESS_STATE_RECEIVER;
             case ActivityManager.PROCESS_STATE_HOME:
-                return ActivityManagerProto.PROCESS_STATE_HOME;
+                return AppProtoEnums.PROCESS_STATE_HOME;
             case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
-                return ActivityManagerProto.PROCESS_STATE_LAST_ACTIVITY;
+                return AppProtoEnums.PROCESS_STATE_LAST_ACTIVITY;
             case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
-                return ActivityManagerProto.PROCESS_STATE_CACHED_ACTIVITY;
+                return AppProtoEnums.PROCESS_STATE_CACHED_ACTIVITY;
             case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
-                return ActivityManagerProto.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+                return AppProtoEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
             case ActivityManager.PROCESS_STATE_CACHED_RECENT:
-                return ActivityManagerProto.PROCESS_STATE_CACHED_RECENT;
+                return AppProtoEnums.PROCESS_STATE_CACHED_RECENT;
             case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
-                return ActivityManagerProto.PROCESS_STATE_CACHED_EMPTY;
+                return AppProtoEnums.PROCESS_STATE_CACHED_EMPTY;
             case ActivityManager.PROCESS_STATE_NONEXISTENT:
-                return ActivityManagerProto.PROCESS_STATE_NONEXISTENT;
+                return AppProtoEnums.PROCESS_STATE_NONEXISTENT;
+            case ActivityManager.PROCESS_STATE_UNKNOWN:
+                return AppProtoEnums.PROCESS_STATE_UNKNOWN;
             default:
-                return -1;
+                return AppProtoEnums.PROCESS_STATE_UNKNOWN_TO_PROTO;
         }
     }
 
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
new file mode 100644
index 0000000..fe576fda
--- /dev/null
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 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.am;
+
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Handler;
+import android.view.IRecentsAnimationRunner;
+import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
+import com.android.server.wm.WindowManagerService;
+
+/**
+ * Manages the recents animation, including the reordering of the stacks for the transition and
+ * cleanup. See {@link com.android.server.wm.RecentsAnimationController}.
+ */
+class RecentsAnimation implements RecentsAnimationCallbacks {
+    private static final String TAG = RecentsAnimation.class.getSimpleName();
+
+    private static final int RECENTS_ANIMATION_TIMEOUT = 10 * 1000;
+
+    private final ActivityManagerService mService;
+    private final ActivityStackSupervisor mStackSupervisor;
+    private final ActivityStartController mActivityStartController;
+    private final WindowManagerService mWindowManager;
+    private final UserController mUserController;
+    private final Handler mHandler;
+
+    private final Runnable mCancelAnimationRunnable;
+
+    // The stack to restore the home stack behind when the animation is finished
+    private ActivityStack mRestoreHomeBehindStack;
+
+    RecentsAnimation(ActivityManagerService am, ActivityStackSupervisor stackSupervisor,
+            ActivityStartController activityStartController, WindowManagerService wm,
+            UserController userController) {
+        mService = am;
+        mStackSupervisor = stackSupervisor;
+        mActivityStartController = activityStartController;
+        mHandler = new Handler(mStackSupervisor.mLooper);
+        mWindowManager = wm;
+        mUserController = userController;
+        mCancelAnimationRunnable = () -> {
+            // The caller has not finished the animation in a predefined amount of time, so
+            // force-cancel the animation
+            mWindowManager.cancelRecentsAnimation();
+        };
+    }
+
+    void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
+            ComponentName recentsComponent, int recentsUid) {
+
+        // Cancel the previous recents animation if necessary
+        mWindowManager.cancelRecentsAnimation();
+
+        final boolean hasExistingHomeActivity = mStackSupervisor.getHomeActivity() != null;
+        if (!hasExistingHomeActivity) {
+            // No home activity
+            final ActivityOptions opts = ActivityOptions.makeBasic();
+            opts.setLaunchActivityType(ACTIVITY_TYPE_HOME);
+            opts.setAvoidMoveToFront();
+            intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION);
+
+            mActivityStartController.obtainStarter(intent, "startRecentsActivity_noHomeActivity")
+                    .setCallingUid(recentsUid)
+                    .setCallingPackage(recentsComponent.getPackageName())
+                    .setActivityOptions(SafeActivityOptions.fromBundle(opts.toBundle()))
+                    .setMayWait(mUserController.getCurrentUserId())
+                    .execute();
+            mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+
+            // TODO: Maybe wait for app to draw in this particular case?
+        }
+
+        final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
+        final ActivityDisplay display = homeActivity.getDisplay();
+
+        // Save the initial position of the home activity stack to be restored to after the
+        // animation completes
+        mRestoreHomeBehindStack = hasExistingHomeActivity
+                ? display.getStackAboveHome()
+                : null;
+
+        // Move the home activity into place for the animation
+        display.moveHomeStackBehindBottomMostVisibleStack();
+
+        // Mark the home activity as launch-behind to bump its visibility for the
+        // duration of the gesture that is driven by the recents component
+        homeActivity.mLaunchTaskBehind = true;
+
+        // Fetch all the surface controls and pass them to the client to get the animation
+        // started
+        mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this, display.mDisplayId);
+
+        // If we updated the launch-behind state, update the visibility of the activities after we
+        // fetch the visible tasks to be controlled by the animation
+        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+
+        // Post a timeout for the animation
+        mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
+    }
+
+    @Override
+    public void onAnimationFinished(boolean moveHomeToTop) {
+        mHandler.removeCallbacks(mCancelAnimationRunnable);
+        synchronized (mService) {
+            if (mWindowManager.getRecentsAnimationController() == null) return;
+
+            mWindowManager.inSurfaceTransaction(() -> {
+                mWindowManager.cleanupRecentsAnimation();
+
+                // Move the home stack to the front
+                final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
+                if (homeActivity == null) {
+                    return;
+                }
+
+                // Restore the launched-behind state
+                homeActivity.mLaunchTaskBehind = false;
+
+                if (moveHomeToTop) {
+                    // Bring the home stack to the front
+                    final ActivityStack homeStack = homeActivity.getStack();
+                    homeStack.mNoAnimActivities.add(homeActivity);
+                    homeStack.moveToFront("RecentsAnimation.onAnimationFinished()");
+                } else {
+                    // Restore the home stack to its previous position
+                    final ActivityDisplay display = homeActivity.getDisplay();
+                    display.moveHomeStackBehindStack(mRestoreHomeBehindStack);
+                }
+
+                mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
+                mStackSupervisor.resumeFocusedStackTopActivityLocked();
+            });
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/UriPermission.java b/services/core/java/com/android/server/am/UriPermission.java
index 90577e3..1e071aa 100644
--- a/services/core/java/com/android/server/am/UriPermission.java
+++ b/services/core/java/com/android/server/am/UriPermission.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.app.GrantedUriPermission;
 import android.content.Intent;
 import android.os.Binder;
 import android.os.UserHandle;
@@ -387,4 +388,8 @@
     public android.content.UriPermission buildPersistedPublicApiObject() {
         return new android.content.UriPermission(uri.uri, persistedModeFlags, persistedCreateTime);
     }
+
+    public GrantedUriPermission buildGrantedUriPermission() {
+        return new GrantedUriPermission(uri.uri, targetPkg);
+    }
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bedf043..edeee3e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -133,6 +133,7 @@
 
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -4767,6 +4768,16 @@
             Settings.Global.putInt(mContentResolver, Settings.Global.MODE_RINGER, ringerMode);
         }
 
+        private String getSoundEffectFilePath(int effectType) {
+            String filePath = Environment.getProductDirectory() + SOUND_EFFECTS_PATH
+                    + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]);
+            if (!new File(filePath).isFile()) {
+                filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH
+                        + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]);
+            }
+            return filePath;
+        }
+
         private boolean onLoadSoundEffects() {
             int status;
 
@@ -4836,9 +4847,7 @@
                         continue;
                     }
                     if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
-                        String filePath = Environment.getRootDirectory()
-                                + SOUND_EFFECTS_PATH
-                                + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]);
+                        String filePath = getSoundEffectFilePath(effect);
                         int sampleId = mSoundPool.load(filePath, 0);
                         if (sampleId <= 0) {
                             Log.w(TAG, "Soundpool could not load file: "+filePath);
@@ -4944,8 +4953,7 @@
                 } else {
                     MediaPlayer mediaPlayer = new MediaPlayer();
                     try {
-                        String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH +
-                                    SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]);
+                        String filePath = getSoundEffectFilePath(effectType);
                         mediaPlayer.setDataSource(filePath);
                         mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
                         mediaPlayer.prepare();
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 979beed..57258a8 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -257,6 +257,29 @@
                 uid, iface, ethertype, dstMac, srcIp, dstIp, ipNextHeader, srcPort, dstPort);
     }
 
+    @Override
+    public synchronized void onTcpSocketStatsEvent(int[] networkIds,
+            int[] sentPackets, int[] lostPackets, int[] rttsUs, int[] sentAckDiffsMs) {
+        if (networkIds.length != sentPackets.length
+                || networkIds.length != lostPackets.length
+                || networkIds.length != rttsUs.length
+                || networkIds.length != sentAckDiffsMs.length) {
+            Log.e(TAG, "Mismatched lengths of TCP socket stats data arrays");
+            return;
+        }
+
+        long timestamp = System.currentTimeMillis();
+        for (int i = 0; i < networkIds.length; i++) {
+            int netId = networkIds[i];
+            int sent = sentPackets[i];
+            int lost = lostPackets[i];
+            int rttUs = rttsUs[i];
+            int sentAckDiffMs = sentAckDiffsMs[i];
+            getMetricsForNetwork(timestamp, netId)
+                    .addTcpStatsResult(sent, lost, rttUs, sentAckDiffMs);
+        }
+    }
+
     private void addWakeupEvent(WakeupEvent event) {
         String iface = event.iface;
         mWakeupEvents.append(event);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index ad2cf6c..422d0cb 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -20,6 +20,8 @@
 import android.accounts.AccountAndUser;
 import android.accounts.AccountManager;
 import android.accounts.AccountManagerInternal;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.Notification;
@@ -32,6 +34,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ISyncAdapter;
+import android.content.ISyncAdapterUnsyncableAccountCallback;
 import android.content.ISyncContext;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -212,6 +215,10 @@
     private static final int SYNC_OP_STATE_INVALID = 1;
     private static final int SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS = 2;
 
+    /** Flags used when connecting to a sync adapter service */
+    private static final int SYNC_ADAPTER_CONNECTION_FLAGS = Context.BIND_AUTO_CREATE
+            | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT;
+
     private Context mContext;
 
     private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0];
@@ -876,7 +883,7 @@
     public void scheduleSync(Account requestedAccount, int userId, int reason,
                              String requestedAuthority, Bundle extras, int targetSyncState) {
         scheduleSync(requestedAccount, userId, reason, requestedAuthority, extras, targetSyncState,
-                0 /* min delay */);
+                0 /* min delay */, true /* checkIfAccountReady */);
     }
 
     /**
@@ -884,7 +891,7 @@
      */
     private void scheduleSync(Account requestedAccount, int userId, int reason,
                              String requestedAuthority, Bundle extras, int targetSyncState,
-                             final long minDelayMillis) {
+                             final long minDelayMillis, boolean checkIfAccountReady) {
         final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
         if (extras == null) {
             extras = new Bundle();
@@ -963,7 +970,8 @@
             }
 
             for (String authority : syncableAuthorities) {
-                int isSyncable = computeSyncable(account.account, account.userId, authority);
+                int isSyncable = computeSyncable(account.account, account.userId, authority,
+                        !checkIfAccountReady);
 
                 if (isSyncable == AuthorityInfo.NOT_SYNCABLE) {
                     continue;
@@ -1000,7 +1008,8 @@
                                 if (result != null
                                         && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
                                     scheduleSync(account.account, userId, reason, authority,
-                                            finalExtras, targetSyncState, minDelayMillis);
+                                            finalExtras, targetSyncState, minDelayMillis,
+                                            true /* checkIfAccountReady */);
                                 }
                             }
                         ));
@@ -1009,7 +1018,7 @@
 
                 final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
                 final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
-                if (isSyncable < 0 && isAlwaysSyncable) {
+                if (!checkIfAccountReady && isSyncable < 0 && isAlwaysSyncable) {
                     mSyncStorageEngine.setIsSyncable(
                             account.account, account.userId, authority, AuthorityInfo.SYNCABLE);
                     isSyncable = AuthorityInfo.SYNCABLE;
@@ -1045,25 +1054,34 @@
                 final String owningPackage = syncAdapterInfo.componentName.getPackageName();
 
                 if (isSyncable == AuthorityInfo.NOT_INITIALIZED) {
-                    // Initialisation sync.
-                    Bundle newExtras = new Bundle();
-                    newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
-                    if (isLoggable) {
-                        Slog.v(TAG, "schedule initialisation Sync:"
-                                + ", delay until " + delayUntil
-                                + ", run by " + 0
-                                + ", flexMillis " + 0
-                                + ", source " + source
-                                + ", account " + account
-                                + ", authority " + authority
-                                + ", extras " + newExtras);
+                    if (checkIfAccountReady) {
+                        Bundle finalExtras = new Bundle(extras);
+
+                        sendOnUnsyncableAccount(mContext, syncAdapterInfo, account.userId,
+                                () -> scheduleSync(account.account, account.userId, reason,
+                                        authority, finalExtras, targetSyncState, minDelayMillis,
+                                        false));
+                    } else {
+                        // Initialisation sync.
+                        Bundle newExtras = new Bundle();
+                        newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
+                        if (isLoggable) {
+                            Slog.v(TAG, "schedule initialisation Sync:"
+                                    + ", delay until " + delayUntil
+                                    + ", run by " + 0
+                                    + ", flexMillis " + 0
+                                    + ", source " + source
+                                    + ", account " + account
+                                    + ", authority " + authority
+                                    + ", extras " + newExtras);
+                        }
+                        postScheduleSyncMessage(
+                                new SyncOperation(account.account, account.userId,
+                                        owningUid, owningPackage, reason, source,
+                                        authority, newExtras, allowParallelSyncs),
+                                minDelayMillis
+                        );
                     }
-                    postScheduleSyncMessage(
-                            new SyncOperation(account.account, account.userId,
-                                    owningUid, owningPackage, reason, source,
-                                    authority, newExtras, allowParallelSyncs),
-                            minDelayMillis
-                    );
                 } else if (targetSyncState == AuthorityInfo.UNDEFINED
                         || targetSyncState == isSyncable) {
                     if (isLoggable) {
@@ -1085,10 +1103,6 @@
         }
     }
 
-    private int computeSyncable(Account account, int userId, String authority) {
-        return computeSyncable(account, userId, authority, true);
-    }
-
     public int computeSyncable(Account account, int userId, String authority,
             boolean checkAccountAccess) {
         final int status = getIsSyncable(account, userId, authority);
@@ -1198,7 +1212,7 @@
         final Bundle extras = new Bundle();
         extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
         scheduleSync(account, userId, reason, authority, extras,
-                AuthorityInfo.UNDEFINED, LOCAL_SYNC_DELAY);
+                AuthorityInfo.UNDEFINED, LOCAL_SYNC_DELAY, true /* checkIfAccountReady */);
     }
 
     public SyncAdapterType[] getSyncAdapterTypes(int userId) {
@@ -1706,6 +1720,28 @@
     }
 
     /**
+     * Construct intent used to bind to an adapter.
+     *
+     * @param context Context to create intent for
+     * @param syncAdapterComponent The adapter description
+     * @param userId The user the adapter belongs to
+     *
+     * @return The intent required to bind to the adapter
+     */
+    static @NonNull Intent getAdapterBindIntent(@NonNull Context context,
+            @NonNull ComponentName syncAdapterComponent, @UserIdInt int userId) {
+        final Intent intent = new Intent();
+        intent.setAction("android.content.SyncAdapter");
+        intent.setComponent(syncAdapterComponent);
+        intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+                com.android.internal.R.string.sync_binding_label);
+        intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(context, 0,
+                new Intent(Settings.ACTION_SYNC_SETTINGS), 0, null, UserHandle.of(userId)));
+
+        return intent;
+    }
+
+    /**
      * @hide
      */
     class ActiveSyncContext extends ISyncContext.Stub
@@ -1793,19 +1829,11 @@
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Log.d(TAG, "bindToSyncAdapter: " + serviceComponent + ", connection " + this);
             }
-            Intent intent = new Intent();
-            intent.setAction("android.content.SyncAdapter");
-            intent.setComponent(serviceComponent);
-            intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
-                    com.android.internal.R.string.sync_binding_label);
-            intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
-                    mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0,
-                    null, new UserHandle(userId)));
+            Intent intent = getAdapterBindIntent(mContext, serviceComponent, userId);
+
             mBound = true;
             final boolean bindResult = mContext.bindServiceAsUser(intent, this,
-                    Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
-                            | Context.BIND_ALLOW_OOM_MANAGEMENT,
-                    new UserHandle(mSyncOperation.target.userId));
+                    SYNC_ADAPTER_CONNECTION_FLAGS, new UserHandle(mSyncOperation.target.userId));
             mLogger.log("bindService() returned=", mBound, " for ", this);
             if (!bindResult) {
                 mBound = false;
@@ -2528,6 +2556,92 @@
         }
     }
 
+    interface OnReadyCallback {
+        void onReady();
+    }
+
+    static void sendOnUnsyncableAccount(@NonNull Context context,
+            @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo,
+            @UserIdInt int userId, @NonNull OnReadyCallback onReadyCallback) {
+        OnUnsyncableAccountCheck connection = new OnUnsyncableAccountCheck(syncAdapterInfo,
+                onReadyCallback);
+
+        boolean isBound = context.bindServiceAsUser(
+                getAdapterBindIntent(context, syncAdapterInfo.componentName, userId),
+                connection, SYNC_ADAPTER_CONNECTION_FLAGS, UserHandle.of(userId));
+
+        if (isBound) {
+            // Unbind after SERVICE_BOUND_TIME_MILLIS to not leak the connection.
+            (new Handler(Looper.getMainLooper())).postDelayed(
+                    () -> context.unbindService(connection),
+                    OnUnsyncableAccountCheck.SERVICE_BOUND_TIME_MILLIS);
+        } else {
+                /*
+                 * The default implementation of adapter.onUnsyncableAccount returns true. Hence if
+                 * there the service cannot be bound, assume the default behavior.
+                 */
+            connection.onReady();
+        }
+    }
+
+
+    /**
+     * Helper class for calling ISyncAdapter.onUnsyncableAccountDone.
+     *
+     * If this returns {@code true} the onReadyCallback is called. Otherwise nothing happens.
+     */
+    private static class OnUnsyncableAccountCheck implements ServiceConnection {
+        static final long SERVICE_BOUND_TIME_MILLIS = 5000;
+
+        private final @NonNull OnReadyCallback mOnReadyCallback;
+        private final @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType>
+                mSyncAdapterInfo;
+
+        OnUnsyncableAccountCheck(
+                @NonNull RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo,
+                @NonNull OnReadyCallback onReadyCallback) {
+            mSyncAdapterInfo = syncAdapterInfo;
+            mOnReadyCallback = onReadyCallback;
+        }
+
+        private void onReady() {
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mOnReadyCallback.onReady();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            final ISyncAdapter adapter = ISyncAdapter.Stub.asInterface(service);
+
+            try {
+                adapter.onUnsyncableAccount(new ISyncAdapterUnsyncableAccountCallback.Stub() {
+                    @Override
+                    public void onUnsyncableAccountDone(boolean isReady) {
+                        if (isReady) {
+                            onReady();
+                        }
+                    }
+                });
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Could not call onUnsyncableAccountDone " + mSyncAdapterInfo, e);
+                /*
+                 * The default implementation of adapter.onUnsyncableAccount returns true. Hence if
+                 * there is a crash in the implementation, assume the default behavior.
+                 */
+                onReady();
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            // Wait until the service connects again
+        }
+    }
+
     /**
      * A helper object to keep track of the time we have spent syncing since the last boot
      */
@@ -3199,7 +3313,7 @@
                 return SYNC_OP_STATE_INVALID;
             }
             // Drop this sync request if it isn't syncable.
-            state = computeSyncable(target.account, target.userId, target.provider);
+            state = computeSyncable(target.account, target.userId, target.provider, true);
             if (state == AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS) {
                 if (isLoggable) {
                     Slog.v(TAG, "    Dropping sync operation: "
diff --git a/services/core/java/com/android/server/job/JobSchedulerInternal.java b/services/core/java/com/android/server/job/JobSchedulerInternal.java
index 4e74908..569e9ec 100644
--- a/services/core/java/com/android/server/job/JobSchedulerInternal.java
+++ b/services/core/java/com/android/server/job/JobSchedulerInternal.java
@@ -16,6 +16,7 @@
 
 package com.android.server.job;
 
+import android.annotation.UserIdInt;
 import android.app.job.JobInfo;
 
 import java.util.List;
@@ -39,6 +40,14 @@
     long nextHeartbeatForBucket(int bucket);
 
     /**
+     * Heartbeat ordinal for the given app.  This is typically the heartbeat at which
+     * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
+     * jobs in a long time is immediately runnable even if the app is bucketed into
+     * an infrequent time allocation.
+     */
+    public long baseHeartbeatForApp(String packageName, @UserIdInt int userId, int appBucket);
+
+    /**
      * Returns a list of pending jobs scheduled by the system service.
      */
     List<JobInfo> getSystemScheduledPendingJobs();
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 5da470e..2066f2a 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 
+import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -39,6 +40,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.Intent.UriFlags;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
@@ -2065,6 +2067,33 @@
         }
 
         /**
+         * Heartbeat ordinal for the given app.  This is typically the heartbeat at which
+         * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
+         * jobs in a long time is immediately runnable even if the app is bucketed into
+         * an infrequent time allocation.
+         */
+        public long baseHeartbeatForApp(String packageName, @UserIdInt int userId,
+                final int appStandbyBucket) {
+            if (appStandbyBucket == 0 ||
+                    appStandbyBucket >= mConstants.STANDBY_BEATS.length) {
+                // ACTIVE => everything can be run right away
+                // NEVER => we won't run them anyway, so let them go in the future
+                // as soon as the app enters normal use
+                return 0;
+            }
+
+            final long timeSinceLastJob = mStandbyTracker.getTimeSinceLastJobRun(
+                    packageName, userId);
+            final long bucketLength = mConstants.STANDBY_BEATS[appStandbyBucket];
+            final long bucketsAgo = timeSinceLastJob / bucketLength;
+
+            // If we haven't run any jobs for more than the app's current bucket period, just
+            // consider anything new to be immediately runnable.  Otherwise, base it on the
+            // bucket at which we last ran jobs.
+            return (bucketsAgo > bucketLength) ? 0 : (getCurrentHeartbeat() - bucketsAgo);
+        }
+
+        /**
          * Returns a list of all pending jobs. A running job is not considered pending. Periodic
          * jobs are always considered pending.
          */
@@ -2139,10 +2168,14 @@
             mUsageStats = usageStats;
         }
 
+        public long getTimeSinceLastJobRun(String packageName, final @UserIdInt int userId) {
+            return mUsageStats.getTimeSinceLastJobRun(packageName, userId);
+        }
+
         // AppIdleStateChangeListener interface for live updates
 
         @Override
-        public void onAppIdleStateChanged(final String packageName, final int userId,
+        public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
                 boolean idle, int bucket) {
             final int uid = mLocalPM.getPackageUid(packageName,
                     PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 6da783c..a24a4ac 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -1052,13 +1052,18 @@
             final ArraySet<JobStatus> jobs = mJobs.get(uid);
             final int sourceUid = job.getSourceUid();
             final ArraySet<JobStatus> jobsForSourceUid = mJobsPerSourceUid.get(sourceUid);
-            boolean didRemove = jobs != null && jobs.remove(job) && jobsForSourceUid.remove(job);
-            if (didRemove) {
-                if (jobs.size() == 0) {
-                    // no more jobs for this uid; let the now-empty set object be GC'd.
+            final boolean didRemove = jobs != null && jobs.remove(job);
+            final boolean sourceRemove = jobsForSourceUid != null && jobsForSourceUid.remove(job);
+            if (didRemove != sourceRemove) {
+                Slog.wtf(TAG, "Job presence mismatch; caller=" + didRemove
+                        + " source=" + sourceRemove);
+            }
+            if (didRemove || sourceRemove) {
+                // no more jobs for this uid?  let the now-empty set objects be GC'd.
+                if (jobs != null && jobs.size() == 0) {
                     mJobs.remove(uid);
                 }
-                if (jobsForSourceUid.size() == 0) {
+                if (jobsForSourceUid != null && jobsForSourceUid.size() == 0) {
                     mJobsPerSourceUid.remove(sourceUid);
                 }
                 return true;
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index d9a5ff6..08ff7bd 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -414,8 +414,9 @@
         int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage,
                 sourceUserId, elapsedNow);
         JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
-        long currentHeartbeat = js != null ? js.currentHeartbeat() : 0;
-
+        long currentHeartbeat = js != null
+                ? js.baseHeartbeatForApp(jobPackage, sourceUserId, standbyBucket)
+                : 0;
         return new JobStatus(job, callingUid, resolveTargetSdkVersion(job), sourcePkg, sourceUserId,
                 standbyBucket, currentHeartbeat, tag, 0,
                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index e1e769c..db46c3d 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -305,7 +305,7 @@
             NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
             InvalidKeyException, InvalidAlgorithmParameterException {
         PlatformKeyManager platformKeyManager = mPlatformKeyManagerFactory.newInstance();
-        PlatformDecryptionKey decryptKey = platformKeyManager.getDecryptKey(mUserId);
+        PlatformDecryptionKey decryptKey = platformKeyManager.getDecryptKey(mUserId);;
         Map<String, WrappedKey> wrappedKeys = mRecoverableKeyStoreDb.getAllKeys(
                 mUserId, recoveryAgentUid, decryptKey.getGenerationId());
         return WrappedKey.unwrapKeys(decryptKey, wrappedKeys);
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index 7005de5..ee6a893 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -131,6 +131,7 @@
     /**
      * Generates a new key and increments the generation ID. Should be invoked if the platform key
      * is corrupted and needs to be rotated.
+     * Updates status of old keys to {@code RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE}.
      *
      * @param userId The ID of the user to whose lock screen the platform key must be bound.
      * @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
@@ -158,6 +159,7 @@
 
     /**
      * Returns the platform key used for encryption.
+     * Tries to regenerate key one time if it is permanently invalid.
      *
      * @param userId The ID of the user to whose lock screen the platform key must be bound.
      * @throws KeyStoreException if there was an AndroidKeyStore error.
@@ -170,6 +172,30 @@
     public PlatformEncryptionKey getEncryptKey(int userId) throws KeyStoreException,
            UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
         init(userId);
+        try {
+            return getEncryptKeyInternal(userId);
+        } catch (UnrecoverableKeyException e) {
+            Log.i(TAG, String.format(Locale.US,
+                    "Regenerating permanently invalid Platform key for user %d.",
+                    userId));
+            regenerate(userId);
+            return getEncryptKeyInternal(userId);
+        }
+    }
+
+    /**
+     * Returns the platform key used for encryption.
+     *
+     * @param userId The ID of the user to whose lock screen the platform key must be bound.
+     * @throws KeyStoreException if there was an AndroidKeyStore error.
+     * @throws UnrecoverableKeyException if the key could not be recovered.
+     * @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
+     * @throws InsecureUserException if the user does not have a lock screen set.
+     *
+     * @hide
+     */
+    private PlatformEncryptionKey getEncryptKeyInternal(int userId) throws KeyStoreException,
+           UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
         int generationId = getGenerationId(userId);
         AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
                 getEncryptAlias(userId, generationId), /*password=*/ null);
@@ -178,6 +204,32 @@
 
     /**
      * Returns the platform key used for decryption. Only works after a recent screen unlock.
+     * Tries to regenerate key one time if it is permanently invalid.
+     *
+     * @param userId The ID of the user to whose lock screen the platform key must be bound.
+     * @throws KeyStoreException if there was an AndroidKeyStore error.
+     * @throws UnrecoverableKeyException if the key could not be recovered.
+     * @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
+     * @throws InsecureUserException if the user does not have a lock screen set.
+     *
+     * @hide
+     */
+    public PlatformDecryptionKey getDecryptKey(int userId) throws KeyStoreException,
+           UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
+        init(userId);
+        try {
+            return getDecryptKeyInternal(userId);
+        } catch (UnrecoverableKeyException e) {
+            Log.i(TAG, String.format(Locale.US,
+                    "Regenerating permanently invalid Platform key for user %d.",
+                    userId));
+            regenerate(userId);
+            return getDecryptKeyInternal(userId);
+        }
+    }
+
+    /**
+     * Returns the platform key used for decryption. Only works after a recent screen unlock.
      *
      * @param userId The ID of the user to whose lock screen the platform key must be bound.
      * @throws KeyStoreException if there was an AndroidKeyStore error.
@@ -187,9 +239,8 @@
      *
      * @hide
      */
-    public PlatformDecryptionKey getDecryptKey(int userId) throws KeyStoreException,
+    private PlatformDecryptionKey getDecryptKeyInternal(int userId) throws KeyStoreException,
            UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
-        init(userId);
         int generationId = getGenerationId(userId);
         AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
                 getDecryptAlias(userId, generationId), /*password=*/ null);
@@ -294,13 +345,7 @@
         String decryptAlias = getDecryptAlias(userId, generationId);
         SecretKey secretKey = generateAesKey();
 
-        mKeyStore.setEntry(
-                encryptAlias,
-                new KeyStore.SecretKeyEntry(secretKey),
-                new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
-                    .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
-                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
-                    .build());
+        // Store Since decryption key first since it is more likely to fail.
         mKeyStore.setEntry(
                 decryptAlias,
                 new KeyStore.SecretKeyEntry(secretKey),
@@ -313,6 +358,14 @@
                     .setBoundToSpecificSecureUserId(userId)
                     .build());
 
+        mKeyStore.setEntry(
+                encryptAlias,
+                new KeyStore.SecretKeyEntry(secretKey),
+                new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
+                    .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                    .build());
+
         setGenerationId(userId, generationId);
 
         try {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index f2e71b3..b96208d 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
+import android.security.keystore.recovery.RecoveryController;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -289,8 +290,27 @@
         ContentValues values = new ContentValues();
         values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, userId);
         values.put(UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID, generationId);
-        return db.replace(
+        long result = db.replace(
                 UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values);
+        if (result != -1) {
+            invalidateKeysWithOldGenerationId(userId, generationId);
+        }
+        return result;
+    }
+
+    /**
+     * Updates status of old keys to {@code RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE}.
+     */
+    public void invalidateKeysWithOldGenerationId(int userId, int newGenerationId) {
+        SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS,
+                RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+        String selection =
+                KeysEntry.COLUMN_NAME_USER_ID + " = ? AND "
+                + KeysEntry.COLUMN_NAME_GENERATION_ID + " < ?";
+        db.update(KeysEntry.TABLE_NAME, values, selection,
+            new String[] {String.valueOf(userId), String.valueOf(newGenerationId)});
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 5bf38dc..10e05cf 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -362,9 +362,10 @@
                 continue;
             }
 
-            // If the path is in /system or /vendor, ignore. It will have been ota-dexopted into
-            // /data/ota and moved into the dalvik-cache already.
-            if (pkg.codePath.startsWith("/system") || pkg.codePath.startsWith("/vendor")) {
+            // If the path is in /system, /vendor or /product, ignore. It will have been
+            // ota-dexopted into /data/ota and moved into the dalvik-cache already.
+            if (pkg.codePath.startsWith("/system") || pkg.codePath.startsWith("/vendor")
+                    || pkg.codePath.startsWith("/product")) {
                 continue;
             }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 837a118..7767945 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -466,6 +466,7 @@
     static final int SCAN_AS_PRIVILEGED = 1<<18;
     static final int SCAN_AS_OEM = 1<<19;
     static final int SCAN_AS_VENDOR = 1<<20;
+    static final int SCAN_AS_PRODUCT = 1<<21;
 
     @IntDef(flag = true, prefix = { "SCAN_" }, value = {
             SCAN_NO_DEX,
@@ -570,6 +571,8 @@
 
     private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
 
+    private static final String PRODUCT_OVERLAY_DIR = "/product/overlay";
+
     private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob";
 
     /** Canonical intent used to identify what counts as a "web browser" app */
@@ -2552,7 +2555,7 @@
                 scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
             }
 
-            // Collect vendor overlay packages. (Do this before scanning any apps.)
+            // Collect vendor/product overlay packages. (Do this before scanning any apps.)
             // For security and version matching reason, only consider
             // overlay packages if they reside in the right directory.
             scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),
@@ -2562,6 +2565,13 @@
                     | SCAN_AS_SYSTEM
                     | SCAN_AS_VENDOR,
                     0);
+            scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR),
+                    mDefParseFlags
+                    | PackageParser.PARSE_IS_SYSTEM_DIR,
+                    scanFlags
+                    | SCAN_AS_SYSTEM
+                    | SCAN_AS_PRODUCT,
+                    0);
 
             mParallelPackageParserCallback.findStaticOverlayPackages();
 
@@ -2595,8 +2605,7 @@
                     0);
 
             // Collected privileged vendor packages.
-                File privilegedVendorAppDir = new File(Environment.getVendorDirectory(),
-                        "priv-app");
+            File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
             try {
                 privilegedVendorAppDir = privilegedVendorAppDir.getCanonicalFile();
             } catch (IOException e) {
@@ -2636,6 +2645,37 @@
                     | SCAN_AS_OEM,
                     0);
 
+            // Collected privileged product packages.
+            File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app");
+            try {
+                privilegedProductAppDir = privilegedProductAppDir.getCanonicalFile();
+            } catch (IOException e) {
+                // failed to look up canonical path, continue with original one
+            }
+            scanDirTracedLI(privilegedProductAppDir,
+                    mDefParseFlags
+                    | PackageParser.PARSE_IS_SYSTEM_DIR,
+                    scanFlags
+                    | SCAN_AS_SYSTEM
+                    | SCAN_AS_PRODUCT
+                    | SCAN_AS_PRIVILEGED,
+                    0);
+
+            // Collect ordinary product packages.
+            File productAppDir = new File(Environment.getProductDirectory(), "app");
+            try {
+                productAppDir = productAppDir.getCanonicalFile();
+            } catch (IOException e) {
+                // failed to look up canonical path, continue with original one
+            }
+            scanDirTracedLI(productAppDir,
+                    mDefParseFlags
+                    | PackageParser.PARSE_IS_SYSTEM_DIR,
+                    scanFlags
+                    | SCAN_AS_SYSTEM
+                    | SCAN_AS_PRODUCT,
+                    0);
+
             // Prune any system packages that no longer exist.
             final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
             // Stub packages must either be replaced with full versions in the /data
@@ -2842,6 +2882,23 @@
                                     scanFlags
                                     | SCAN_AS_SYSTEM
                                     | SCAN_AS_OEM;
+                        } else if (FileUtils.contains(privilegedProductAppDir, scanFile)) {
+                            reparseFlags =
+                                    mDefParseFlags |
+                                    PackageParser.PARSE_IS_SYSTEM_DIR;
+                            rescanFlags =
+                                    scanFlags
+                                    | SCAN_AS_SYSTEM
+                                    | SCAN_AS_PRODUCT
+                                    | SCAN_AS_PRIVILEGED;
+                        } else if (FileUtils.contains(productAppDir, scanFile)) {
+                            reparseFlags =
+                                    mDefParseFlags |
+                                    PackageParser.PARSE_IS_SYSTEM_DIR;
+                            rescanFlags =
+                                    scanFlags
+                                    | SCAN_AS_SYSTEM
+                                    | SCAN_AS_PRODUCT;
                         } else {
                             Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
                             continue;
@@ -9862,6 +9919,7 @@
      * <li>{@link #SCAN_AS_PRIVILEGED}</li>
      * <li>{@link #SCAN_AS_OEM}</li>
      * <li>{@link #SCAN_AS_VENDOR}</li>
+     * <li>{@link #SCAN_AS_PRODUCT}</li>
      * <li>{@link #SCAN_AS_INSTANT_APP}</li>
      * <li>{@link #SCAN_AS_VIRTUAL_PRELOAD}</li>
      * </ul>
@@ -9884,6 +9942,10 @@
                     & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
                 scanFlags |= SCAN_AS_VENDOR;
             }
+            if ((disabledPkgSetting.pkgPrivateFlags
+                    & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
+                scanFlags |= SCAN_AS_PRODUCT;
+            }
         }
         if (pkgSetting != null) {
             final int userId = ((user == null) ? 0 : user.getIdentifier());
@@ -10662,6 +10724,10 @@
             pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_VENDOR;
         }
 
+        if ((scanFlags & SCAN_AS_PRODUCT) != 0) {
+            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRODUCT;
+        }
+
         if (!isSystemApp(pkg)) {
             // Only system apps can use these features.
             pkg.mOriginalPackages = null;
@@ -11708,6 +11774,8 @@
             codeRoot = Environment.getOemDirectory();
         } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) {
             codeRoot = Environment.getVendorDirectory();
+        } else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) {
+            codeRoot = Environment.getProductDirectory();
         } else {
             // Unrecognized code path; take its top real segment as the apk root:
             // e.g. /something/app/blah.apk => /something
@@ -16110,7 +16178,7 @@
 
         boolean sysPkg = (isSystemApp(oldPackage));
         if (sysPkg) {
-            // Set the system/privileged/oem/vendor flags as needed
+            // Set the system/privileged/oem/vendor/product flags as needed
             final boolean privileged =
                     (oldPackage.applicationInfo.privateFlags
                             & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
@@ -16120,12 +16188,16 @@
             final boolean vendor =
                     (oldPackage.applicationInfo.privateFlags
                             & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
+            final boolean product =
+                    (oldPackage.applicationInfo.privateFlags
+                            & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
             final @ParseFlags int systemParseFlags = parseFlags;
             final @ScanFlags int systemScanFlags = scanFlags
                     | SCAN_AS_SYSTEM
                     | (privileged ? SCAN_AS_PRIVILEGED : 0)
                     | (oem ? SCAN_AS_OEM : 0)
-                    | (vendor ? SCAN_AS_VENDOR : 0);
+                    | (vendor ? SCAN_AS_VENDOR : 0)
+                    | (product ? SCAN_AS_PRODUCT : 0);
 
             replaceSystemPackageLIF(oldPackage, pkg, systemParseFlags, systemScanFlags,
                     user, allUsers, installerPackageName, res, installReason);
@@ -17373,6 +17445,10 @@
         return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
     }
 
+    private static boolean isProductApp(PackageParser.Package pkg) {
+        return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+    }
+
     private static boolean hasDomainURLs(PackageParser.Package pkg) {
         return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
     }
@@ -18112,8 +18188,10 @@
         try {
             final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
             final File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
+            final File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app");
             return path.startsWith(privilegedAppDir.getCanonicalPath())
-                    || path.startsWith(privilegedVendorAppDir.getCanonicalPath());
+                    || path.startsWith(privilegedVendorAppDir.getCanonicalPath())
+                    || path.startsWith(privilegedProductAppDir.getCanonicalPath());
         } catch (IOException e) {
             Slog.e(TAG, "Unable to access code path " + path);
         }
@@ -18138,6 +18216,15 @@
         return false;
     }
 
+    static boolean locationIsProduct(String path) {
+        try {
+            return path.startsWith(Environment.getProductDirectory().getCanonicalPath());
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to access code path " + path);
+        }
+        return false;
+    }
+
     /*
      * Tries to delete system package.
      */
@@ -18262,6 +18349,9 @@
         if (locationIsVendor(codePathString)) {
             scanFlags |= SCAN_AS_VENDOR;
         }
+        if (locationIsProduct(codePathString)) {
+            scanFlags |= SCAN_AS_PRODUCT;
+        }
 
         final File codePath = new File(codePathString);
         final PackageParser.Package pkg =
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 47cd813..686c4a5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1555,6 +1555,15 @@
         }
     }
 
+    private boolean isProductApp(String pkg) {
+        try {
+            final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
+            return info != null && info.applicationInfo.isProduct();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     private int runGetPrivappPermissions() {
         final String pkg = getNextArg();
         if (pkg == null) {
@@ -1562,9 +1571,14 @@
             return 1;
         }
 
-        ArraySet<String> privAppPermissions = isVendorApp(pkg) ?
-                SystemConfig.getInstance().getVendorPrivAppPermissions(pkg)
-                    : SystemConfig.getInstance().getPrivAppPermissions(pkg);
+        ArraySet<String> privAppPermissions = null;
+        if (isVendorApp(pkg)) {
+            privAppPermissions = SystemConfig.getInstance().getVendorPrivAppPermissions(pkg);
+        } else if (isProductApp(pkg)) {
+            privAppPermissions = SystemConfig.getInstance().getProductPrivAppPermissions(pkg);
+        } else {
+            privAppPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg);
+        }
 
         getOutPrintWriter().println(privAppPermissions == null
                 ? "{}" : privAppPermissions.toString());
@@ -1578,9 +1592,14 @@
             return 1;
         }
 
-        ArraySet<String> privAppPermissions = isVendorApp(pkg) ?
-                SystemConfig.getInstance().getVendorPrivAppDenyPermissions(pkg)
-                    : SystemConfig.getInstance().getPrivAppDenyPermissions(pkg);
+        ArraySet<String> privAppPermissions = null;
+        if (isVendorApp(pkg)) {
+            privAppPermissions = SystemConfig.getInstance().getVendorPrivAppDenyPermissions(pkg);
+        } else if (isProductApp(pkg)) {
+            privAppPermissions = SystemConfig.getInstance().getProductPrivAppDenyPermissions(pkg);
+        } else {
+            privAppPermissions = SystemConfig.getInstance().getPrivAppDenyPermissions(pkg);
+        }
 
         getOutPrintWriter().println(privAppPermissions == null
                 ? "{}" : privAppPermissions.toString());
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 2a2430c..3e2bd4a 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -173,6 +173,10 @@
         return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
     }
 
+    public boolean isProduct() {
+        return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+    }
+
     public boolean isForwardLocked() {
         return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
     }
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 46ba006..7c92045 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -62,6 +62,7 @@
                 & (ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
                 | ApplicationInfo.PRIVATE_FLAG_OEM
                 | ApplicationInfo.PRIVATE_FLAG_VENDOR
+                | ApplicationInfo.PRIVATE_FLAG_PRODUCT
                 | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK
                 | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 8ce412e..5e9019d 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -851,6 +851,8 @@
                 pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM;
         pkgSetting.pkgPrivateFlags |=
                 pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR;
+        pkgSetting.pkgPrivateFlags |=
+                pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT;
         pkgSetting.primaryCpuAbiString = primaryCpuAbi;
         pkgSetting.secondaryCpuAbiString = secondaryCpuAbi;
         if (childPkgNames != null) {
@@ -4397,6 +4399,7 @@
             ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER, "REQUIRED_FOR_SYSTEM_USER",
             ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY, "STATIC_SHARED_LIBRARY",
             ApplicationInfo.PRIVATE_FLAG_VENDOR, "VENDOR",
+            ApplicationInfo.PRIVATE_FLAG_PRODUCT, "PRODUCT",
             ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD, "VIRTUAL_PRELOAD",
     };
 
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 6e07eaa..e2123c2 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1215,6 +1215,10 @@
         if (dir.isDirectory() && dir.canRead()) {
             Collections.addAll(ret, dir.listFiles());
         }
+        dir = new File(Environment.getProductDirectory(), "etc/default-permissions");
+        if (dir.isDirectory() && dir.canRead()) {
+            Collections.addAll(ret, dir.listFiles());
+        }
         return ret.isEmpty() ? null : ret.toArray(new File[0]);
     }
 
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 786b998..cb3b107 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -954,9 +954,16 @@
      * <p>This handles parent/child apps.
      */
     private boolean hasPrivappWhitelistEntry(String perm, PackageParser.Package pkg) {
-        ArraySet<String> wlPermissions = pkg.isVendor() ?
-                SystemConfig.getInstance().getVendorPrivAppPermissions(pkg.packageName)
-                    : SystemConfig.getInstance().getPrivAppPermissions(pkg.packageName);
+        ArraySet<String> wlPermissions = null;
+        if (pkg.isVendor()) {
+            wlPermissions =
+                    SystemConfig.getInstance().getVendorPrivAppPermissions(pkg.packageName);
+        } else if (pkg.isProduct()) {
+            wlPermissions =
+                    SystemConfig.getInstance().getProductPrivAppPermissions(pkg.packageName);
+        } else {
+            wlPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg.packageName);
+        }
         // Let's check if this package is whitelisted...
         boolean whitelisted = wlPermissions != null && wlPermissions.contains(perm);
         // If it's not, we'll also tail-recurse to the parent.
@@ -979,11 +986,17 @@
                 // Only report violations for apps on system image
                 if (!mSystemReady && !pkg.isUpdatedSystemApp()) {
                     // it's only a reportable violation if the permission isn't explicitly denied
-                    final ArraySet<String> deniedPermissions = pkg.isVendor() ?
-                            SystemConfig.getInstance()
-                                    .getVendorPrivAppDenyPermissions(pkg.packageName)
-                            : SystemConfig.getInstance()
-                                    .getPrivAppDenyPermissions(pkg.packageName);
+                    ArraySet<String> deniedPermissions = null;
+                    if (pkg.isVendor()) {
+                        deniedPermissions = SystemConfig.getInstance()
+                                .getVendorPrivAppDenyPermissions(pkg.packageName);
+                    } else if (pkg.isProduct()) {
+                        deniedPermissions = SystemConfig.getInstance()
+                                .getProductPrivAppDenyPermissions(pkg.packageName);
+                    } else {
+                        deniedPermissions = SystemConfig.getInstance()
+                                .getPrivAppDenyPermissions(pkg.packageName);
+                    }
                     final boolean permissionViolation =
                             deniedPermissions == null || !deniedPermissions.contains(perm);
                     if (permissionViolation) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index cf36166..db83158 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3763,12 +3763,8 @@
                 proto.write(PowerManagerServiceDumpProto.UidStateProto.UID_STRING, UserHandle.formatUid(uid));
                 proto.write(PowerManagerServiceDumpProto.UidStateProto.IS_ACTIVE, state.mActive);
                 proto.write(PowerManagerServiceDumpProto.UidStateProto.NUM_WAKE_LOCKS, state.mNumWakeLocks);
-                if (state.mProcState == ActivityManager.PROCESS_STATE_UNKNOWN) {
-                    proto.write(PowerManagerServiceDumpProto.UidStateProto.IS_PROCESS_STATE_UNKNOWN, true);
-                } else {
-                    proto.write(PowerManagerServiceDumpProto.UidStateProto.PROCESS_STATE,
-                            ActivityManager.processStateAmToProto(state.mProcState));
-                }
+                proto.write(PowerManagerServiceDumpProto.UidStateProto.PROCESS_STATE,
+                        ActivityManager.processStateAmToProto(state.mProcState));
                 proto.end(uIDToken);
             }
 
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdater.java b/services/core/java/com/android/server/webkit/WebViewUpdater.java
index 7e05e46..61a3c09 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdater.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdater.java
@@ -507,22 +507,16 @@
         if (systemInterface.systemIsDebuggable()) {
             return true;
         }
-        Signature[] packageSignatures;
         // If no signature is declared, instead check whether the package is included in the
         // system.
         if (provider.signatures == null || provider.signatures.length == 0) {
             return packageInfo.applicationInfo.isSystemApp();
         }
-        packageSignatures = packageInfo.signatures;
-        if (packageSignatures.length != 1)
-            return false;
+        if (packageInfo.signatures.length != 1) return false;
 
-        final byte[] packageSignature = packageSignatures[0].toByteArray();
         // Return whether the package signature matches any of the valid signatures
-        for (String signature : provider.signatures) {
-            final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
-            if (Arrays.equals(packageSignature, validSignature))
-                return true;
+        for (Signature signature : provider.signatures) {
+            if (signature.equals(packageInfo.signatures[0])) return true;
         }
         return false;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6dc384a..3f49f0c 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1508,6 +1508,10 @@
         return mTaskStackContainers.getTopStack();
     }
 
+    ArrayList<Task> getVisibleTasks() {
+        return mTaskStackContainers.getVisibleTasks();
+    }
+
     void onStackWindowingModeChanged(TaskStack stack) {
         mTaskStackContainers.onStackWindowingModeChanged(stack);
     }
@@ -1802,6 +1806,11 @@
         getParent().positionChildAt(position, this, includingParents);
     }
 
+    void positionStackAt(int position, TaskStack child) {
+        mTaskStackContainers.positionChildAt(position, child, false /* includingParents */);
+        layoutAndAssignWindowLayersIfNeeded();
+    }
+
     int taskIdFromPoint(int x, int y) {
         for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
             final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
@@ -3255,6 +3264,16 @@
             return mSplitScreenPrimaryStack;
         }
 
+        ArrayList<Task> getVisibleTasks() {
+            final ArrayList<Task> visibleTasks = new ArrayList<>();
+            forAllTasks(task -> {
+                if (task.isVisible()) {
+                    visibleTasks.add(task);
+                }
+            });
+            return visibleTasks;
+        }
+
         /**
          * Adds the stack to this container.
          * @see DisplayContent#createStack(int, boolean, StackWindowController)
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
new file mode 100644
index 0000000..ad4957e
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.content.res.Configuration;
+import android.util.Slog;
+
+/**
+ * Controller for the display container. This is created by activity manager to link activity
+ * displays to the display content they use in window manager.
+ */
+public class DisplayWindowController
+        extends WindowContainerController<DisplayContent, WindowContainerListener> {
+
+    private final int mDisplayId;
+
+    public DisplayWindowController(int displayId, WindowContainerListener listener) {
+        super(listener, WindowManagerService.getInstance());
+        mDisplayId = displayId;
+
+        synchronized (mWindowMap) {
+            // TODO: Convert to setContainer() from DisplayContent once everything is hooked up.
+            // Currently we are not setup to register for config changes.
+            mContainer = mRoot.getDisplayContentOrCreate(displayId);
+            if (mContainer == null) {
+                throw new IllegalArgumentException("Trying to add displayId=" + displayId);
+            }
+        }
+    }
+
+    @Override
+    public void removeContainer() {
+        // TODO: Pipe through from ActivityDisplay to remove the display
+        throw new UnsupportedOperationException("To be implemented");
+    }
+
+    @Override
+    public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+        // TODO: Pipe through from ActivityDisplay to update the configuration for the display
+        throw new UnsupportedOperationException("To be implemented");
+    }
+
+    /**
+     * Positions the task stack at the given position in the task stack container.
+     */
+    public void positionChildAt(StackWindowController child, int position) {
+        synchronized (mWindowMap) {
+            if (DEBUG_STACK) Slog.i(TAG_WM, "positionTaskStackAt: positioning stack=" + child
+                    + " at " + position);
+            if (mContainer == null) {
+                if (DEBUG_STACK) Slog.i(TAG_WM,
+                        "positionTaskStackAt: could not find display=" + mContainer);
+                return;
+            }
+            if (child.mContainer == null) {
+                if (DEBUG_STACK) Slog.i(TAG_WM,
+                        "positionTaskStackAt: could not find stack=" + this);
+                return;
+            }
+            mContainer.positionStackAt(position, child.mContainer);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "{DisplayWindowController displayId=" + mDisplayId + "}";
+    }
+}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 88b7a11..281e0a8 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -19,6 +19,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
@@ -86,6 +87,7 @@
     private boolean mAddInputConsumerHandle;
     private boolean mAddPipInputConsumerHandle;
     private boolean mAddWallpaperInputConsumerHandle;
+    private boolean mAddRecentsAnimationInputConsumerHandle;
     private boolean mDisableWallpaperTouchEvents;
     private final Rect mTmpRect = new Rect();
     private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer =
@@ -612,7 +614,7 @@
         InputConsumerImpl navInputConsumer;
         InputConsumerImpl pipInputConsumer;
         InputConsumerImpl wallpaperInputConsumer;
-        Rect pipTouchableBounds;
+        InputConsumerImpl recentsAnimationInputConsumer;
         boolean inDrag;
         WallpaperController wallpaperController;
 
@@ -622,11 +624,13 @@
             navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION, DEFAULT_DISPLAY);
             pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP, DEFAULT_DISPLAY);
             wallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER, DEFAULT_DISPLAY);
+            recentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION,
+                    DEFAULT_DISPLAY);
             mAddInputConsumerHandle = navInputConsumer != null;
             mAddPipInputConsumerHandle = pipInputConsumer != null;
             mAddWallpaperInputConsumerHandle = wallpaperInputConsumer != null;
+            mAddRecentsAnimationInputConsumerHandle = recentsAnimationInputConsumer != null;
             mTmpRect.setEmpty();
-            pipTouchableBounds = mAddPipInputConsumerHandle ? mTmpRect : null;
             mDisableWallpaperTouchEvents = false;
             this.inDrag = inDrag;
             wallpaperController = mService.mRoot.mWallpaperController;
@@ -659,12 +663,28 @@
             final boolean hasFocus = w == mInputFocus;
             final boolean isVisible = w.isVisibleLw();
 
+            if (mAddRecentsAnimationInputConsumerHandle) {
+                final RecentsAnimationController recentsAnimationController =
+                        mService.getRecentsAnimationController();
+                if (recentsAnimationController != null
+                        && recentsAnimationController.hasInputConsumerForApp(w.mAppToken)) {
+                    if (recentsAnimationController.updateInputConsumerForApp(
+                            recentsAnimationInputConsumer, hasFocus)) {
+                        addInputWindowHandle(recentsAnimationInputConsumer.mWindowHandle);
+                        mAddRecentsAnimationInputConsumerHandle = false;
+                    }
+                    // Skip adding the window below regardless of whether there is an input consumer
+                    // to handle it
+                    return;
+                }
+            }
+
             if (w.inPinnedWindowingMode()) {
                 if (mAddPipInputConsumerHandle
                         && (inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer)) {
                     // Update the bounds of the Pip input consumer to match the window bounds.
-                    w.getBounds(pipTouchableBounds);
-                    pipInputConsumer.mWindowHandle.touchableRegion.set(pipTouchableBounds);
+                    w.getBounds(mTmpRect);
+                    pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
                     addInputWindowHandle(pipInputConsumer.mWindowHandle);
                     mAddPipInputConsumerHandle = false;
                 }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
new file mode 100644
index 0000000..c7d4b8e
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.TaskSnapshot;
+import android.app.WindowConfiguration;
+import android.graphics.GraphicBuffer;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Slog;
+import android.view.IRecentsAnimationController;
+import android.view.IRecentsAnimationRunner;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Controls a single instance of the remote driven recents animation. In particular, this allows
+ * the calling SystemUI to animate the visible task windows as a part of the transition. The remote
+ * runner is provided an animation controller which allows it to take screenshots and to notify
+ * window manager when the animation is completed. In addition, window manager may also notify the
+ * app if it requires the animation to be canceled at any time (ie. due to timeout, etc.)
+ */
+public class RecentsAnimationController {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentsAnimationController" : TAG_WM;
+    private static final boolean DEBUG = false;
+
+    private final WindowManagerService mService;
+    private final IRecentsAnimationRunner mRunner;
+    private final RecentsAnimationCallbacks mCallbacks;
+    private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
+
+    // The recents component app token that is shown behind the visibile tasks
+    private AppWindowToken mHomeAppToken;
+
+    // We start the RecentsAnimationController in a pending-start state since we need to wait for
+    // the wallpaper/activity to draw before we can give control to the handler to start animating
+    // the visible task surfaces
+    private boolean mPendingStart = true;
+
+    // Set when the animation has been canceled
+    private boolean mCanceled = false;
+
+    // Whether or not the input consumer is enabled. The input consumer must be both registered and
+    // enabled for it to start intercepting touch events.
+    private boolean mInputConsumerEnabled;
+
+    private Rect mTmpRect = new Rect();
+
+    public interface RecentsAnimationCallbacks {
+        void onAnimationFinished(boolean moveHomeToTop);
+    }
+
+    private final IRecentsAnimationController mController =
+            new IRecentsAnimationController.Stub() {
+
+        @Override
+        public TaskSnapshot screenshotTask(int taskId) {
+            if (DEBUG) Log.d(TAG, "screenshotTask(" + taskId + "): mCanceled=" + mCanceled);
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mService.getWindowManagerLock()) {
+                    if (mCanceled) {
+                        return null;
+                    }
+                    for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+                        final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
+                        final Task task = adapter.mTask;
+                        if (task.mTaskId == taskId) {
+                            // TODO: Save this screenshot as the task snapshot?
+                            final Rect taskFrame = new Rect();
+                            task.getBounds(taskFrame);
+                            final GraphicBuffer buffer = SurfaceControl.captureLayers(
+                                    task.getSurfaceControl().getHandle(), taskFrame, 1f);
+                            final AppWindowToken topChild = task.getTopChild();
+                            final WindowState mainWindow = topChild.findMainWindow();
+                            return new TaskSnapshot(buffer, topChild.getConfiguration().orientation,
+                                    mainWindow.mStableInsets,
+                                    ActivityManager.isLowRamDeviceStatic() /* reduced */,
+                                    1.0f /* scale */);
+                        }
+                    }
+                    return null;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void finish(boolean moveHomeToTop) {
+            if (DEBUG) Log.d(TAG, "finish(" + moveHomeToTop + "): mCanceled=" + mCanceled);
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mService.getWindowManagerLock()) {
+                    if (mCanceled) {
+                        return;
+                    }
+                }
+
+                // Note, the callback will handle its own synchronization, do not lock on WM lock
+                // prior to calling the callback
+                mCallbacks.onAnimationFinished(moveHomeToTop);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setInputConsumerEnabled(boolean enabled) {
+            if (DEBUG) Log.d(TAG, "setInputConsumerEnabled(" + enabled + "): mCanceled="
+                    + mCanceled);
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mService.getWindowManagerLock()) {
+                    if (mCanceled) {
+                        return;
+                    }
+
+                    mInputConsumerEnabled = enabled;
+                    mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+                    mService.scheduleAnimationLocked();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    };
+
+    /**
+     * Initializes a new RecentsAnimationController.
+     *
+     * @param remoteAnimationRunner The remote runner which should be notified when the animation is
+     *                              ready to start or has been canceled
+     * @param callbacks Callbacks to be made when the animation finishes
+     * @param restoreHomeBehindStackId The stack id to restore the home stack behind once the
+     *                                 animation is complete. Will be passed to the callback.
+     */
+    RecentsAnimationController(WindowManagerService service,
+            IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks,
+            int displayId) {
+        mService = service;
+        mRunner = remoteAnimationRunner;
+        mCallbacks = callbacks;
+
+        final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+        final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
+        if (visibleTasks.isEmpty()) {
+            cancelAnimation();
+            return;
+        }
+
+        // Make leashes for each of the visible tasks and add it to the recents animation to be
+        // started
+        final int taskCount = visibleTasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            final Task task = visibleTasks.get(i);
+            final WindowConfiguration config = task.getWindowConfiguration();
+            if (config.tasksAreFloating()
+                    || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                    || config.getActivityType() == ACTIVITY_TYPE_HOME) {
+                continue;
+            }
+            addAnimation(task);
+        }
+
+        // Adjust the wallpaper visibility for the showing home activity
+        final AppWindowToken recentsComponentAppToken =
+                dc.getHomeStack().getTopChild().getTopFullscreenAppToken();
+        if (recentsComponentAppToken != null) {
+            if (DEBUG) Log.d(TAG, "setHomeApp(" + recentsComponentAppToken.getName() + ")");
+            mHomeAppToken = recentsComponentAppToken;
+            final WallpaperController wc = dc.mWallpaperController;
+            if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) {
+                dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+                dc.setLayoutNeeded();
+            }
+        }
+
+        mService.mWindowPlacerLocked.performSurfacePlacement();
+    }
+
+    private void addAnimation(Task task) {
+        if (DEBUG) Log.d(TAG, "addAnimation(" + task.getName() + ")");
+        final SurfaceAnimator anim = new SurfaceAnimator(task, null /* animationFinishedCallback */,
+                mService.mAnimator::addAfterPrepareSurfacesRunnable, mService);
+        final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task);
+        anim.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
+        task.commitPendingTransaction();
+        mPendingAnimations.add(taskAdapter);
+    }
+
+    void startAnimation() {
+        if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart);
+        if (!mPendingStart) {
+            return;
+        }
+        try {
+            final RemoteAnimationTarget[] appAnimations =
+                    new RemoteAnimationTarget[mPendingAnimations.size()];
+            for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+                appAnimations[i] = mPendingAnimations.get(i).createRemoteAnimationApp();
+            }
+            mPendingStart = false;
+            mRunner.onAnimationStart(mController, appAnimations);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to start recents animation", e);
+        }
+    }
+
+    void cancelAnimation() {
+        if (DEBUG) Log.d(TAG, "cancelAnimation()");
+        if (mCanceled) {
+            // We've already canceled the animation
+            return;
+        }
+        mCanceled = true;
+        try {
+            mRunner.onAnimationCanceled();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to cancel recents animation", e);
+        }
+
+        // Clean up and return to the previous app
+        mCallbacks.onAnimationFinished(false /* moveHomeToTop */);
+    }
+
+    void cleanupAnimation() {
+        if (DEBUG) Log.d(TAG, "cleanupAnimation(): mPendingAnimations="
+                + mPendingAnimations.size());
+        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+            final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
+            adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
+        }
+        mPendingAnimations.clear();
+
+        mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+        mService.scheduleAnimationLocked();
+        mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+    }
+
+    void checkAnimationReady(WallpaperController wallpaperController) {
+        if (mPendingStart) {
+            final boolean wallpaperReady = !isHomeAppOverWallpaper()
+                    || (wallpaperController.getWallpaperTarget() != null
+                            && wallpaperController.wallpaperTransitionReady());
+            if (wallpaperReady) {
+                mService.getRecentsAnimationController().startAnimation();
+            }
+        }
+    }
+
+    boolean isWallpaperVisible(WindowState w) {
+        return w != null && w.mAppToken != null && mHomeAppToken == w.mAppToken
+                && isHomeAppOverWallpaper();
+    }
+
+    boolean hasInputConsumerForApp(AppWindowToken appToken) {
+        return mInputConsumerEnabled && isAnimatingApp(appToken);
+    }
+
+    boolean updateInputConsumerForApp(InputConsumerImpl recentsAnimationInputConsumer,
+            boolean hasFocus) {
+        // Update the input consumer touchable region to match the home app main window
+        final WindowState homeAppMainWindow = mHomeAppToken != null
+                ? mHomeAppToken.findMainWindow()
+                : null;
+        if (homeAppMainWindow != null) {
+            homeAppMainWindow.getBounds(mTmpRect);
+            recentsAnimationInputConsumer.mWindowHandle.hasFocus = hasFocus;
+            recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isHomeAppOverWallpaper() {
+        if (mHomeAppToken == null) {
+            return false;
+        }
+        return mHomeAppToken.windowsCanBeWallpaperTarget();
+    }
+
+    private boolean isAnimatingApp(AppWindowToken appToken) {
+        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+            final Task task = mPendingAnimations.get(i).mTask;
+            for (int j = task.getChildCount() - 1; j >= 0; j--) {
+                final AppWindowToken app = task.getChildAt(j);
+                if (app == appToken) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private class TaskAnimationAdapter implements AnimationAdapter {
+
+        private Task mTask;
+        private SurfaceControl mCapturedLeash;
+        private OnAnimationFinishedCallback mCapturedFinishCallback;
+
+        TaskAnimationAdapter(Task task) {
+            mTask = task;
+        }
+
+        RemoteAnimationTarget createRemoteAnimationApp() {
+            // TODO: Do we need position and stack bounds?
+            return new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
+                    !mTask.fillsParent(),
+                    mTask.getTopVisibleAppMainWindow().mWinAnimator.mLastClipRect,
+                    mTask.getPrefixOrderIndex(), new Point(), new Rect(),
+                    mTask.getWindowConfiguration());
+        }
+
+        @Override
+        public boolean getDetachWallpaper() {
+            return false;
+        }
+
+        @Override
+        public int getBackgroundColor() {
+            return 0;
+        }
+
+        @Override
+        public void startAnimation(SurfaceControl animationLeash, Transaction t,
+                OnAnimationFinishedCallback finishCallback) {
+            mCapturedLeash = animationLeash;
+            mCapturedFinishCallback = finishCallback;
+        }
+
+        @Override
+        public void onAnimationCancelled(SurfaceControl animationLeash) {
+            cancelAnimation();
+        }
+
+        @Override
+        public long getDurationHint() {
+            return 0;
+        }
+
+        @Override
+        public long getStatusBarTransitionsStartTime() {
+            return SystemClock.uptimeMillis();
+        }
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
+        pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
+        pw.print(innerPrefix); pw.println("mHomeAppToken=" + mHomeAppToken);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 8515dcb..7d4eafb 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -160,7 +160,8 @@
             return new RemoteAnimationTarget(task.mTaskId, getMode(),
                     mCapturedLeash, !mAppWindowToken.fillsParent(),
                     mainWindow.mWinAnimator.mLastClipRect,
-                    mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds);
+                    mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
+                    task.getWindowConfiguration());
         }
 
         private int getMode() {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2cc96c9..deed7f1 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -623,6 +623,13 @@
                         defaultDisplay.pendingLayoutChanges);
         }
 
+        // Defer starting the recents animation until the wallpaper has drawn
+        final RecentsAnimationController recentsAnimationController =
+            mService.getRecentsAnimationController();
+        if (recentsAnimationController != null) {
+            recentsAnimationController.checkAnimationReady(mWallpaperController);
+        }
+
         if (mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0
                 && !mService.mAppTransition.isReady()) {
             // At this point, there was a window with a wallpaper that was force hiding other
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 10f1c3a..0512a08 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -62,7 +62,7 @@
      * @param addAfterPrepareSurfaces Consumer that takes a runnable and executes it after preparing
      *                                surfaces in WM. Can be implemented differently during testing.
      */
-    SurfaceAnimator(Animatable animatable, Runnable animationFinishedCallback,
+    SurfaceAnimator(Animatable animatable, @Nullable Runnable animationFinishedCallback,
             Consumer<Runnable> addAfterPrepareSurfaces, WindowManagerService service) {
         mAnimatable = animatable;
         mService = service;
@@ -71,7 +71,8 @@
                 addAfterPrepareSurfaces);
     }
 
-    private OnAnimationFinishedCallback getFinishedCallback(Runnable animationFinishedCallback,
+    private OnAnimationFinishedCallback getFinishedCallback(
+            @Nullable Runnable animationFinishedCallback,
             Consumer<Runnable> addAfterPrepareSurfaces) {
         return anim -> {
             synchronized (mService.mWindowMap) {
@@ -97,7 +98,9 @@
                     SurfaceControl.openTransaction();
                     try {
                         reset(t, true /* destroyLeash */);
-                        animationFinishedCallback.run();
+                        if (animationFinishedCallback != null) {
+                            animationFinishedCallback.run();
+                        }
                     } finally {
                         SurfaceControl.mergeToGlobalTransaction(t);
                         SurfaceControl.closeTransaction();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index f79719c..212a0d70 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -223,6 +223,27 @@
             return null;
         }
 
+        if (top.hasCommittedReparentToAnimationLeash()) {
+            if (DEBUG_SCREENSHOT) {
+                Slog.w(TAG_WM, "Failed to take screenshot. App is animating " + top);
+            }
+            return null;
+        }
+
+        final boolean hasVisibleChild = top.forAllWindows(
+                // Ensure at least one window for the top app is visible before attempting to take
+                // a screenshot. Visible here means that the WSA surface is shown and has an alpha
+                // greater than 0.
+                ws -> ws.mWinAnimator != null && ws.mWinAnimator.getShown()
+                        && ws.mWinAnimator.mLastAlpha > 0f, true);
+
+        if (!hasVisibleChild) {
+            if (DEBUG_SCREENSHOT) {
+                Slog.w(TAG_WM, "Failed to take screenshot. No visible windows for " + task);
+            }
+            return null;
+        }
+
         final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
         final float scaleFraction = isLowRamDevice ? REDUCED_SCALE : 1f;
         task.getBounds(mTmpRect);
@@ -233,7 +254,7 @@
 
         if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
             if (DEBUG_SCREENSHOT) {
-                Slog.w(TAG_WM, "Failed to take screenshot");
+                Slog.w(TAG_WM, "Failed to take screenshot for " + task);
             }
             return null;
         }
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 1218d3b..f2ad6fb 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -149,8 +149,17 @@
             mFindResults.setUseTopWallpaperAsTarget(true);
         }
 
+        final RecentsAnimationController recentsAnimationController =
+                mService.getRecentsAnimationController();
         final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
-        if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
+        final boolean isRecentsTransitionTarget = (recentsAnimationController != null
+                && recentsAnimationController.isWallpaperVisible(w));
+        if (isRecentsTransitionTarget) {
+            if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w);
+            mFindResults.setWallpaperTarget(w);
+            return true;
+        } else if (hasWallpaper && w.isOnScreen()
+                && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
             if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w);
             mFindResults.setWallpaperTarget(w);
             if (w == mWallpaperTarget && w.mWinAnimator.isAnimationSet()) {
@@ -199,15 +208,22 @@
         }
     }
 
-    private boolean isWallpaperVisible(WindowState wallpaperTarget) {
+    private final boolean isWallpaperVisible(WindowState wallpaperTarget) {
+        final RecentsAnimationController recentsAnimationController =
+                mService.getRecentsAnimationController();
+        boolean isAnimatingWithRecentsComponent = recentsAnimationController != null
+                && recentsAnimationController.isWallpaperVisible(wallpaperTarget);
         if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
                 + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
                 + " animating=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null)
                 ? wallpaperTarget.mAppToken.isSelfAnimating() : null)
-                + " prev=" + mPrevWallpaperTarget);
+                + " prev=" + mPrevWallpaperTarget
+                + " recentsAnimationWallpaperVisible=" + isAnimatingWithRecentsComponent);
         return (wallpaperTarget != null
-                && (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null
-                && wallpaperTarget.mAppToken.isSelfAnimating())))
+                && (!wallpaperTarget.mObscured
+                        || isAnimatingWithRecentsComponent
+                        || (wallpaperTarget.mAppToken != null
+                                && wallpaperTarget.mAppToken.isSelfAnimating())))
                 || mPrevWallpaperTarget != null;
     }
 
@@ -587,6 +603,11 @@
             mWallpaperDrawState = WALLPAPER_DRAW_TIMEOUT;
             if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
                     "*** WALLPAPER DRAW TIMEOUT");
+
+            // If there was a recents animation in progress, cancel that animation
+            if (mService.getRecentsAnimationController() != null) {
+                mService.getRecentsAnimationController().cancelAnimation();
+            }
             return true;
         }
         return false;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 42c6ec2..1f9255a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -100,6 +100,12 @@
     /** Total number of elements in this subtree, including our own hierarchy element. */
     private int mTreeWeight = 1;
 
+    /**
+     * Indicates whether we are animating and have committed the transaction to reparent our 
+     * surface to the animation leash
+     */
+    private boolean mCommittedReparentToAnimationLeash;
+
     WindowContainer(WindowManagerService service) {
         mService = service;
         mPendingTransaction = service.mTransactionFactory.make();
@@ -337,9 +343,9 @@
     }
 
     /** Returns true if this window container has the input child. */
-    boolean hasChild(WindowContainer child) {
+    boolean hasChild(E child) {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowContainer current = mChildren.get(i);
+            final E current = mChildren.get(i);
             if (current == child || current.hasChild(child)) {
                 return true;
             }
@@ -1025,12 +1031,24 @@
      */
     void prepareSurfaces() {
         SurfaceControl.mergeToGlobalTransaction(getPendingTransaction());
+
+        // If a leash has been set when the transaction was committed, then the leash reparent has
+        // been committed.
+        mCommittedReparentToAnimationLeash = mSurfaceAnimator.hasLeash();
         for (int i = 0; i < mChildren.size(); i++) {
             mChildren.get(i).prepareSurfaces();
         }
     }
 
     /**
+     * @return true if the reparent to animation leash transaction has been committed, false
+     * otherwise.
+     */
+    boolean hasCommittedReparentToAnimationLeash() {
+        return mCommittedReparentToAnimationLeash;
+    }
+
+    /**
      * Trigger a call to prepareSurfaces from the animation thread, such that
      * mPendingTransaction will be applied.
      */
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index de1e7ec..4fb2390 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -24,6 +24,8 @@
 import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.StatusBarManager.DISABLE_MASK;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_USER_HANDLE;
@@ -123,6 +125,7 @@
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.IAssistDataReceiver;
+import android.app.WindowConfiguration;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -196,6 +199,7 @@
 import android.view.IInputFilter;
 import android.view.IOnKeyguardExitResult;
 import android.view.IPinnedStackListener;
+import android.view.IRecentsAnimationRunner;
 import android.view.IRotationWatcher;
 import android.view.IWallpaperVisibilityListener;
 import android.view.IWindow;
@@ -528,6 +532,7 @@
     IInputMethodManager mInputMethodManager;
 
     AccessibilityController mAccessibilityController;
+    private RecentsAnimationController mRecentsAnimationController;
 
     Watermark mWatermark;
     StrictModeFlash mStrictModeFlash;
@@ -2670,6 +2675,39 @@
         }
     }
 
+    public void initializeRecentsAnimation(
+            IRecentsAnimationRunner recentsAnimationRunner,
+            RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId) {
+        synchronized (mWindowMap) {
+            cancelRecentsAnimation();
+            mRecentsAnimationController = new RecentsAnimationController(this,
+                    recentsAnimationRunner, callbacks, displayId);
+        }
+    }
+
+    public RecentsAnimationController getRecentsAnimationController() {
+        return mRecentsAnimationController;
+    }
+
+    public void cancelRecentsAnimation() {
+        synchronized (mWindowMap) {
+            if (mRecentsAnimationController != null) {
+                // This call will call through to cleanupAnimation() below after the animation is
+                // canceled
+                mRecentsAnimationController.cancelAnimation();
+            }
+        }
+    }
+
+    public void cleanupRecentsAnimation() {
+        synchronized (mWindowMap) {
+            if (mRecentsAnimationController != null) {
+                mRecentsAnimationController.cleanupAnimation();
+                mRecentsAnimationController = null;
+            }
+        }
+    }
+
     public void setAppFullscreen(IBinder token, boolean toOpaque) {
         synchronized (mWindowMap) {
             final AppWindowToken atoken = mRoot.getAppWindowToken(token);
@@ -6327,6 +6365,10 @@
             pw.print("  mSkipAppTransitionAnimation=");pw.println(mSkipAppTransitionAnimation);
             pw.println("  mLayoutToAnim:");
             mAppTransition.dump(pw, "    ");
+            if (mRecentsAnimationController != null) {
+                pw.print("  mRecentsAnimationController="); pw.println(mRecentsAnimationController);
+                mRecentsAnimationController.dump(pw, "    ");
+            }
         }
     }
 
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 67bad0f..8fd5be2 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -358,6 +358,7 @@
     Return<void> gnssAcquireWakelockCb() override;
     Return<void> gnssReleaseWakelockCb() override;
     Return<void> gnssRequestTimeCb() override;
+    Return<void> gnssRequestLocationCb(const bool independentFromGnss) override;
     Return<void> gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) override;
 
     // New in 1.1
@@ -472,6 +473,11 @@
     return Void();
 }
 
+Return<void> GnssCallback::gnssRequestLocationCb(const bool independentFromGnss) {
+    // TODO(b/72405645): call into java implementation
+    return Void();
+}
+
 Return<void> GnssCallback::gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) {
     ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw);
 
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 96bf49b..10253c5 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.spy;
 
+import com.android.server.wm.DisplayWindowController;
 import org.mockito.invocation.InvocationOnMock;
 
 import android.app.IApplicationThread;
@@ -345,7 +346,7 @@
         }
     }
 
-    private static class TestActivityDisplay extends ActivityDisplay {
+    protected static class TestActivityDisplay extends ActivityDisplay {
 
         private final ActivityStackSupervisor mSupervisor;
         TestActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
@@ -374,6 +375,11 @@
                         this, stackId, mSupervisor, windowingMode, activityType, onTop);
             }
         }
+
+        @Override
+        protected DisplayWindowController createWindowContainerController() {
+            return mock(DisplayWindowController.class);
+        }
     }
 
     private static WindowManagerService prepareMockWindowManager() {
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 5a21102..24566fc 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -573,7 +573,8 @@
         assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
         assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
         assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null,
-                null, 0));
+                null));
+        assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation());
     }
 
     private void testGetTasksApis(boolean expectCallable) {
@@ -676,8 +677,8 @@
         @Override
         public void initialize() {
             super.initialize();
-            mDisplay = new ActivityDisplay(this, DEFAULT_DISPLAY);
-            mOtherDisplay = new ActivityDisplay(this, DEFAULT_DISPLAY);
+            mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
+            mOtherDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
             attachDisplay(mOtherDisplay);
             attachDisplay(mDisplay);
         }
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
index fc75628..c6ce7e1 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
@@ -68,7 +68,7 @@
         // Create a number of stacks with tasks (of incrementing active time)
         final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
         final SparseArray<ActivityDisplay> displays = new SparseArray<>();
-        final ActivityDisplay display = new ActivityDisplay(supervisor, DEFAULT_DISPLAY);
+        final ActivityDisplay display = new TestActivityDisplay(supervisor, DEFAULT_DISPLAY);
         displays.put(DEFAULT_DISPLAY, display);
 
         final int numStacks = 2;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index b1bff70..6fc9e08 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -48,6 +49,7 @@
 
 import java.io.File;
 import java.security.KeyStore;
+import java.security.UnrecoverableKeyException;
 import java.util.List;
 
 @SmallTest
@@ -258,6 +260,40 @@
     }
 
     @Test
+    public void getEncryptKey_generatesNewKeyIfOldOneIsInvalid() throws Exception {
+        doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey(
+                eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
+                any());
+
+        mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
+
+        verify(mKeyStoreProxy).getKey(
+                eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
+                any());
+        // Attempt to get regenerated key.
+        verify(mKeyStoreProxy).getKey(
+                eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"),
+                any());
+    }
+
+    @Test
+    public void getDecryptKey_generatesNewKeyIfOldOneIsInvalid() throws Exception {
+        doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey(
+                eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+                any());
+
+        mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
+
+        verify(mKeyStoreProxy).getKey(
+                eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+                any());
+        // Attempt to get regenerated key.
+        verify(mKeyStoreProxy).getKey(
+                eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+                any());
+    }
+
+    @Test
     public void getEncryptKey_getsDecryptKeyWithCorrectAlias() throws Exception {
         mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index f0254c6..097d214 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -315,6 +315,29 @@
         assertThat(statuses).hasSize(0);
     }
 
+    public void testInvalidateKeysWithOldGenerationId_withSingleKey() {
+        int userId = 12;
+        int uid = 1009;
+        int generationId = 6;
+        int status = 120;
+        int status2 = 121;
+        String alias = "test";
+        byte[] nonce = getUtf8Bytes("nonce");
+        byte[] keyMaterial = getUtf8Bytes("keymaterial");
+        WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId, status);
+        mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
+
+        WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
+        assertThat(retrievedKey.getRecoveryStatus()).isEqualTo(status);
+
+        mRecoverableKeyStoreDb.setRecoveryStatus(uid, alias, status2);
+        mRecoverableKeyStoreDb.invalidateKeysWithOldGenerationId(userId, generationId + 1);
+
+        retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias);
+        assertThat(retrievedKey.getRecoveryStatus())
+                .isEqualTo(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+    }
+
     @Test
     public void setRecoveryServicePublicKey_replaceOldKey() throws Exception {
         int userId = 12;
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index 88bae33..a1a6a5a 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -110,6 +110,9 @@
     /** Result code of execution with no error. */
     public static final int RESULT_OK = 0;
 
+    /** Result code of an unknown error. */
+    public static final int RESULT_UNKNOWN_ERROR = -1;
+
     /**
      * Callback to receive the result of an eUICC card API.
      *
diff --git a/tests/libs-permissions/Android.mk b/tests/libs-permissions/Android.mk
new file mode 100644
index 0000000..eb38623
--- /dev/null
+++ b/tests/libs-permissions/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.android.test.libs.product
+LOCAL_PRODUCT_MODULE := true
+LOCAL_SRC_FILES := $(call all-java-files-under, product/java)
+LOCAL_REQUIRED_MODULES := com.android.test.libs.product.xml
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.android.test.libs.product.xml
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)/permissions
+LOCAL_SRC_FILES:= product/com.android.test.libs.product.xml
+include $(BUILD_PREBUILT)
diff --git a/tests/libs-permissions/product/com.android.test.libs.product.xml b/tests/libs-permissions/product/com.android.test.libs.product.xml
new file mode 100644
index 0000000..0a955e9
--- /dev/null
+++ b/tests/libs-permissions/product/com.android.test.libs.product.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<permissions>
+    <library name="com.android.test.libs.product"
+            file="/product/framework/com.android.test.libs.product.jar" />
+</permissions>
diff --git a/tests/libs-permissions/product/java/com/android/test/libs/product/LibsProductTest.java b/tests/libs-permissions/product/java/com/android/test/libs/product/LibsProductTest.java
new file mode 100644
index 0000000..f49b46e
--- /dev/null
+++ b/tests/libs-permissions/product/java/com/android/test/libs/product/LibsProductTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 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.test.libs.product;
+
+/**
+ * Test class for product libs.
+ */
+public class LibsProductTest {
+
+    /**
+     * Dummpy method for testing.
+     */
+    public static void test() {
+    }
+}
diff --git a/tests/privapp-permissions/Android.mk b/tests/privapp-permissions/Android.mk
index b001c8c..3c80ad8 100644
--- a/tests/privapp-permissions/Android.mk
+++ b/tests/privapp-permissions/Android.mk
@@ -29,3 +29,17 @@
 LOCAL_SRC_FILES:= vendor/privapp-permissions-test.xml
 include $(BUILD_PREBUILT)
 
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := ProductPrivAppPermissionTest
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_MANIFEST_FILE := product/AndroidManifest.xml
+LOCAL_PRODUCT_MODULE := true
+LOCAL_REQUIRED_MODULES := productprivapp-permissions-test.xml
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := productprivapp-permissions-test.xml
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)/permissions
+LOCAL_SRC_FILES:= product/privapp-permissions-test.xml
+include $(BUILD_PREBUILT)
diff --git a/tests/privapp-permissions/product/AndroidManifest.xml b/tests/privapp-permissions/product/AndroidManifest.xml
new file mode 100644
index 0000000..3d9415c
--- /dev/null
+++ b/tests/privapp-permissions/product/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.framework.permission.privapp.tests.product">
+
+    <!-- MANAGE_USB is signature|privileged -->
+    <uses-permission android:name="android.permission.MANAGE_USB"/>
+</manifest>
diff --git a/tests/privapp-permissions/product/privapp-permissions-test.xml b/tests/privapp-permissions/product/privapp-permissions-test.xml
new file mode 100644
index 0000000..f298f9d
--- /dev/null
+++ b/tests/privapp-permissions/product/privapp-permissions-test.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<permissions>
+    <privapp-permissions package="com.android.framework.permission.privapp.tests.product">
+        <permission name="android.permission.MANAGE_USB"/>
+    </privapp-permissions>
+</permissions>
diff --git a/tools/incident_report/main.cpp b/tools/incident_report/main.cpp
index bd1b973..302d739 100644
--- a/tools/incident_report/main.cpp
+++ b/tools/incident_report/main.cpp
@@ -452,9 +452,10 @@
     bool adbIncidentWorkaround = true;
     pid_t childPid = -1;
     vector<string> sections;
+    const char* privacy = NULL;
 
     int opt;
-    while ((opt = getopt(argc, argv, "bhi:o:s:tw")) != -1) {
+    while ((opt = getopt(argc, argv, "bhi:o:s:twp:")) != -1) {
         switch (opt) {
             case 'b':
                 outputFormat = OUTPUT_PROTO;
@@ -477,6 +478,9 @@
             case 'w':
                 adbIncidentWorkaround = false;
                 break;
+            case 'p':
+                privacy = optarg;
+                break;
             default:
                 usage(stderr);
                 return 1;
@@ -526,7 +530,7 @@
             }
 
             // TODO: This is what the real implementation will be...
-            char const** args = (char const**)malloc(sizeof(char*) * (6 + sections.size()));
+            char const** args = (char const**)malloc(sizeof(char*) * (8 + sections.size()));
             int argpos = 0;
             args[argpos++] = "adb";
             if (adbSerial != NULL) {
@@ -535,6 +539,10 @@
             }
             args[argpos++] = "shell";
             args[argpos++] = "incident";
+            if (privacy != NULL) {
+                args[argpos++] = "-p";
+                args[argpos++] = privacy;
+            }
             for (vector<string>::const_iterator it=sections.begin(); it!=sections.end(); it++) {
                 args[argpos++] = it->c_str();
             }