Merge "Update ripple according to UX feedback."
diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk
index e3f7775..2db0dd6 100644
--- a/apct-tests/perftests/multiuser/Android.mk
+++ b/apct-tests/perftests/multiuser/Android.mk
@@ -20,7 +20,8 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-test
+    android-support-test \
+    ub-uiautomator
 
 LOCAL_PACKAGE_NAME := MultiUserPerfTests
 
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
index c7bebf3..629e6f4 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -17,13 +17,16 @@
 
 import android.os.Bundle;
 import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
 
+import java.io.IOException;
 import java.util.ArrayList;
 
 // Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java
 public class BenchmarkRunner {
 
-    private static long COOL_OFF_PERIOD_MS = 2000;
+    private static final long COOL_OFF_PERIOD_MS = 1000;
 
     private static final int NUM_ITERATIONS = 4;
 
@@ -70,9 +73,13 @@
     }
 
     private void prepareForNextRun() {
-        // TODO: Once http://b/63115387 is fixed, look into using "am wait-for-broadcast-idle"
-        // command instead of waiting for a fixed amount of time.
         SystemClock.sleep(COOL_OFF_PERIOD_MS);
+        try {
+            UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+                    .executeShellCommand("am wait-for-broadcast-idle");
+        } catch (IOException e) {
+            throw new IllegalStateException("Cannot execute shell command", e);
+        }
         mStartTimeNs = System.nanoTime();
         mPausedDurationNs = 0;
     }
diff --git a/api/current.txt b/api/current.txt
index f0aa1d3..f865d14 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -37137,6 +37137,8 @@
     method public android.service.autofill.Dataset.Builder setId(java.lang.String);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
+    method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern);
+    method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
   }
 
   public final class FillCallback {
@@ -37204,8 +37206,10 @@
   }
 
   public static class ImageTransformation.Builder {
-    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
-    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+    ctor public deprecated ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int, java.lang.CharSequence);
+    method public deprecated android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int, java.lang.CharSequence);
     method public android.service.autofill.ImageTransformation build();
   }
 
@@ -37223,6 +37227,9 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
   }
 
+  public abstract interface Sanitizer {
+  }
+
   public final class SaveCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess();
@@ -37246,6 +37253,7 @@
   public static final class SaveInfo.Builder {
     ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
     ctor public SaveInfo.Builder(int);
+    method public android.service.autofill.SaveInfo.Builder addSanitizer(android.service.autofill.Sanitizer, android.view.autofill.AutofillId...);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -37264,6 +37272,13 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
+  public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+    ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.TextValueSanitizer> CREATOR;
+  }
+
   public abstract interface Transformation {
   }
 
@@ -45964,8 +45979,8 @@
     method public boolean hasTransientState();
     method public boolean hasWindowFocus();
     method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
-    method public void invalidate(android.graphics.Rect);
-    method public void invalidate(int, int, int, int);
+    method public deprecated void invalidate(android.graphics.Rect);
+    method public deprecated void invalidate(int, int, int, int);
     method public void invalidate();
     method public void invalidateDrawable(android.graphics.drawable.Drawable);
     method public void invalidateOutline();
diff --git a/api/system-current.txt b/api/system-current.txt
index 4e68265..a8b41be 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6314,6 +6314,7 @@
   }
 
   public class VrManager {
+    method public void setAndBindVrCompositor(android.content.ComponentName);
     method public void setPersistentVrModeEnabled(boolean);
   }
 
@@ -40232,6 +40233,8 @@
     method public android.service.autofill.Dataset.Builder setId(java.lang.String);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
+    method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern);
+    method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
   }
 
   public final class FillCallback {
@@ -40299,8 +40302,10 @@
   }
 
   public static class ImageTransformation.Builder {
-    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
-    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+    ctor public deprecated ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int, java.lang.CharSequence);
+    method public deprecated android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int, java.lang.CharSequence);
     method public android.service.autofill.ImageTransformation build();
   }
 
@@ -40318,6 +40323,9 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
   }
 
+  public abstract interface Sanitizer {
+  }
+
   public final class SaveCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess();
@@ -40341,6 +40349,7 @@
   public static final class SaveInfo.Builder {
     ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
     ctor public SaveInfo.Builder(int);
+    method public android.service.autofill.SaveInfo.Builder addSanitizer(android.service.autofill.Sanitizer, android.view.autofill.AutofillId...);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -40359,6 +40368,13 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
+  public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+    ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.TextValueSanitizer> CREATOR;
+  }
+
   public abstract interface Transformation {
   }
 
@@ -49661,8 +49677,8 @@
     method public boolean hasTransientState();
     method public boolean hasWindowFocus();
     method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
-    method public void invalidate(android.graphics.Rect);
-    method public void invalidate(int, int, int, int);
+    method public deprecated void invalidate(android.graphics.Rect);
+    method public deprecated void invalidate(int, int, int, int);
     method public void invalidate();
     method public void invalidateDrawable(android.graphics.drawable.Drawable);
     method public void invalidateOutline();
diff --git a/api/test-current.txt b/api/test-current.txt
index 435f3b4..38b15b4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -37426,6 +37426,8 @@
     method public android.service.autofill.Dataset.Builder setId(java.lang.String);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
+    method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern);
+    method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
   }
 
   public final class FillCallback {
@@ -37494,11 +37496,17 @@
   }
 
   public static class ImageTransformation.Builder {
-    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
-    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+    ctor public deprecated ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int, java.lang.CharSequence);
+    method public deprecated android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int, java.lang.CharSequence);
     method public android.service.autofill.ImageTransformation build();
   }
 
+  public abstract class InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+    ctor public InternalSanitizer();
+  }
+
   public final class LuhnChecksumValidator implements android.os.Parcelable android.service.autofill.Validator {
     ctor public LuhnChecksumValidator(android.view.autofill.AutofillId...);
     method public int describeContents();
@@ -37515,6 +37523,9 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
   }
 
+  public abstract interface Sanitizer {
+  }
+
   public final class SaveCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess();
@@ -37538,6 +37549,7 @@
   public static final class SaveInfo.Builder {
     ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
     ctor public SaveInfo.Builder(int);
+    method public android.service.autofill.SaveInfo.Builder addSanitizer(android.service.autofill.Sanitizer, android.view.autofill.AutofillId...);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -37556,6 +37568,14 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
+  public final class TextValueSanitizer extends android.service.autofill.InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+    ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
+    method public int describeContents();
+    method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.TextValueSanitizer> CREATOR;
+  }
+
   public abstract interface Transformation {
   }
 
@@ -46504,8 +46524,8 @@
     method public boolean hasTransientState();
     method public boolean hasWindowFocus();
     method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
-    method public void invalidate(android.graphics.Rect);
-    method public void invalidate(int, int, int, int);
+    method public deprecated void invalidate(android.graphics.Rect);
+    method public deprecated void invalidate(int, int, int, int);
     method public void invalidate();
     method public void invalidateDrawable(android.graphics.drawable.Drawable);
     method public void invalidateOutline();
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 64b9ae8..47063f0 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,8 +15,11 @@
  */
 package android.app;
 
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.NotificationManager.Importance;
+import android.content.ContentResolver;
+import android.content.Context;
 import android.content.Intent;
 import android.media.AudioAttributes;
 import android.net.Uri;
@@ -27,6 +30,8 @@
 import android.text.TextUtils;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.util.Preconditions;
+
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
@@ -569,14 +574,35 @@
     /**
      * @hide
      */
+    public void populateFromXmlForRestore(XmlPullParser parser, Context context) {
+        populateFromXml(parser, true, context);
+    }
+
+    /**
+     * @hide
+     */
     @SystemApi
     public void populateFromXml(XmlPullParser parser) {
+        populateFromXml(parser, false, null);
+    }
+
+    /**
+     * If {@param forRestore} is true, {@param Context} MUST be non-null.
+     */
+    private void populateFromXml(XmlPullParser parser, boolean forRestore,
+            @Nullable Context context) {
+        Preconditions.checkArgument(!forRestore || context != null,
+                "forRestore is true but got null context");
+
         // Name, id, and importance are set in the constructor.
         setDescription(parser.getAttributeValue(null, ATT_DESC));
         setBypassDnd(Notification.PRIORITY_DEFAULT
                 != safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT));
         setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY));
-        setSound(safeUri(parser, ATT_SOUND), safeAudioAttributes(parser));
+
+        Uri sound = safeUri(parser, ATT_SOUND);
+        setSound(forRestore ? restoreSoundUri(context, sound) : sound, safeAudioAttributes(parser));
+
         enableLights(safeBool(parser, ATT_LIGHTS, false));
         setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR));
         setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null));
@@ -588,11 +614,62 @@
         setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
     }
 
+    @Nullable
+    private Uri restoreSoundUri(Context context, @Nullable Uri uri) {
+        if (uri == null) {
+            return null;
+        }
+        ContentResolver contentResolver = context.getContentResolver();
+        // There are backups out there with uncanonical uris (because we fixed this after
+        // shipping). If uncanonical uris are given to MediaProvider.uncanonicalize it won't
+        // verify the uri against device storage and we'll possibly end up with a broken uri.
+        // We then canonicalize the uri to uncanonicalize it back, which means we properly check
+        // the uri and in the case of not having the resource we end up with the default - better
+        // than broken. As a side effect we'll canonicalize already canonicalized uris, this is fine
+        // according to the docs because canonicalize method has to handle canonical uris as well.
+        Uri canonicalizedUri = contentResolver.canonicalize(uri);
+        if (canonicalizedUri == null) {
+            // We got a null because the uri in the backup does not exist here, so we return default
+            return Settings.System.DEFAULT_NOTIFICATION_URI;
+        }
+        return contentResolver.uncanonicalize(canonicalizedUri);
+    }
+
     /**
      * @hide
      */
     @SystemApi
     public void writeXml(XmlSerializer out) throws IOException {
+        writeXml(out, false, null);
+    }
+
+    /**
+     * @hide
+     */
+    public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException {
+        writeXml(out, true, context);
+    }
+
+    private Uri getSoundForBackup(Context context) {
+        Uri sound = getSound();
+        if (sound == null) {
+            return null;
+        }
+        Uri canonicalSound = context.getContentResolver().canonicalize(sound);
+        if (canonicalSound == null) {
+            // The content provider does not support canonical uris so we backup the default
+            return Settings.System.DEFAULT_NOTIFICATION_URI;
+        }
+        return canonicalSound;
+    }
+
+    /**
+     * If {@param forBackup} is true, {@param Context} MUST be non-null.
+     */
+    private void writeXml(XmlSerializer out, boolean forBackup, @Nullable Context context)
+            throws IOException {
+        Preconditions.checkArgument(!forBackup || context != null,
+                "forBackup is true but got null context");
         out.startTag(null, TAG_CHANNEL);
         out.attribute(null, ATT_ID, getId());
         if (getName() != null) {
@@ -613,8 +690,9 @@
             out.attribute(null, ATT_VISIBILITY,
                     Integer.toString(getLockscreenVisibility()));
         }
-        if (getSound() != null) {
-            out.attribute(null, ATT_SOUND, getSound().toString());
+        Uri sound = forBackup ? getSoundForBackup(context) : getSound();
+        if (sound != null) {
+            out.attribute(null, ATT_SOUND, sound.toString());
         }
         if (getAudioAttributes() != null) {
             out.attribute(null, ATT_USAGE, Integer.toString(getAudioAttributes().getUsage()));
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 80144bd..8987bc0 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -72,13 +72,16 @@
      */
     public static final int DISABLE2_QUICK_SETTINGS = 1;
     public static final int DISABLE2_SYSTEM_ICONS = 1 << 1;
+    public static final int DISABLE2_NOTIFICATION_SHADE = 1 << 2;
 
     public static final int DISABLE2_NONE = 0x00000000;
 
-    public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS | DISABLE2_SYSTEM_ICONS;
+    public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS | DISABLE2_SYSTEM_ICONS
+            | DISABLE2_NOTIFICATION_SHADE;
 
     @IntDef(flag = true,
-            value = {DISABLE2_NONE, DISABLE2_MASK, DISABLE2_QUICK_SETTINGS, DISABLE2_SYSTEM_ICONS})
+            value = {DISABLE2_NONE, DISABLE2_MASK, DISABLE2_QUICK_SETTINGS, DISABLE2_SYSTEM_ICONS,
+                    DISABLE2_NOTIFICATION_SHADE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Disable2Flags {}
 
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index 5786238..5c6ffa3 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -4,6 +4,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.content.ComponentName;
 import android.content.Context;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -181,4 +182,20 @@
             e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Set the component name of the compositor service to bind.
+     *
+     * @param componentName ComponentName of a Service in the application's compositor process to
+     * bind to, or null to clear the current binding.
+     */
+    @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
+    public void setAndBindVrCompositor(ComponentName componentName) {
+        try {
+            mService.setAndBindCompositor(
+                    (componentName == null) ? null : componentName.flattenToString());
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9ccc552..02e70f5 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -47,6 +47,8 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.slice.Slice;
+import android.slice.SliceProvider;
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
@@ -178,6 +180,8 @@
     public static final Intent ACTION_SYNC_CONN_STATUS_CHANGED =
             new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
 
+    /** @hide */
+    public static final String SCHEME_SLICE = "slice";
     public static final String SCHEME_CONTENT = "content";
     public static final String SCHEME_ANDROID_RESOURCE = "android.resource";
     public static final String SCHEME_FILE = "file";
@@ -1718,6 +1722,36 @@
     }
 
     /**
+     * Turns a slice Uri into slice content.
+     *
+     * @param uri The URI to a slice provider
+     * @return The Slice provided by the app or null if none is given.
+     * @see Slice
+     * @hide
+     */
+    public final @Nullable Slice bindSlice(@NonNull Uri uri) {
+        Preconditions.checkNotNull(uri, "uri");
+        IContentProvider provider = acquireProvider(uri);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown URI " + uri);
+        }
+        try {
+            Bundle extras = new Bundle();
+            extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+            final Bundle res = provider.call(mPackageName, SliceProvider.METHOD_SLICE, null,
+                    extras);
+            Bundle.setDefusable(res, true);
+            return res.getParcelable(SliceProvider.EXTRA_SLICE);
+        } catch (RemoteException e) {
+            // Arbitrary and not worth documenting, as Activity
+            // Manager will kill this process shortly anyway.
+            return null;
+        } finally {
+            releaseProvider(provider);
+        }
+    }
+
+    /**
      * Returns the content provider for the given content URI.
      *
      * @param uri The URI to a content provider
@@ -1725,7 +1759,7 @@
      * @hide
      */
     public final IContentProvider acquireProvider(Uri uri) {
-        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
+        if (!SCHEME_CONTENT.equals(uri.getScheme()) && !SCHEME_SLICE.equals(uri.getScheme())) {
             return null;
         }
         final String auth = uri.getAuthority();
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 4c981cd..be7f921 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -16,6 +16,9 @@
 
 package android.content.pm;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.PackageManager.ApplicationInfoFlags;
@@ -25,6 +28,8 @@
 import android.os.Bundle;
 import android.util.SparseArray;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 /**
@@ -33,6 +38,20 @@
  * @hide Only for use within the system server.
  */
 public abstract class PackageManagerInternal {
+    public static final int PACKAGE_SYSTEM = 0;
+    public static final int PACKAGE_SETUP_WIZARD = 1;
+    public static final int PACKAGE_INSTALLER = 2;
+    public static final int PACKAGE_VERIFIER = 3;
+    public static final int PACKAGE_BROWSER = 4;
+    @IntDef(value = {
+        PACKAGE_SYSTEM,
+        PACKAGE_SETUP_WIZARD,
+        PACKAGE_INSTALLER,
+        PACKAGE_VERIFIER,
+        PACKAGE_BROWSER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface KnownPackage {}
 
     /**
      * Provider for package names.
@@ -172,6 +191,13 @@
             @ResolveInfoFlags int flags, int filterCallingUid, int userId);
 
     /**
+     * Retrieve all services that can be performed for the given intent.
+     * @see PackageManager#queryIntentServices(Intent, int)
+     */
+    public abstract List<ResolveInfo> queryIntentServices(
+            Intent intent, int flags, int callingUid, int userId);
+
+    /**
      * Interface to {@link com.android.server.pm.PackageManagerService#getHomeActivitiesAsUser}.
      */
     public abstract ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
@@ -343,14 +369,19 @@
      * Resolves an activity intent, allowing instant apps to be resolved.
      */
     public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
-            int flags, int userId);
+            int flags, int userId, boolean resolveForStart);
 
     /**
     * Resolves a service intent, allowing instant apps to be resolved.
     */
-   public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
+    public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
            int flags, int userId, int callingUid);
 
+   /**
+    * Resolves a content provider intent.
+    */
+    public abstract ProviderInfo resolveContentProvider(String name, int flags, int userId);
+
     /**
      * Track the creator of a new isolated uid.
      * @param isolatedUid The newly created isolated uid.
@@ -383,4 +414,59 @@
      * Updates a package last used time.
      */
     public abstract void notifyPackageUse(String packageName, int reason);
+
+    /**
+     * Returns a package object for the given package name.
+     */
+    public abstract @Nullable PackageParser.Package getPackage(@NonNull String packageName);
+
+    /**
+     * Returns a package object for the disabled system package name.
+     */
+    public abstract @Nullable PackageParser.Package getDisabledPackage(@NonNull String packageName);
+
+    /**
+     * Returns whether or not the component is the resolver activity.
+     */
+    public abstract boolean isResolveActivityComponent(@NonNull ComponentInfo component);
+
+    /**
+     * Returns the package name for a known package.
+     */
+    public abstract @Nullable String getKnownPackageName(
+            @KnownPackage int knownPackage, int userId);
+
+    /**
+     * Returns whether the package is an instant app.
+     */
+    public abstract boolean isInstantApp(String packageName, int userId);
+
+    /**
+     * Returns whether the package is an instant app.
+     */
+    public abstract @Nullable String getInstantAppPackageName(int uid);
+
+    /**
+     * Returns whether or not access to the application should be filtered.
+     * <p>
+     * Access may be limited based upon whether the calling or target applications
+     * are instant applications.
+     *
+     * @see #canAccessInstantApps(int)
+     */
+    public abstract boolean filterAppAccess(
+            @Nullable PackageParser.Package pkg, int callingUid, int userId);
+
+    /*
+     * NOTE: The following methods are temporary until permissions are extracted from
+     * the package manager into a component specifically for handling permissions.
+     */
+    /** Returns the flags for the given permission. */
+    public abstract @Nullable int getPermissionFlagsTEMP(@NonNull String permName,
+            @NonNull String packageName, int userId);
+    /** Updates the flags for the given permission. */
+    public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
+            @NonNull String packageName, int flagMask, int flagValues, int userId);
+    /** temporary until mPermissionTrees is moved to PermissionManager */
+    public abstract Object enforcePermissionTreeTEMP(@NonNull String permName, int callingUid);
 }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 6fbacaf..ae4f05a 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -278,6 +278,15 @@
      */
     public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 1 << 7;
 
+    /**
+     * Virtual display flag: Indicates that the contents will be destroyed once
+     * the display is removed.
+     *
+     * @see #createVirtualDisplay
+     * @hide
+     */
+    public static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8;
+
     /** @hide */
     public DisplayManager(Context context) {
         mContext = context;
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index ceccc07..632b7fc 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -34,7 +34,7 @@
     // Preventing this from being null simplifies Java->Native binder
     private String mRemoteAddress = "";
 
-    // The underlying network interface that represents the "gateway" Network
+    // The underlying Network that represents the "gateway" Network
     // for outbound packets. It may also be used to select packets.
     private Network mNetwork;
 
@@ -273,6 +273,7 @@
             };
 
     @VisibleForTesting
+    /** Equals method used for testing */
     public static boolean equals(IpSecConfig lhs, IpSecConfig rhs) {
         if (lhs == null || rhs == null) return (lhs == rhs);
         return (lhs.mMode == rhs.mMode
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 529cf1a..e15a2c6 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -409,8 +409,6 @@
         public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress)
                 throws IpSecManager.ResourceUnavailableException,
                         IpSecManager.SpiUnavailableException, IOException {
-            //FIXME: argument validation here
-            //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
             mConfig.setMode(MODE_TRANSPORT);
             mConfig.setRemoteAddress(remoteAddress.getHostAddress());
             return new IpSecTransform(mContext, mConfig).activate();
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 66b6b47..bf0a264 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -78,17 +78,17 @@
      * A constant indicating a sensor timer.
      */
     public static final int SENSOR = 3;
-    
+
     /**
      * A constant indicating a a wifi running timer
      */
     public static final int WIFI_RUNNING = 4;
-    
+
     /**
      * A constant indicating a full wifi lock timer
      */
     public static final int FULL_WIFI_LOCK = 5;
-    
+
     /**
      * A constant indicating a wifi scan
      */
@@ -217,8 +217,10 @@
      *   - Package wakeup alarms are now on screen-off timebase
      * New in version 26:
      *   - Resource power manager (rpm) states [but screenOffRpm is disabled from working properly]
+     * New in version 27:
+     *   - Always On Display (screen doze mode) time and power
      */
-    static final String CHECKIN_VERSION = "26";
+    static final String CHECKIN_VERSION = "27";
 
     /**
      * Old version, we hit 9 and ran out of room, need to remove.
@@ -1381,12 +1383,12 @@
         public static final int STATE_PHONE_SCANNING_FLAG = 1<<21;
         public static final int STATE_SCREEN_ON_FLAG = 1<<20;       // consider moving to states2
         public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19; // consider moving to states2
-        // empty slot
+        public static final int STATE_SCREEN_DOZE_FLAG = 1 << 18;
         // empty slot
         public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<16;
 
         public static final int MOST_INTERESTING_STATES =
-            STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG;
+                STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG | STATE_SCREEN_DOZE_FLAG;
 
         public static final int SETTLE_TO_ZERO_STATES = 0xffff0000 & ~MOST_INTERESTING_STATES;
 
@@ -1414,8 +1416,8 @@
         public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20;
 
         public static final int MOST_INTERESTING_STATES2 =
-            STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
-            | STATE2_CHARGING_FLAG | STATE2_PHONE_IN_CALL_FLAG | STATE2_BLUETOOTH_ON_FLAG;
+                STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
+                | STATE2_CHARGING_FLAG | STATE2_PHONE_IN_CALL_FLAG | STATE2_BLUETOOTH_ON_FLAG;
 
         public static final int SETTLE_TO_ZERO_STATES2 = 0xffff0000 & ~MOST_INTERESTING_STATES2;
 
@@ -1863,6 +1865,21 @@
      */
     public abstract int getScreenOnCount(int which);
 
+    /**
+     * Returns the time in microseconds that the screen has been dozing while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getScreenDozeTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times the screen was turned dozing.
+     *
+     * {@hide}
+     */
+    public abstract int getScreenDozeCount(int which);
+
     public abstract long getInteractiveTime(long elapsedRealtimeUs, int which);
 
     public static final int SCREEN_BRIGHTNESS_DARK = 0;
@@ -2116,8 +2133,7 @@
         "group", "compl", "dorm", "uninit"
     };
 
-    public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS
-            = new BitDescription[] {
+    public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS = new BitDescription[] {
         new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"),
         new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock", "w"),
         new BitDescription(HistoryItem.STATE_SENSOR_ON_FLAG, "sensor", "s"),
@@ -2131,6 +2147,7 @@
         new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio", "a"),
         new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"),
         new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"),
+        new BitDescription(HistoryItem.STATE_SCREEN_DOZE_FLAG, "screen_doze", "Sd"),
         new BitDescription(HistoryItem.STATE_DATA_CONNECTION_MASK,
                 HistoryItem.STATE_DATA_CONNECTION_SHIFT, "data_conn", "Pcn",
                 DATA_CONNECTION_NAMES, DATA_CONNECTION_NAMES),
@@ -2467,6 +2484,18 @@
     public abstract int getDischargeAmountScreenOffSinceCharge();
 
     /**
+     * Get the amount the battery has discharged while the screen was doze,
+     * since the last time power was unplugged.
+     */
+    public abstract int getDischargeAmountScreenDoze();
+
+    /**
+     * Get the amount the battery has discharged while the screen was doze,
+     * since the last time the device was charged.
+     */
+    public abstract int getDischargeAmountScreenDozeSinceCharge();
+
+    /**
      * Returns the total, last, or current battery uptime in microseconds.
      *
      * @param curTime the elapsed realtime in microseconds.
@@ -2483,7 +2512,7 @@
     public abstract long computeBatteryRealtime(long curTime, int which);
 
     /**
-     * Returns the total, last, or current battery screen off uptime in microseconds.
+     * Returns the total, last, or current battery screen off/doze uptime in microseconds.
      *
      * @param curTime the elapsed realtime in microseconds.
      * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
@@ -2491,7 +2520,7 @@
     public abstract long computeBatteryScreenOffUptime(long curTime, int which);
 
     /**
-     * Returns the total, last, or current battery screen off realtime in microseconds.
+     * Returns the total, last, or current battery screen off/doze realtime in microseconds.
      *
      * @param curTime the current elapsed realtime in microseconds.
      * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
@@ -2590,18 +2619,24 @@
     };
 
     /**
-     * Return the counter keeping track of the amount of battery discharge while the screen was off,
-     * measured in micro-Ampere-hours. This will be non-zero only if the device's battery has
-     * a coulomb counter.
-     */
-    public abstract LongCounter getDischargeScreenOffCoulombCounter();
-
-    /**
-     * Return the counter keeping track of the amount of battery discharge measured in
+     * Return the amount of battery discharge while the screen was off, measured in
      * micro-Ampere-hours. This will be non-zero only if the device's battery has
      * a coulomb counter.
      */
-    public abstract LongCounter getDischargeCoulombCounter();
+    public abstract long getMahDischargeScreenOff(int which);
+
+    /**
+     * Return the amount of battery discharge while the screen was in doze mode, measured in
+     * micro-Ampere-hours. This will be non-zero only if the device's battery has
+     * a coulomb counter.
+     */
+    public abstract long getMahDischargeScreenDoze(int which);
+
+    /**
+     * Return the amount of battery discharge  measured in micro-Ampere-hours. This will be
+     * non-zero only if the device's battery has a coulomb counter.
+     */
+    public abstract long getMahDischarge(int which);
 
     /**
      * Returns the estimated real battery capacity, which may be less than the capacity
@@ -3112,6 +3147,7 @@
         final long totalRealtime = computeRealtime(rawRealtime, which);
         final long totalUptime = computeUptime(rawUptime, which);
         final long screenOnTime = getScreenOnTime(rawRealtime, which);
+        final long screenDozeTime = getScreenDozeTime(rawRealtime, which);
         final long interactiveTime = getInteractiveTime(rawRealtime, which);
         final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
         final long deviceIdleModeLightTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT,
@@ -3124,9 +3160,9 @@
                 rawRealtime, which);
         final int connChanges = getNumConnectivityChange(which);
         final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
-        final long dischargeCount = getDischargeCoulombCounter().getCountLocked(which);
-        final long dischargeScreenOffCount = getDischargeScreenOffCoulombCounter()
-                .getCountLocked(which);
+        final long dischargeCount = getMahDischarge(which);
+        final long dischargeScreenOffCount = getMahDischargeScreenOff(which);
+        final long dischargeScreenDozeCount = getMahDischargeScreenDoze(which);
 
         final StringBuilder sb = new StringBuilder(128);
 
@@ -3143,7 +3179,8 @@
                 getStartClockTime(),
                 whichBatteryScreenOffRealtime / 1000, whichBatteryScreenOffUptime / 1000,
                 getEstimatedBatteryCapacity(),
-                getMinLearnedBatteryCapacity(), getMaxLearnedBatteryCapacity());
+                getMinLearnedBatteryCapacity(), getMaxLearnedBatteryCapacity(),
+                screenDozeTime / 1000);
 
 
         // Calculate wakelock times across all uids.
@@ -3295,13 +3332,15 @@
                     getDischargeStartLevel()-getDischargeCurrentLevel(),
                     getDischargeStartLevel()-getDischargeCurrentLevel(),
                     getDischargeAmountScreenOn(), getDischargeAmountScreenOff(),
-                    dischargeCount / 1000, dischargeScreenOffCount / 1000);
+                    dischargeCount / 1000, dischargeScreenOffCount / 1000,
+                    getDischargeAmountScreenDoze(), dischargeScreenDozeCount / 1000);
         } else {
             dumpLine(pw, 0 /* uid */, category, BATTERY_DISCHARGE_DATA,
                     getLowDischargeAmountSinceCharge(), getHighDischargeAmountSinceCharge(),
                     getDischargeAmountScreenOnSinceCharge(),
                     getDischargeAmountScreenOffSinceCharge(),
-                    dischargeCount / 1000, dischargeScreenOffCount / 1000);
+                    dischargeCount / 1000, dischargeScreenOffCount / 1000,
+                    getDischargeAmountScreenDozeSinceCharge(), dischargeScreenDozeCount / 1000);
         }
 
         if (reqUid < 0) {
@@ -3831,6 +3870,7 @@
                 which);
         final long batteryTimeRemaining = computeBatteryTimeRemaining(rawRealtime);
         final long chargeTimeRemaining = computeChargeTimeRemaining(rawRealtime);
+        final long screenDozeTime = getScreenDozeTime(rawRealtime, which);
 
         final StringBuilder sb = new StringBuilder(128);
 
@@ -3868,25 +3908,35 @@
 
         sb.setLength(0);
         sb.append(prefix);
-                sb.append("  Time on battery: ");
-                formatTimeMs(sb, whichBatteryRealtime / 1000); sb.append("(");
-                sb.append(formatRatioLocked(whichBatteryRealtime, totalRealtime));
-                sb.append(") realtime, ");
-                formatTimeMs(sb, whichBatteryUptime / 1000);
-                sb.append("("); sb.append(formatRatioLocked(whichBatteryUptime, totalRealtime));
-                sb.append(") uptime");
+        sb.append("  Time on battery: ");
+        formatTimeMs(sb, whichBatteryRealtime / 1000); sb.append("(");
+        sb.append(formatRatioLocked(whichBatteryRealtime, totalRealtime));
+        sb.append(") realtime, ");
+        formatTimeMs(sb, whichBatteryUptime / 1000);
+        sb.append("("); sb.append(formatRatioLocked(whichBatteryUptime, whichBatteryRealtime));
+        sb.append(") uptime");
         pw.println(sb.toString());
+
         sb.setLength(0);
         sb.append(prefix);
-                sb.append("  Time on battery screen off: ");
-                formatTimeMs(sb, whichBatteryScreenOffRealtime / 1000); sb.append("(");
-                sb.append(formatRatioLocked(whichBatteryScreenOffRealtime, totalRealtime));
-                sb.append(") realtime, ");
-                formatTimeMs(sb, whichBatteryScreenOffUptime / 1000);
-                sb.append("(");
-                sb.append(formatRatioLocked(whichBatteryScreenOffUptime, totalRealtime));
-                sb.append(") uptime");
+        sb.append("  Time on battery screen off: ");
+        formatTimeMs(sb, whichBatteryScreenOffRealtime / 1000); sb.append("(");
+        sb.append(formatRatioLocked(whichBatteryScreenOffRealtime, whichBatteryRealtime));
+        sb.append(") realtime, ");
+        formatTimeMs(sb, whichBatteryScreenOffUptime / 1000);
+        sb.append("(");
+        sb.append(formatRatioLocked(whichBatteryScreenOffUptime, whichBatteryRealtime));
+        sb.append(") uptime");
         pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  Time on battery screen doze: ");
+        formatTimeMs(sb, screenDozeTime / 1000); sb.append("(");
+        sb.append(formatRatioLocked(screenDozeTime, whichBatteryRealtime));
+        sb.append(")");
+        pw.println(sb.toString());
+
         sb.setLength(0);
         sb.append(prefix);
                 sb.append("  Total run time: ");
@@ -3910,8 +3960,7 @@
             pw.println(sb.toString());
         }
 
-        final LongCounter dischargeCounter = getDischargeCoulombCounter();
-        final long dischargeCount = dischargeCounter.getCountLocked(which);
+        final long dischargeCount = getMahDischarge(which);
         if (dischargeCount >= 0) {
             sb.setLength(0);
             sb.append(prefix);
@@ -3921,8 +3970,7 @@
             pw.println(sb.toString());
         }
 
-        final LongCounter dischargeScreenOffCounter = getDischargeScreenOffCoulombCounter();
-        final long dischargeScreenOffCount = dischargeScreenOffCounter.getCountLocked(which);
+        final long dischargeScreenOffCount = getMahDischargeScreenOff(which);
         if (dischargeScreenOffCount >= 0) {
             sb.setLength(0);
             sb.append(prefix);
@@ -3932,7 +3980,18 @@
             pw.println(sb.toString());
         }
 
-        final long dischargeScreenOnCount = dischargeCount - dischargeScreenOffCount;
+        final long dischargeScreenDozeCount = getMahDischargeScreenDoze(which);
+        if (dischargeScreenDozeCount >= 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  Screen doze discharge: ");
+            sb.append(BatteryStatsHelper.makemAh(dischargeScreenDozeCount / 1000.0));
+            sb.append(" mAh");
+            pw.println(sb.toString());
+        }
+
+        final long dischargeScreenOnCount =
+                dischargeCount - dischargeScreenOffCount - dischargeScreenDozeCount;
         if (dischargeScreenOnCount >= 0) {
             sb.setLength(0);
             sb.append(prefix);
@@ -4340,20 +4399,24 @@
                         pw.println(getDischargeCurrentLevel());
             }
             pw.print(prefix); pw.print("    Amount discharged while screen on: ");
-                    pw.println(getDischargeAmountScreenOn());
+            pw.println(getDischargeAmountScreenOn());
             pw.print(prefix); pw.print("    Amount discharged while screen off: ");
-                    pw.println(getDischargeAmountScreenOff());
+            pw.println(getDischargeAmountScreenOff());
+            pw.print(prefix); pw.print("    Amount discharged while screen doze: ");
+            pw.println(getDischargeAmountScreenDoze());
             pw.println(" ");
         } else {
             pw.print(prefix); pw.println("  Device battery use since last full charge");
             pw.print(prefix); pw.print("    Amount discharged (lower bound): ");
-                    pw.println(getLowDischargeAmountSinceCharge());
+            pw.println(getLowDischargeAmountSinceCharge());
             pw.print(prefix); pw.print("    Amount discharged (upper bound): ");
-                    pw.println(getHighDischargeAmountSinceCharge());
+            pw.println(getHighDischargeAmountSinceCharge());
             pw.print(prefix); pw.print("    Amount discharged while screen on: ");
-                    pw.println(getDischargeAmountScreenOnSinceCharge());
+            pw.println(getDischargeAmountScreenOnSinceCharge());
             pw.print(prefix); pw.print("    Amount discharged while screen off: ");
-                    pw.println(getDischargeAmountScreenOffSinceCharge());
+            pw.println(getDischargeAmountScreenOffSinceCharge());
+            pw.print(prefix); pw.print("    Amount discharged while screen doze: ");
+            pw.println(getDischargeAmountScreenDozeSinceCharge());
             pw.println();
         }
 
@@ -5426,7 +5489,7 @@
             }
         }
     }
-    
+
     public void prepareForDumpLocked() {
     }
 
@@ -6248,7 +6311,7 @@
             pw.println();
         }
     }
-    
+
     @SuppressWarnings("unused")
     public void dumpCheckinLocked(Context context, PrintWriter pw,
             List<ApplicationInfo> apps, int flags, long histStart) {
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index cc2af21..8271701 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -23,6 +23,11 @@
 interface IDeviceIdleController {
     void addPowerSaveWhitelistApp(String name);
     void removePowerSaveWhitelistApp(String name);
+    /* Removes an app from the system whitelist. Calling restoreSystemPowerWhitelistApp will add
+    the app back into the system whitelist */
+    void removeSystemPowerWhitelistApp(String name);
+    void restoreSystemPowerWhitelistApp(String name);
+    String[] getRemovedSystemPowerWhitelistApps();
     String[] getSystemPowerWhitelistExceptIdle();
     String[] getSystemPowerWhitelist();
     String[] getUserPowerWhitelist();
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 17f00c2..9369eeb 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -154,11 +154,21 @@
     public abstract boolean isUserUnlocked(int userId);
 
     /**
-     * Return whether the given user is running
+     * Returns whether the given user is running
      */
     public abstract boolean isUserRunning(int userId);
 
     /**
+     * Returns whether the given user is initialized
+     */
+    public abstract boolean isUserInitialized(int userId);
+
+    /**
+     * Returns whether the given user exists
+     */
+    public abstract boolean exists(int userId);
+
+    /**
      * Set user's running state
      */
     public abstract void setUserState(int userId, int userState);
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 1521e7e..2e59f6c 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -187,7 +187,7 @@
  * protect a dataset that contains sensitive information by requiring dataset authentication
  * (see {@link Dataset.Builder#setAuthentication(android.content.IntentSender)}), and to include
  * info about the "primary" field of the partition in the custom presentation for "secondary"
- * fields &mdash; that would prevent a malicious app from getting the "primary" fields without the
+ * fields&mdash;that would prevent a malicious app from getting the "primary" fields without the
  * user realizing they're being released (for example, a malicious app could have fields for a
  * credit card number, verification code, and expiration date crafted in a way that just the latter
  * is visible; by explicitly indicating the expiration date is related to a given credit card
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 2a9a39f..ef9598a 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -29,32 +29,77 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.io.Serializable;
 import java.util.ArrayList;
+import java.util.regex.Pattern;
 
 /**
- * A dataset object represents a group of key/value pairs used to autofill parts of a screen.
+ * A dataset object represents a group of fields (key / value pairs) used to autofill parts of a
+ * screen.
  *
- * <p>In its simplest form, a dataset contains one or more key / value pairs (comprised of
- * {@link AutofillId} and {@link AutofillValue} respectively); and one or more
- * {@link RemoteViews presentation} for these pairs (a pair could have its own
- * {@link RemoteViews presentation}, or use the default {@link RemoteViews presentation} associated
- * with the whole dataset). When an autofill service returns datasets in a {@link FillResponse}
+ * <a name="BasicUsage"></a>
+ * <h3>Basic usage</h3>
+ *
+ * <p>In its simplest form, a dataset contains one or more fields (comprised of
+ * an {@link AutofillId id}, a {@link AutofillValue value}, and an optional filter
+ * {@link Pattern regex}); and one or more {@link RemoteViews presentations} for these fields
+ * (each field could have its own {@link RemoteViews presentation}, or use the default
+ * {@link RemoteViews presentation} associated with the whole dataset).
+ *
+ * <p>When an autofill service returns datasets in a {@link FillResponse}
  * and the screen input is focused in a view that is present in at least one of these datasets,
- * the Android System displays a UI affordance containing the {@link RemoteViews presentation} of
+ * the Android System displays a UI containing the {@link RemoteViews presentation} of
  * all datasets pairs that have that view's {@link AutofillId}. Then, when the user selects a
- * dataset from the affordance, all views in that dataset are autofilled.
+ * dataset from the UI, all views in that dataset are autofilled.
  *
- * <p>In a more sophisticated form, the dataset value can be protected until the user authenticates
- * the dataset - see {@link Dataset.Builder#setAuthentication(IntentSender)}.
+ * <a name="Authentication"></a>
+ * <h3>Dataset authentication</h3>
  *
- * @see android.service.autofill.AutofillService for more information and examples about the
- * role of datasets in the autofill workflow.
+ * <p>In a more sophisticated form, the dataset values can be protected until the user authenticates
+ * the dataset&mdash;in that case, when a dataset is selected by the user, the Android System
+ * launches an intent set by the service to "unlock" the dataset.
+ *
+ * <p>For example, when a data set contains credit card information (such as number,
+ * expiration date, and verification code), you could provide a dataset presentation saying
+ * "Tap to authenticate". Then when the user taps that option, you would launch an activity asking
+ * the user to enter the credit card code, and if the user enters a valid code, you could then
+ * "unlock" the dataset.
+ *
+ * <p>You can also use authenticated datasets to offer an interactive UI for the user. For example,
+ * if the activity being autofilled is an account creation screen, you could use an authenticated
+ * dataset to automatically generate a random password for the user.
+ *
+ * <p>See {@link Dataset.Builder#setAuthentication(IntentSender)} for more details about the dataset
+ * authentication mechanism.
+ *
+ * <a name="Filtering"></a>
+ * <h3>Filtering</h3>
+ * <p>The autofill UI automatically changes which values are shown based on value of the view
+ * anchoring it, following the rules below:
+ * <ol>
+ *   <li>If the view's {@link android.view.View#getAutofillValue() autofill value} is not
+ * {@link AutofillValue#isText() text} or is empty, all datasets are shown.
+ *   <li>Datasets that have a filter regex (set through
+ * {@link Dataset.Builder#setValue(AutofillId, AutofillValue, Pattern)} or
+ * {@link Dataset.Builder#setValue(AutofillId, AutofillValue, Pattern, RemoteViews)}) and whose
+ * regex matches the view's text value converted to lower case are shown.
+ *   <li>Datasets that do not require authentication, have a field value that is
+ * {@link AutofillValue#isText() text} and whose {@link AutofillValue#getTextValue() value} starts
+ * with the lower case value of the view's text are shown.
+ *   <li>All other datasets are hidden.
+ * </ol>
+ *
+ * <a name="MoreInfo"></a>
+ * <h3>More information</h3>
+ * <p>See {@link android.service.autofill.AutofillService} for more information and examples about
+ * the role of datasets in the autofill workflow.
  */
 public final class Dataset implements Parcelable {
 
     private final ArrayList<AutofillId> mFieldIds;
     private final ArrayList<AutofillValue> mFieldValues;
     private final ArrayList<RemoteViews> mFieldPresentations;
+    private final ArrayList<Pattern> mFieldFilters;
     private final RemoteViews mPresentation;
     private final IntentSender mAuthentication;
     @Nullable String mId;
@@ -63,6 +108,7 @@
         mFieldIds = builder.mFieldIds;
         mFieldValues = builder.mFieldValues;
         mFieldPresentations = builder.mFieldPresentations;
+        mFieldFilters = builder.mFieldFilters;
         mPresentation = builder.mPresentation;
         mAuthentication = builder.mAuthentication;
         mId = builder.mId;
@@ -85,6 +131,12 @@
     }
 
     /** @hide */
+    @Nullable
+    public Pattern getFilter(int index) {
+        return mFieldFilters.get(index);
+    }
+
+    /** @hide */
     public @Nullable IntentSender getAuthentication() {
         return mAuthentication;
     }
@@ -103,6 +155,8 @@
                 .append(", fieldValues=").append(mFieldValues)
                 .append(", fieldPresentations=")
                 .append(mFieldPresentations == null ? 0 : mFieldPresentations.size())
+                .append(", fieldFilters=")
+                .append(mFieldFilters == null ? 0 : mFieldFilters.size())
                 .append(", hasPresentation=").append(mPresentation != null)
                 .append(", hasAuthentication=").append(mAuthentication != null)
                 .append(']').toString();
@@ -127,6 +181,7 @@
         private ArrayList<AutofillId> mFieldIds;
         private ArrayList<AutofillValue> mFieldValues;
         private ArrayList<RemoteViews> mFieldPresentations;
+        private ArrayList<Pattern> mFieldFilters;
         private RemoteViews mPresentation;
         private IntentSender mAuthentication;
         private boolean mDestroyed;
@@ -182,12 +237,12 @@
          * credit card information without the CVV for the data set in the {@link FillResponse
          * response} then the returned data set should contain the CVV entry.
          *
-         * <p><b>NOTE:</b> Do not make the provided pending intent
+         * <p><b>Note:</b> Do not make the provided pending intent
          * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
          * platform needs to fill in the authentication arguments.
          *
          * @param authentication Intent to an activity with your authentication flow.
-         * @return This builder.
+         * @return this builder.
          *
          * @see android.app.PendingIntent
          */
@@ -214,11 +269,10 @@
          *
          * @param id id for this dataset or {@code null} to unset.
          *
-         * @return This builder.
+         * @return this builder.
          */
         public @NonNull Builder setId(@Nullable String id) {
             throwIfDestroyed();
-
             mId = id;
             return this;
         }
@@ -230,17 +284,16 @@
          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
          * @param value value to be autofilled. Pass {@code null} if you do not have the value
          *        but the target view is a logical part of the dataset. For example, if
-         *        the dataset needs an authentication and you have no access to the value.
-         * @return This builder.
+         *        the dataset needs authentication and you have no access to the value.
+         * @return this builder.
          * @throws IllegalStateException if the builder was constructed without a
          * {@link RemoteViews presentation}.
          */
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) {
             throwIfDestroyed();
-            if (mPresentation == null) {
-                throw new IllegalStateException("Dataset presentation not set on constructor");
-            }
-            setValueAndPresentation(id, value, null);
+            Preconditions.checkState(mPresentation != null,
+                    "Dataset presentation not set on constructor");
+            setLifeTheUniverseAndEverything(id, value, null, null);
             return this;
         }
 
@@ -250,23 +303,81 @@
          *
          * @param id id returned by {@link
          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
-         * @param value value to be auto filled. Pass {@code null} if you do not have the value
+         * @param value the value to be autofilled. Pass {@code null} if you do not have the value
          *        but the target view is a logical part of the dataset. For example, if
-         *        the dataset needs an authentication and you have no access to the value.
-         *        Filtering matches any user typed string to {@code null} values.
-         * @param presentation The presentation used to visualize this field.
-         * @return This builder.
+         *        the dataset needs authentication and you have no access to the value.
+         * @param presentation the presentation used to visualize this field.
+         * @return this builder.
+         *
          */
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
                 @NonNull RemoteViews presentation) {
             throwIfDestroyed();
             Preconditions.checkNotNull(presentation, "presentation cannot be null");
-            setValueAndPresentation(id, value, presentation);
+            setLifeTheUniverseAndEverything(id, value, presentation, null);
             return this;
         }
 
-        private void setValueAndPresentation(AutofillId id, AutofillValue value,
-                RemoteViews presentation) {
+        /**
+         * Sets the value of a field using an <a href="#Filtering">explicit filter</a>.
+         *
+         * <p>This method is typically used when the dataset is not authenticated and the field
+         * value is not {@link AutofillValue#isText() text} but the service still wants to allow
+         * the user to filter it out.
+         *
+         * @param id id returned by {@link
+         *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+         * @param value the value to be autofilled. Pass {@code null} if you do not have the value
+         *        but the target view is a logical part of the dataset. For example, if
+         *        the dataset needs authentication and you have no access to the value.
+         * @param filter regex used to determine if the dataset should be shown in the autofill UI.
+         *
+         * @return this builder.
+         * @throws IllegalStateException if the builder was constructed without a
+         * {@link RemoteViews presentation}.
+         */
+        public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
+                @NonNull Pattern filter) {
+            throwIfDestroyed();
+            Preconditions.checkNotNull(filter, "filter cannot be null");
+            Preconditions.checkState(mPresentation != null,
+                    "Dataset presentation not set on constructor");
+            setLifeTheUniverseAndEverything(id, value, null, filter);
+            return this;
+        }
+
+        /**
+         * Sets the value of a field, using a custom {@link RemoteViews presentation} to
+         * visualize it and a <a href="#Filtering">explicit filter</a>.
+         *
+         * <p>Typically used to allow filtering on
+         * {@link Dataset.Builder#setAuthentication(IntentSender) authenticated datasets}. For
+         * example, if the dataset represents a credit card number and the service does not want to
+         * show the "Tap to authenticate" message until the user tapped 4 digits, in which case
+         * the filter would be {@code Pattern.compile("\\d.{4,}")}.
+         *
+         * @param id id returned by {@link
+         *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+         * @param value the value to be autofilled. Pass {@code null} if you do not have the value
+         *        but the target view is a logical part of the dataset. For example, if
+         *        the dataset needs authentication and you have no access to the value.
+         * @param presentation the presentation used to visualize this field.
+         * @param filter regex used to determine if the dataset should be shown in the autofill UI.
+         *
+         * @return this builder.
+         */
+        public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
+                @NonNull Pattern filter, @NonNull RemoteViews presentation) {
+            throwIfDestroyed();
+            Preconditions.checkNotNull(filter, "filter cannot be null");
+            Preconditions.checkNotNull(presentation, "presentation cannot be null");
+            setLifeTheUniverseAndEverything(id, value, presentation, filter);
+            return this;
+        }
+
+        private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
+                @Nullable AutofillValue value, @Nullable RemoteViews presentation,
+                @Nullable Pattern filter) {
             Preconditions.checkNotNull(id, "id cannot be null");
             if (mFieldIds != null) {
                 final int existingIdx = mFieldIds.indexOf(id);
@@ -279,10 +390,12 @@
                 mFieldIds = new ArrayList<>();
                 mFieldValues = new ArrayList<>();
                 mFieldPresentations = new ArrayList<>();
+                mFieldFilters = new ArrayList<>();
             }
             mFieldIds.add(id);
             mFieldValues.add(value);
             mFieldPresentations.add(presentation);
+            mFieldFilters.add(filter);
         }
 
         /**
@@ -290,8 +403,9 @@
          *
          * <p>You should not interact with this builder once this method is called.
          *
-         * <p>It is required that you specify at least one field before calling this method. It's
-         * also mandatory to provide a presentation view to visualize the data set in the UI.
+         * @throws IllegalStateException if no field was set (through
+         * {@link #setValue(AutofillId, AutofillValue)} or
+         * {@link #setValue(AutofillId, AutofillValue, RemoteViews)}).
          *
          * @return The built dataset.
          */
@@ -299,7 +413,7 @@
             throwIfDestroyed();
             mDestroyed = true;
             if (mFieldIds == null) {
-                throw new IllegalArgumentException("at least one value must be set");
+                throw new IllegalStateException("at least one value must be set");
             }
             return new Dataset(this);
         }
@@ -326,6 +440,7 @@
         parcel.writeTypedList(mFieldIds, flags);
         parcel.writeTypedList(mFieldValues, flags);
         parcel.writeParcelableList(mFieldPresentations, flags);
+        parcel.writeSerializable(mFieldFilters);
         parcel.writeParcelable(mAuthentication, flags);
         parcel.writeString(mId);
     }
@@ -345,6 +460,9 @@
                     parcel.createTypedArrayList(AutofillValue.CREATOR);
             final ArrayList<RemoteViews> presentations = new ArrayList<>();
             parcel.readParcelableList(presentations, null);
+            @SuppressWarnings("unchecked")
+            final ArrayList<Serializable> filters =
+                    (ArrayList<Serializable>) parcel.readSerializable();
             final int idCount = (ids != null) ? ids.size() : 0;
             final int valueCount = (values != null) ? values.size() : 0;
             for (int i = 0; i < idCount; i++) {
@@ -352,7 +470,8 @@
                 final AutofillValue value = (valueCount > i) ? values.get(i) : null;
                 final RemoteViews fieldPresentation = presentations.isEmpty() ? null
                         : presentations.get(i);
-                builder.setValueAndPresentation(id, value, fieldPresentation);
+                final Pattern filter = (Pattern) filters.get(i);
+                builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, filter);
             }
             builder.setAuthentication(parcel.readParcelable(null));
             builder.setId(parcel.readString());
diff --git a/core/java/android/service/autofill/ImageTransformation.java b/core/java/android/service/autofill/ImageTransformation.java
index 2151f74..4afda24 100644
--- a/core/java/android/service/autofill/ImageTransformation.java
+++ b/core/java/android/service/autofill/ImageTransformation.java
@@ -20,11 +20,12 @@
 
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.util.Log;
-import android.util.Pair;
 import android.view.autofill.AutofillId;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
@@ -43,9 +44,9 @@
  *
  * <pre class="prettyprint">
  *   new ImageTransformation.Builder(ccNumberId, Pattern.compile("^4815.*$"),
- *                                   R.drawable.ic_credit_card_logo1)
- *     .addOption(Pattern.compile("^1623.*$"), R.drawable.ic_credit_card_logo2)
- *     .addOption(Pattern.compile("^42.*$"), R.drawable.ic_credit_card_logo3)
+ *                                   R.drawable.ic_credit_card_logo1, "Brand 1")
+ *     .addOption(Pattern.compile("^1623.*$"), R.drawable.ic_credit_card_logo2, "Brand 2")
+ *     .addOption(Pattern.compile("^42.*$"), R.drawable.ic_credit_card_logo3, "Brand 3")
  *     .build();
  * </pre>
  *
@@ -59,7 +60,7 @@
     private static final String TAG = "ImageTransformation";
 
     private final AutofillId mId;
-    private final ArrayList<Pair<Pattern, Integer>> mOptions;
+    private final ArrayList<Option> mOptions;
 
     private ImageTransformation(Builder builder) {
         mId = builder.mId;
@@ -82,17 +83,21 @@
         }
 
         for (int i = 0; i < size; i++) {
-            final Pair<Pattern, Integer> option = mOptions.get(i);
+            final Option option = mOptions.get(i);
             try {
-                if (option.first.matcher(value).matches()) {
+                if (option.pattern.matcher(value).matches()) {
                     Log.d(TAG, "Found match at " + i + ": " + option);
-                    parentTemplate.setImageViewResource(childViewId, option.second);
+                    parentTemplate.setImageViewResource(childViewId, option.resId);
+                    if (option.contentDescription != null) {
+                        parentTemplate.setContentDescription(childViewId,
+                                option.contentDescription);
+                    }
                     return;
                 }
             } catch (Exception e) {
                 // Do not log full exception to avoid PII leaking
-                Log.w(TAG, "Error matching regex #" + i + "(" + option.first.pattern() + ") on id "
-                        + option.second + ": " + e.getClass());
+                Log.w(TAG, "Error matching regex #" + i + "(" + option.pattern + ") on id "
+                        + option.resId + ": " + e.getClass());
                 throw e;
 
             }
@@ -105,25 +110,44 @@
      */
     public static class Builder {
         private final AutofillId mId;
-        private final ArrayList<Pair<Pattern, Integer>> mOptions = new ArrayList<>();
+        private final ArrayList<Option> mOptions = new ArrayList<>();
         private boolean mDestroyed;
 
         /**
-         * Create a new builder for a autofill id and add a first option.
+         * Creates a new builder for a autofill id and add a first option.
          *
          * @param id id of the screen field that will be used to evaluate whether the image should
          * be used.
          * @param regex regular expression defining what should be matched to use this image.
          * @param resId resource id of the image (in the autofill service's package). The
          * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
+         *
+         * @deprecated use
+         * {@link #ImageTransformation.Builder(AutofillId, Pattern, int, CharSequence)} instead.
          */
+        @Deprecated
         public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId) {
             mId = Preconditions.checkNotNull(id);
-
             addOption(regex, resId);
         }
 
         /**
+         * Creates a new builder for a autofill id and add a first option.
+         *
+         * @param id id of the screen field that will be used to evaluate whether the image should
+         * be used.
+         * @param regex regular expression defining what should be matched to use this image.
+         * @param resId resource id of the image (in the autofill service's package). The
+         * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
+         * @param contentDescription content description to be applied in the child view.
+         */
+        public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId,
+                @NonNull CharSequence contentDescription) {
+            mId = Preconditions.checkNotNull(id);
+            addOption(regex, resId, contentDescription);
+        }
+
+        /**
          * Adds an option to replace the child view with a different image when the regex matches.
          *
          * @param regex regular expression defining what should be matched to use this image.
@@ -131,17 +155,43 @@
          * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
          *
          * @return this build
+         *
+         * @deprecated use {@link #addOption(Pattern, int, CharSequence)} instead.
          */
+        @Deprecated
         public Builder addOption(@NonNull Pattern regex, @DrawableRes int resId) {
+            addOptionInternal(regex, resId, null);
+            return this;
+        }
+
+        /**
+         * Adds an option to replace the child view with a different image and content description
+         * when the regex matches.
+         *
+         * @param regex regular expression defining what should be matched to use this image.
+         * @param resId resource id of the image (in the autofill service's package). The
+         * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
+         * @param contentDescription content description to be applied in the child view.
+         *
+         * @return this build
+         */
+        public Builder addOption(@NonNull Pattern regex, @DrawableRes int resId,
+                @NonNull CharSequence contentDescription) {
+            addOptionInternal(regex, resId, Preconditions.checkNotNull(contentDescription));
+            return this;
+        }
+
+        private void addOptionInternal(@NonNull Pattern regex, @DrawableRes int resId,
+                @Nullable CharSequence contentDescription) {
             throwIfDestroyed();
 
             Preconditions.checkNotNull(regex);
             Preconditions.checkArgument(resId != 0);
 
-            mOptions.add(new Pair<>(regex, resId));
-            return this;
+            mOptions.add(new Option(regex, resId, contentDescription));
         }
 
+
         /**
          * Creates a new {@link ImageTransformation} instance.
          */
@@ -178,15 +228,18 @@
         parcel.writeParcelable(mId, flags);
 
         final int size = mOptions.size();
-        final Pattern[] regexs = new Pattern[size];
+        final Pattern[] patterns = new Pattern[size];
         final int[] resIds = new int[size];
+        final CharSequence[] contentDescriptions = new String[size];
         for (int i = 0; i < size; i++) {
-            Pair<Pattern, Integer> regex = mOptions.get(i);
-            regexs[i] = regex.first;
-            resIds[i] = regex.second;
+            final Option option = mOptions.get(i);
+            patterns[i] = option.pattern;
+            resIds[i] = option.resId;
+            contentDescriptions[i] = option.contentDescription;
         }
-        parcel.writeSerializable(regexs);
+        parcel.writeSerializable(patterns);
         parcel.writeIntArray(resIds);
+        parcel.writeCharSequenceArray(contentDescriptions);
     }
 
     public static final Parcelable.Creator<ImageTransformation> CREATOR =
@@ -197,15 +250,22 @@
 
             final Pattern[] regexs = (Pattern[]) parcel.readSerializable();
             final int[] resIds = parcel.createIntArray();
+            final CharSequence[] contentDescriptions = parcel.readCharSequenceArray();
 
             // Always go through the builder to ensure the data ingested by the system obeys the
             // contract of the builder to avoid attacks using specially crafted parcels.
-            final ImageTransformation.Builder builder = new ImageTransformation.Builder(id,
-                    regexs[0], resIds[0]);
+            final CharSequence contentDescription = contentDescriptions[0];
+            final ImageTransformation.Builder builder = (contentDescription != null)
+                    ? new ImageTransformation.Builder(id, regexs[0], resIds[0], contentDescription)
+                    : new ImageTransformation.Builder(id, regexs[0], resIds[0]);
 
             final int size = regexs.length;
             for (int i = 1; i < size; i++) {
-                builder.addOption(regexs[i], resIds[i]);
+                if (contentDescriptions[i] != null) {
+                    builder.addOption(regexs[i], resIds[i], contentDescriptions[i]);
+                } else {
+                    builder.addOption(regexs[i], resIds[i]);
+                }
             }
 
             return builder.build();
@@ -216,4 +276,16 @@
             return new ImageTransformation[size];
         }
     };
+
+    private static final class Option {
+        public final Pattern pattern;
+        public final int resId;
+        public final CharSequence contentDescription;
+
+        Option(Pattern pattern, int resId, CharSequence contentDescription) {
+            this.pattern = pattern;
+            this.resId = resId;
+            this.contentDescription = TextUtils.trimNoCopySpans(contentDescription);
+        }
+    }
 }
diff --git a/core/java/android/service/autofill/InternalSanitizer.java b/core/java/android/service/autofill/InternalSanitizer.java
new file mode 100644
index 0000000..95d2f66
--- /dev/null
+++ b/core/java/android/service/autofill/InternalSanitizer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcelable;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Superclass of all sanitizers the system understands. As this is not public all public subclasses
+ * have to implement {@link Sanitizer} again.
+ *
+ * @hide
+ */
+@TestApi
+public abstract class InternalSanitizer implements Sanitizer, Parcelable {
+
+    /**
+     * Sanitizes an {@link AutofillValue}.
+     *
+     * @hide
+     */
+    public abstract AutofillValue sanitize(@NonNull AutofillValue value);
+}
diff --git a/core/java/android/service/autofill/Sanitizer.java b/core/java/android/service/autofill/Sanitizer.java
new file mode 100644
index 0000000..38757ac
--- /dev/null
+++ b/core/java/android/service/autofill/Sanitizer.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+/**
+ * Helper class used to sanitize user input before using it in a save request.
+ *
+ * <p>Typically used to avoid displaying the save UI for values that are autofilled but reformatted
+ * by the app&mdash;for example, if the autofill service sends a credit card number
+ * value as "004815162342108" and the app automatically changes it to "0048 1516 2342 108".
+ */
+public interface Sanitizer {
+}
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index e0a0730..1b9240c 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -25,6 +25,8 @@
 import android.content.IntentSender;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
@@ -232,6 +234,8 @@
     private final int mFlags;
     private final CustomDescription mCustomDescription;
     private final InternalValidator mValidator;
+    private final InternalSanitizer[] mSanitizerKeys;
+    private final AutofillId[][] mSanitizerValues;
 
     private SaveInfo(Builder builder) {
         mType = builder.mType;
@@ -243,6 +247,18 @@
         mFlags = builder.mFlags;
         mCustomDescription = builder.mCustomDescription;
         mValidator = builder.mValidator;
+        if (builder.mSanitizers == null) {
+            mSanitizerKeys = null;
+            mSanitizerValues = null;
+        } else {
+            final int size = builder.mSanitizers.size();
+            mSanitizerKeys = new InternalSanitizer[size];
+            mSanitizerValues = new AutofillId[size][];
+            for (int i = 0; i < size; i++) {
+                mSanitizerKeys[i] = builder.mSanitizers.keyAt(i);
+                mSanitizerValues[i] = builder.mSanitizers.valueAt(i);
+            }
+        }
     }
 
     /** @hide */
@@ -292,6 +308,18 @@
         return mValidator;
     }
 
+    /** @hide */
+    @Nullable
+    public InternalSanitizer[] getSanitizerKeys() {
+        return mSanitizerKeys;
+    }
+
+    /** @hide */
+    @Nullable
+    public AutofillId[][] getSanitizerValues() {
+        return mSanitizerValues;
+    }
+
     /**
      * A builder for {@link SaveInfo} objects.
      */
@@ -307,6 +335,9 @@
         private int mFlags;
         private CustomDescription mCustomDescription;
         private InternalValidator mValidator;
+        private ArrayMap<InternalSanitizer, AutofillId[]> mSanitizers;
+        // Set used to validate against duplicate ids.
+        private ArraySet<AutofillId> mSanitizerIds;
 
         /**
          * Creates a new builder.
@@ -530,6 +561,61 @@
         }
 
         /**
+         * Adds a sanitizer for one or more field.
+         *
+         * <p>When a sanitizer is set for a field, the {@link AutofillValue} is sent to the
+         * sanitizer before a save request is <a href="#TriggeringSaveRequest">triggered</a>.
+         *
+         * <p>Typically used to avoid displaying the save UI for values that are autofilled but
+         * reformattedby the app. For example, to remove spaces between every 4 digits of a
+         * credit card number:
+         *
+         * <pre class="prettyprint">
+         * builder.addSanitizer(
+         *     new TextValueSanitizer(Pattern.compile("^(\\d{4}\s?\\d{4}\s?\\d{4}\s?\\d{4})$"),
+         *         "$1$2$3$4"), ccNumberId);
+         * </pre>
+         *
+         * <p>The same sanitizer can be reused to sanitize multiple fields. For example, to trim
+         * both the username and password fields:
+         *
+         * <pre class="prettyprint">
+         * builder.addSanitizer(
+         *     new TextValueSanitizer(Pattern.compile("^\\s*(.*)\\s*$"), "$1"),
+         *         usernameId, passwordId);
+         * </pre>
+         *
+         * @param sanitizer an implementation provided by the Android System.
+         * @param ids id of fields whose value will be sanitized.
+         * @return this builder.
+         *
+         * @throws IllegalArgumentException if a sanitizer for any of the {@code ids} has already
+         * been added or if {@code ids} is empty.
+         */
+        public @NonNull Builder addSanitizer(@NonNull Sanitizer sanitizer,
+                @NonNull AutofillId... ids) {
+            throwIfDestroyed();
+            Preconditions.checkArgument(!ArrayUtils.isEmpty(ids), "ids cannot be empty or null");
+            Preconditions.checkArgument((sanitizer instanceof InternalSanitizer),
+                    "not provided by Android System: " + sanitizer);
+
+            if (mSanitizers == null) {
+                mSanitizers = new ArrayMap<>();
+                mSanitizerIds = new ArraySet<>(ids.length);
+            }
+
+            // Check for duplicates first.
+            for (AutofillId id : ids) {
+                Preconditions.checkArgument(!mSanitizerIds.contains(id), "already added %s", id);
+                mSanitizerIds.add(id);
+            }
+
+            mSanitizers.put((InternalSanitizer) sanitizer, ids);
+
+            return this;
+        }
+
+        /**
          * Builds a new {@link SaveInfo} instance.
          *
          * @throws IllegalStateException if no
@@ -569,6 +655,10 @@
                 .append(", mFlags=").append(mFlags)
                 .append(", mCustomDescription=").append(mCustomDescription)
                 .append(", validation=").append(mValidator)
+                .append(", sanitizerKeys=")
+                    .append(mSanitizerKeys == null ? "N/A:" : mSanitizerKeys.length)
+                .append(", sanitizerValues=")
+                    .append(mSanitizerValues == null ? "N/A:" : mSanitizerValues.length)
                 .append("]").toString();
     }
 
@@ -591,6 +681,12 @@
         parcel.writeCharSequence(mDescription);
         parcel.writeParcelable(mCustomDescription, flags);
         parcel.writeParcelable(mValidator, flags);
+        parcel.writeParcelableArray(mSanitizerKeys, flags);
+        if (mSanitizerKeys != null) {
+            for (int i = 0; i < mSanitizerValues.length; i++) {
+                parcel.writeParcelableArray(mSanitizerValues[i], flags);
+            }
+        }
         parcel.writeInt(mFlags);
     }
 
@@ -621,6 +717,16 @@
             if (validator != null) {
                 builder.setValidator(validator);
             }
+            final InternalSanitizer[] sanitizers =
+                    parcel.readParcelableArray(null, InternalSanitizer.class);
+            if (sanitizers != null) {
+                final int size = sanitizers.length;
+                for (int i = 0; i < size; i++) {
+                    final AutofillId[] autofillIds =
+                            parcel.readParcelableArray(null, AutofillId.class);
+                    builder.addSanitizer(sanitizers[i], autofillIds);
+                }
+            }
             builder.setFlags(parcel.readInt());
             return builder.build();
         }
diff --git a/core/java/android/service/autofill/TextValueSanitizer.java b/core/java/android/service/autofill/TextValueSanitizer.java
new file mode 100644
index 0000000..12e85b1
--- /dev/null
+++ b/core/java/android/service/autofill/TextValueSanitizer.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Slog;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Sanitizes a text {@link AutofillValue} using a regular expression (regex) substitution.
+ *
+ * <p>For example, to remove spaces from groups of 4-digits in a credit card:
+ *
+ * <pre class="prettyprint">
+ * new TextValueSanitizer(Pattern.compile("^(\\d{4}\s?\\d{4}\s?\\d{4}\s?\\d{4})$"), "$1$2$3$4")
+ * </pre>
+ */
+public final class TextValueSanitizer extends InternalSanitizer implements
+        Sanitizer, Parcelable {
+    private static final String TAG = "TextValueSanitizer";
+
+    private final Pattern mRegex;
+    private final String mSubst;
+
+    /**
+     * Default constructor.
+     *
+     * @param regex regular expression with groups (delimited by {@code (} and {@code (}) that
+     * are used to substitute parts of the {@link AutofillValue#getTextValue() text value}.
+     * @param subst the string that substitutes the matched regex, using {@code $} for
+     * group substitution ({@code $1} for 1st group match, {@code $2} for 2nd, etc).
+     */
+    public TextValueSanitizer(@NonNull Pattern regex, @NonNull String subst) {
+        mRegex = Preconditions.checkNotNull(regex);
+        mSubst = Preconditions.checkNotNull(subst);
+    }
+
+    /** @hide */
+    @Override
+    @TestApi
+    public AutofillValue sanitize(@NonNull AutofillValue value) {
+        if (value == null) {
+            Slog.w(TAG, "sanitize() called with null value");
+            return null;
+        }
+        if (!value.isText()) return value;
+
+        final CharSequence text = value.getTextValue();
+
+        try {
+            final Matcher matcher = mRegex.matcher(text);
+            if (!matcher.matches()) return value;
+
+            final CharSequence sanitized = matcher.replaceAll(mSubst);
+            return AutofillValue.forText(sanitized);
+        } catch (Exception e) {
+            Slog.w(TAG, "Exception evaluating " + mRegex + "/" + mSubst + ": " + e);
+            return value;
+        }
+    }
+
+    /////////////////////////////////////
+    // Object "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public String toString() {
+        if (!sDebug) return super.toString();
+
+        return "TextValueSanitizer: [regex=" + mRegex + ", subst=" + mSubst + "]";
+    }
+
+    /////////////////////////////////////
+    // Parcelable "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeSerializable(mRegex);
+        parcel.writeString(mSubst);
+    }
+
+    public static final Parcelable.Creator<TextValueSanitizer> CREATOR =
+            new Parcelable.Creator<TextValueSanitizer>() {
+        @Override
+        public TextValueSanitizer createFromParcel(Parcel parcel) {
+            return new TextValueSanitizer((Pattern) parcel.readSerializable(), parcel.readString());
+        }
+
+        @Override
+        public TextValueSanitizer[] newArray(int size) {
+            return new TextValueSanitizer[size];
+        }
+    };
+}
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
index c38fab1..fef9223 100644
--- a/core/java/android/service/vr/IVrManager.aidl
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -93,5 +93,13 @@
      * currently, else return the display id of the virtual display
      */
     int getVr2dDisplayId();
+
+    /**
+     * Set the component name of the compositor service to bind.
+     *
+     * @param componentName flattened string representing a ComponentName of a Service in the
+     * application's compositor process to bind to, or null to clear the current binding.
+     */
+    void setAndBindCompositor(in String componentName);
 }
 
diff --git a/core/java/android/slice/Slice.java b/core/java/android/slice/Slice.java
new file mode 100644
index 0000000..bb810e6
--- /dev/null
+++ b/core/java/android/slice/Slice.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.slice;
+
+import static android.slice.SliceItem.TYPE_ACTION;
+import static android.slice.SliceItem.TYPE_COLOR;
+import static android.slice.SliceItem.TYPE_IMAGE;
+import static android.slice.SliceItem.TYPE_REMOTE_INPUT;
+import static android.slice.SliceItem.TYPE_REMOTE_VIEW;
+import static android.slice.SliceItem.TYPE_SLICE;
+import static android.slice.SliceItem.TYPE_TEXT;
+import static android.slice.SliceItem.TYPE_TIMESTAMP;
+
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.widget.RemoteViews;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A slice is a piece of app content and actions that can be surfaced outside of the app.
+ *
+ * <p>They are constructed using {@link Builder} in a tree structure
+ * that provides the OS some information about how the content should be displayed.
+ * @hide
+ */
+public final class Slice implements Parcelable {
+
+    /**
+     * @hide
+     */
+    @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED,
+            HINT_SOURCE, HINT_MESSAGE, HINT_HORIZONTAL, HINT_NO_TINT})
+    public @interface SliceHint{ }
+
+    /**
+     * Hint that this content is a title of other content in the slice.
+     */
+    public static final String HINT_TITLE       = "title";
+    /**
+     * Hint that all sub-items/sub-slices within this content should be considered
+     * to have {@link #HINT_LIST_ITEM}.
+     */
+    public static final String HINT_LIST        = "list";
+    /**
+     * Hint that this item is part of a list and should be formatted as if is part
+     * of a list.
+     */
+    public static final String HINT_LIST_ITEM   = "list_item";
+    /**
+     * Hint that this content is important and should be larger when displayed if
+     * possible.
+     */
+    public static final String HINT_LARGE       = "large";
+    /**
+     * Hint that this slice contains a number of actions that can be grouped together
+     * in a sort of controls area of the UI.
+     */
+    public static final String HINT_ACTIONS     = "actions";
+    /**
+     * Hint indicating that this item (and its sub-items) are the current selection.
+     */
+    public static final String HINT_SELECTED    = "selected";
+    /**
+     * Hint to indicate that this is a message as part of a communication
+     * sequence in this slice.
+     */
+    public static final String HINT_MESSAGE     = "message";
+    /**
+     * Hint to tag the source (i.e. sender) of a {@link #HINT_MESSAGE}.
+     */
+    public static final String HINT_SOURCE      = "source";
+    /**
+     * Hint that list items within this slice or subslice would appear better
+     * if organized horizontally.
+     */
+    public static final String HINT_HORIZONTAL  = "horizontal";
+    /**
+     * Hint to indicate that this content should not be tinted.
+     */
+    public static final String HINT_NO_TINT     = "no_tint";
+
+    // These two are coming over from prototyping, but we probably don't want in
+    // public API, at least not right now.
+    /**
+     * @hide
+     */
+    public static final String HINT_ALT         = "alt";
+    /**
+     * @hide
+     */
+    public static final String HINT_PARTIAL     = "partial";
+
+    private final SliceItem[] mItems;
+    private final @SliceHint String[] mHints;
+    private Uri mUri;
+
+    /**
+     * @hide
+     */
+    public Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri) {
+        mHints = hints;
+        mItems = items.toArray(new SliceItem[items.size()]);
+        mUri = uri;
+    }
+
+    protected Slice(Parcel in) {
+        mHints = in.readStringArray();
+        int n = in.readInt();
+        mItems = new SliceItem[n];
+        for (int i = 0; i < n; i++) {
+            mItems[i] = SliceItem.CREATOR.createFromParcel(in);
+        }
+        mUri = Uri.CREATOR.createFromParcel(in);
+    }
+
+    /**
+     * @return The Uri that this slice represents.
+     */
+    public Uri getUri() {
+        return mUri;
+    }
+
+    /**
+     * @return All child {@link SliceItem}s that this Slice contains.
+     */
+    public SliceItem[] getItems() {
+        return mItems;
+    }
+
+    /**
+     * @return All hints associated with this Slice.
+     */
+    public @SliceHint String[] getHints() {
+        return mHints;
+    }
+
+    /**
+     * @hide
+     */
+    public SliceItem getPrimaryIcon() {
+        for (SliceItem item : getItems()) {
+            if (item.getType() == TYPE_IMAGE) {
+                return item;
+            }
+            if (!(item.getType() == TYPE_SLICE && item.hasHint(Slice.HINT_LIST))
+                    && !item.hasHint(Slice.HINT_ACTIONS)
+                    && !item.hasHint(Slice.HINT_LIST_ITEM)
+                    && (item.getType() != TYPE_ACTION)) {
+                SliceItem icon = SliceQuery.find(item, TYPE_IMAGE);
+                if (icon != null) return icon;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStringArray(mHints);
+        dest.writeInt(mItems.length);
+        for (int i = 0; i < mItems.length; i++) {
+            mItems[i].writeToParcel(dest, flags);
+        }
+        mUri.writeToParcel(dest, 0);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * A Builder used to construct {@link Slice}s
+     */
+    public static class Builder {
+
+        private final Uri mUri;
+        private ArrayList<SliceItem> mItems = new ArrayList<>();
+        private @SliceHint ArrayList<String> mHints = new ArrayList<>();
+
+        /**
+         * Create a builder which will construct a {@link Slice} for the Given Uri.
+         * @param uri Uri to tag for this slice.
+         */
+        public Builder(@NonNull Uri uri) {
+            mUri = uri;
+        }
+
+        /**
+         * Create a builder for a {@link Slice} that is a sub-slice of the slice
+         * being constructed by the provided builder.
+         * @param parent The builder constructing the parent slice
+         */
+        public Builder(@NonNull Slice.Builder parent) {
+            mUri = parent.mUri.buildUpon().appendPath("_gen")
+                    .appendPath(String.valueOf(mItems.size())).build();
+        }
+
+        /**
+         * Add hints to the Slice being constructed
+         */
+        public Builder addHints(@SliceHint String... hints) {
+            mHints.addAll(Arrays.asList(hints));
+            return this;
+        }
+
+        /**
+         * Add a sub-slice to the slice being constructed
+         */
+        public Builder addSubSlice(@NonNull Slice slice) {
+            mItems.add(new SliceItem(slice, TYPE_SLICE, slice.getHints()));
+            return this;
+        }
+
+        /**
+         * Add an action to the slice being constructed
+         */
+        public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s) {
+            mItems.add(new SliceItem(action, s, TYPE_ACTION, new String[0]));
+            return this;
+        }
+
+        /**
+         * Add text to the slice being constructed
+         */
+        public Builder addText(CharSequence text, @SliceHint String... hints) {
+            mItems.add(new SliceItem(text, TYPE_TEXT, hints));
+            return this;
+        }
+
+        /**
+         * Add an image to the slice being constructed
+         */
+        public Builder addIcon(Icon icon, @SliceHint String... hints) {
+            mItems.add(new SliceItem(icon, TYPE_IMAGE, hints));
+            return this;
+        }
+
+        /**
+         * @hide This isn't final
+         */
+        public Builder addRemoteView(RemoteViews remoteView, @SliceHint String... hints) {
+            mItems.add(new SliceItem(remoteView, TYPE_REMOTE_VIEW, hints));
+            return this;
+        }
+
+        /**
+         * Add remote input to the slice being constructed
+         */
+        public Slice.Builder addRemoteInput(RemoteInput remoteInput, @SliceHint String... hints) {
+            mItems.add(new SliceItem(remoteInput, TYPE_REMOTE_INPUT, hints));
+            return this;
+        }
+
+        /**
+         * Add a color to the slice being constructed
+         */
+        public Builder addColor(int color, @SliceHint String... hints) {
+            mItems.add(new SliceItem(color, TYPE_COLOR, hints));
+            return this;
+        }
+
+        /**
+         * Add a timestamp to the slice being constructed
+         */
+        public Slice.Builder addTimestamp(long time, @SliceHint String... hints) {
+            mItems.add(new SliceItem(time, TYPE_TIMESTAMP, hints));
+            return this;
+        }
+
+        /**
+         * Construct the slice.
+         */
+        public Slice build() {
+            return new Slice(mItems, mHints.toArray(new String[mHints.size()]), mUri);
+        }
+    }
+
+    public static final Creator<Slice> CREATOR = new Creator<Slice>() {
+        @Override
+        public Slice createFromParcel(Parcel in) {
+            return new Slice(in);
+        }
+
+        @Override
+        public Slice[] newArray(int size) {
+            return new Slice[size];
+        }
+    };
+}
diff --git a/core/java/android/slice/SliceItem.java b/core/java/android/slice/SliceItem.java
new file mode 100644
index 0000000..16f7dc6
--- /dev/null
+++ b/core/java/android/slice/SliceItem.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.slice;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.slice.Slice.SliceHint;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.widget.RemoteViews;
+
+import com.android.internal.util.ArrayUtils;
+
+
+/**
+ * A SliceItem is a single unit in the tree structure of a {@link Slice}.
+ *
+ * A SliceItem a piece of content and some hints about what that content
+ * means or how it should be displayed. The types of content can be:
+ * <li>{@link #TYPE_SLICE}</li>
+ * <li>{@link #TYPE_TEXT}</li>
+ * <li>{@link #TYPE_IMAGE}</li>
+ * <li>{@link #TYPE_ACTION}</li>
+ * <li>{@link #TYPE_COLOR}</li>
+ * <li>{@link #TYPE_TIMESTAMP}</li>
+ * <li>{@link #TYPE_REMOTE_INPUT}</li>
+ *
+ * The hints that a {@link SliceItem} are a set of strings which annotate
+ * the content. The hints that are guaranteed to be understood by the system
+ * are defined on {@link Slice}.
+ * @hide
+ */
+public final class SliceItem implements Parcelable {
+
+    /**
+     * @hide
+     */
+    @IntDef({TYPE_SLICE, TYPE_TEXT, TYPE_IMAGE, TYPE_ACTION, TYPE_COLOR,
+            TYPE_TIMESTAMP, TYPE_REMOTE_INPUT})
+    public @interface SliceType {}
+
+    /**
+     * A {@link SliceItem} that contains a {@link Slice}
+     */
+    public static final int TYPE_SLICE        = 1;
+    /**
+     * A {@link SliceItem} that contains a {@link CharSequence}
+     */
+    public static final int TYPE_TEXT         = 2;
+    /**
+     * A {@link SliceItem} that contains an {@link Icon}
+     */
+    public static final int TYPE_IMAGE        = 3;
+    /**
+     * A {@link SliceItem} that contains a {@link PendingIntent}
+     *
+     * Note: Actions contain 2 pieces of data, In addition to the pending intent, the
+     * item contains a {@link Slice} that the action applies to.
+     */
+    public static final int TYPE_ACTION       = 4;
+    /**
+     * @hide This isn't final
+     */
+    public static final int TYPE_REMOTE_VIEW  = 5;
+    /**
+     * A {@link SliceItem} that contains a Color int.
+     */
+    public static final int TYPE_COLOR        = 6;
+    /**
+     * A {@link SliceItem} that contains a timestamp.
+     */
+    public static final int TYPE_TIMESTAMP    = 8;
+    /**
+     * A {@link SliceItem} that contains a {@link RemoteInput}.
+     */
+    public static final int TYPE_REMOTE_INPUT = 9;
+
+    /**
+     * @hide
+     */
+    protected @SliceHint String[] mHints;
+    private final int mType;
+    private final Object mObj;
+
+    /**
+     * @hide
+     */
+    public SliceItem(Object obj, @SliceType int type, @SliceHint String[] hints) {
+        mHints = hints;
+        mType = type;
+        mObj = obj;
+    }
+
+    /**
+     * @hide
+     */
+    public SliceItem(PendingIntent intent, Slice slice, int type, @SliceHint String[] hints) {
+        this(new Pair<>(intent, slice), type, hints);
+    }
+
+    /**
+     * Gets all hints associated with this SliceItem.
+     * @return Array of hints.
+     */
+    public @NonNull @SliceHint String[] getHints() {
+        return mHints;
+    }
+
+    /**
+     * @hide
+     */
+    public void addHint(@SliceHint String hint) {
+        mHints = ArrayUtils.appendElement(String.class, mHints, hint);
+    }
+
+    public @SliceType int getType() {
+        return mType;
+    }
+
+    /**
+     * @return The text held by this {@link #TYPE_TEXT} SliceItem
+     */
+    public CharSequence getText() {
+        return (CharSequence) mObj;
+    }
+
+    /**
+     * @return The icon held by this {@link #TYPE_IMAGE} SliceItem
+     */
+    public Icon getIcon() {
+        return (Icon) mObj;
+    }
+
+    /**
+     * @return The pending intent held by this {@link #TYPE_ACTION} SliceItem
+     */
+    public PendingIntent getAction() {
+        return ((Pair<PendingIntent, Slice>) mObj).first;
+    }
+
+    /**
+     * @hide This isn't final
+     */
+    public RemoteViews getRemoteView() {
+        return (RemoteViews) mObj;
+    }
+
+    /**
+     * @return The remote input held by this {@link #TYPE_REMOTE_INPUT} SliceItem
+     */
+    public RemoteInput getRemoteInput() {
+        return (RemoteInput) mObj;
+    }
+
+    /**
+     * @return The color held by this {@link #TYPE_COLOR} SliceItem
+     */
+    public int getColor() {
+        return (Integer) mObj;
+    }
+
+    /**
+     * @return The slice held by this {@link #TYPE_ACTION} or {@link #TYPE_SLICE} SliceItem
+     */
+    public Slice getSlice() {
+        if (getType() == TYPE_ACTION) {
+            return ((Pair<PendingIntent, Slice>) mObj).second;
+        }
+        return (Slice) mObj;
+    }
+
+    /**
+     * @return The timestamp held by this {@link #TYPE_TIMESTAMP} SliceItem
+     */
+    public long getTimestamp() {
+        return (Long) mObj;
+    }
+
+    /**
+     * @param hint The hint to check for
+     * @return true if this item contains the given hint
+     */
+    public boolean hasHint(@SliceHint String hint) {
+        return ArrayUtils.contains(mHints, hint);
+    }
+
+    /**
+     * @hide
+     */
+    public SliceItem(Parcel in) {
+        mHints = in.readStringArray();
+        mType = in.readInt();
+        mObj = readObj(mType, in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStringArray(mHints);
+        dest.writeInt(mType);
+        writeObj(dest, flags, mObj, mType);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean hasHints(@SliceHint String[] hints) {
+        if (hints == null) return true;
+        for (String hint : hints) {
+            if (!ArrayUtils.contains(mHints, hint)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * @hide
+     */
+    public boolean hasAnyHints(@SliceHint String[] hints) {
+        if (hints == null) return true;
+        for (String hint : hints) {
+            if (ArrayUtils.contains(mHints, hint)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void writeObj(Parcel dest, int flags, Object obj, int type) {
+        switch (type) {
+            case TYPE_SLICE:
+            case TYPE_REMOTE_VIEW:
+            case TYPE_IMAGE:
+            case TYPE_REMOTE_INPUT:
+                ((Parcelable) obj).writeToParcel(dest, flags);
+                break;
+            case TYPE_ACTION:
+                ((Pair<PendingIntent, Slice>) obj).first.writeToParcel(dest, flags);
+                ((Pair<PendingIntent, Slice>) obj).second.writeToParcel(dest, flags);
+                break;
+            case TYPE_TEXT:
+                TextUtils.writeToParcel((CharSequence) mObj, dest, flags);
+                break;
+            case TYPE_COLOR:
+                dest.writeInt((Integer) mObj);
+                break;
+            case TYPE_TIMESTAMP:
+                dest.writeLong((Long) mObj);
+                break;
+        }
+    }
+
+    private static Object readObj(int type, Parcel in) {
+        switch (type) {
+            case TYPE_SLICE:
+                return Slice.CREATOR.createFromParcel(in);
+            case TYPE_TEXT:
+                return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            case TYPE_IMAGE:
+                return Icon.CREATOR.createFromParcel(in);
+            case TYPE_ACTION:
+                return new Pair<PendingIntent, Slice>(
+                        PendingIntent.CREATOR.createFromParcel(in),
+                        Slice.CREATOR.createFromParcel(in));
+            case TYPE_REMOTE_VIEW:
+                return RemoteViews.CREATOR.createFromParcel(in);
+            case TYPE_COLOR:
+                return in.readInt();
+            case TYPE_TIMESTAMP:
+                return in.readLong();
+            case TYPE_REMOTE_INPUT:
+                return RemoteInput.CREATOR.createFromParcel(in);
+        }
+        throw new RuntimeException("Unsupported type " + type);
+    }
+
+    public static final Creator<SliceItem> CREATOR = new Creator<SliceItem>() {
+        @Override
+        public SliceItem createFromParcel(Parcel in) {
+            return new SliceItem(in);
+        }
+
+        @Override
+        public SliceItem[] newArray(int size) {
+            return new SliceItem[size];
+        }
+    };
+}
diff --git a/core/java/android/slice/SliceProvider.java b/core/java/android/slice/SliceProvider.java
new file mode 100644
index 0000000..4e21371
--- /dev/null
+++ b/core/java/android/slice/SliceProvider.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.slice;
+
+import android.Manifest.permission;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A SliceProvider allows app to provide content to be displayed in system
+ * spaces. This content is templated and can contain actions, and the behavior
+ * of how it is surfaced is specific to the system surface.
+ *
+ * <p>Slices are not currently live content. They are bound once and shown to the
+ * user. If the content changes due to a callback from user interaction, then
+ * {@link ContentResolver#notifyChange(Uri, ContentObserver)}
+ * should be used to notify the system.</p>
+ *
+ * <p>The provider needs to be declared in the manifest to provide the authority
+ * for the app. The authority for most slices is expected to match the package
+ * of the application.</p>
+ * <pre class="prettyprint">
+ * {@literal
+ * <provider
+ *     android:name="com.android.mypkg.MySliceProvider"
+ *     android:authorities="com.android.mypkg" />}
+ * </pre>
+ *
+ * @see Slice
+ * @hide
+ */
+public abstract class SliceProvider extends ContentProvider {
+
+    private static final String TAG = "SliceProvider";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_BIND_URI = "slice_uri";
+    /**
+     * @hide
+     */
+    public static final String METHOD_SLICE = "bind_slice";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_SLICE = "slice";
+
+    private static final boolean DEBUG = false;
+
+    /**
+     * Implemented to create a slice. Will be called on the main thread.
+     * @see {@link Slice}.
+     */
+    public abstract Slice onBindSlice(Uri sliceUri);
+
+    @Override
+    public final int update(Uri uri, ContentValues values, String selection,
+            String[] selectionArgs) {
+        if (DEBUG) Log.d(TAG, "update " + uri);
+        return 0;
+    }
+
+    @Override
+    public final int delete(Uri uri, String selection, String[] selectionArgs) {
+        if (DEBUG) Log.d(TAG, "delete " + uri);
+        return 0;
+    }
+
+    @Override
+    public final Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        if (DEBUG) Log.d(TAG, "query " + uri);
+        return null;
+    }
+
+    @Override
+    public final Cursor query(Uri uri, String[] projection, String selection, String[]
+            selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
+        if (DEBUG) Log.d(TAG, "query " + uri);
+        return null;
+    }
+
+    @Override
+    public final Cursor query(Uri uri, String[] projection, Bundle queryArgs,
+            CancellationSignal cancellationSignal) {
+        if (DEBUG) Log.d(TAG, "query " + uri);
+        return null;
+    }
+
+    @Override
+    public final Uri insert(Uri uri, ContentValues values) {
+        if (DEBUG) Log.d(TAG, "insert " + uri);
+        return null;
+    }
+
+    @Override
+    public final String getType(Uri uri) {
+        if (DEBUG) Log.d(TAG, "getType " + uri);
+        return null;
+    }
+
+    @Override
+    public final Bundle call(String method, String arg, Bundle extras) {
+        if (method.equals(METHOD_SLICE)) {
+            getContext().enforceCallingPermission(permission.BIND_SLICE,
+                    "Slice binding requires the permission BIND_SLICE");
+            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+
+            Slice s = handleBindSlice(uri);
+            Bundle b = new Bundle();
+            b.putParcelable(EXTRA_SLICE, s);
+            return b;
+        }
+        return super.call(method, arg, extras);
+    }
+
+    private Slice handleBindSlice(Uri sliceUri) {
+        Slice[] output = new Slice[1];
+        CountDownLatch latch = new CountDownLatch(1);
+        Handler mainHandler = new Handler(Looper.getMainLooper());
+        mainHandler.post(() -> {
+            output[0] = onBindSlice(sliceUri);
+            latch.countDown();
+        });
+        try {
+            latch.await();
+            return output[0];
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/core/java/android/slice/SliceQuery.java b/core/java/android/slice/SliceQuery.java
new file mode 100644
index 0000000..edac0cc
--- /dev/null
+++ b/core/java/android/slice/SliceQuery.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.slice;
+
+import static android.slice.SliceItem.TYPE_ACTION;
+import static android.slice.SliceItem.TYPE_SLICE;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Spliterators;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+/**
+ * A bunch of utilities for searching the contents of a slice.
+ * @hide
+ */
+public class SliceQuery {
+    private static final String TAG = "SliceQuery";
+
+    /**
+     * @hide
+     */
+    public static SliceItem findNotContaining(SliceItem container, List<SliceItem> list) {
+        SliceItem ret = null;
+        while (ret == null && list.size() != 0) {
+            SliceItem remove = list.remove(0);
+            if (!contains(container, remove)) {
+                ret = remove;
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * @hide
+     */
+    private static boolean contains(SliceItem container, SliceItem item) {
+        if (container == null || item == null) return false;
+        return stream(container).filter(s -> (s == item)).findAny().isPresent();
+    }
+
+    /**
+     * @hide
+     */
+    public static List<SliceItem> findAll(SliceItem s, int type, String hints, String nonHints) {
+        return findAll(s, type, new String[]{ hints }, new String[]{ nonHints });
+    }
+
+    /**
+     * @hide
+     */
+    public static List<SliceItem> findAll(SliceItem s, int type, String[] hints,
+            String[] nonHints) {
+        return stream(s).filter(item -> (type == -1 || item.getType() == type)
+                && (item.hasHints(hints) && !item.hasAnyHints(nonHints)))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * @hide
+     */
+    public static SliceItem find(Slice s, int type, String hints, String nonHints) {
+        return find(s, type, new String[]{ hints }, new String[]{ nonHints });
+    }
+
+    /**
+     * @hide
+     */
+    public static SliceItem find(SliceItem s, int type) {
+        return find(s, type, (String[]) null, null);
+    }
+
+    /**
+     * @hide
+     */
+    public static SliceItem find(SliceItem s, int type, String hints, String nonHints) {
+        return find(s, type, new String[]{ hints }, new String[]{ nonHints });
+    }
+
+    /**
+     * @hide
+     */
+    public static SliceItem find(Slice s, int type, String[] hints, String[] nonHints) {
+        return find(new SliceItem(s, TYPE_SLICE, s.getHints()), type, hints, nonHints);
+    }
+
+    /**
+     * @hide
+     */
+    public static SliceItem find(SliceItem s, int type, String[] hints, String[] nonHints) {
+        return stream(s).filter(item -> (item.getType() == type || type == -1)
+                && (item.hasHints(hints) && !item.hasAnyHints(nonHints))).findFirst().orElse(null);
+    }
+
+    /**
+     * @hide
+     */
+    public static Stream<SliceItem> stream(SliceItem slice) {
+        Queue<SliceItem> items = new LinkedList();
+        items.add(slice);
+        Iterator<SliceItem> iterator = new Iterator<SliceItem>() {
+            @Override
+            public boolean hasNext() {
+                return items.size() != 0;
+            }
+
+            @Override
+            public SliceItem next() {
+                SliceItem item = items.poll();
+                if (item.getType() == TYPE_SLICE || item.getType() == TYPE_ACTION) {
+                    items.addAll(Arrays.asList(item.getSlice().getItems()));
+                }
+                return item;
+            }
+        };
+        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
+    }
+}
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index 16f2d7d..2c9f871 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -103,7 +103,7 @@
  *  <tr>
  *      <td>ACTION_DRAG_ENDED</td>
  *      <td style="text-align: center;">&nbsp;</td>
- *      <td style="text-align: center;">&nbsp;</td>
+ *      <td style="text-align: center;">X</td>
  *      <td style="text-align: center;">&nbsp;</td>
  *      <td style="text-align: center;">&nbsp;</td>
  *      <td style="text-align: center;">&nbsp;</td>
@@ -112,6 +112,7 @@
  * </table>
  * <p>
  *  The {@link android.view.DragEvent#getAction()},
+ *  {@link android.view.DragEvent#getLocalState()}
  *  {@link android.view.DragEvent#describeContents()},
  *  {@link android.view.DragEvent#writeToParcel(Parcel,int)}, and
  *  {@link android.view.DragEvent#toString()} methods always return valid data.
@@ -397,7 +398,7 @@
      * operation. In all other activities this method will return null
      * </p>
      * <p>
-     *  This method returns valid data for all event actions except for {@link #ACTION_DRAG_ENDED}.
+     *  This method returns valid data for all event actions.
      * </p>
      * @return The local state object sent to the system by startDragAndDrop().
      */
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3f710a8..b6be296 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -15529,7 +15529,13 @@
      * {@code dirty}.
      *
      * @param dirty the rectangle representing the bounds of the dirty region
+     *
+     * @deprecated The switch to hardware accelerated rendering in API 14 reduced
+     * the importance of the dirty rectangle. In API 21 the given rectangle is
+     * ignored entirely in favor of an internally-calculated area instead.
+     * Because of this, clients are encouraged to just call {@link #invalidate()}.
      */
+    @Deprecated
     public void invalidate(Rect dirty) {
         final int scrollX = mScrollX;
         final int scrollY = mScrollY;
@@ -15550,7 +15556,13 @@
      * @param t the top position of the dirty region
      * @param r the right position of the dirty region
      * @param b the bottom position of the dirty region
+     *
+     * @deprecated The switch to hardware accelerated rendering in API 14 reduced
+     * the importance of the dirty rectangle. In API 21 the given rectangle is
+     * ignored entirely in favor of an internally-calculated area instead.
+     * Because of this, clients are encouraged to just call {@link #invalidate()}.
      */
+    @Deprecated
     public void invalidate(int l, int t, int r, int b) {
         final int scrollX = mScrollX;
         final int scrollY = mScrollY;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a346863..71106ad 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -512,7 +512,7 @@
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
 
         if (!sCompatibilityDone) {
-            sAlwaysAssignFocus = true;
+            sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
 
             sCompatibilityDone = true;
         }
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 4773cda..95cb454 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -454,13 +454,13 @@
         // waiting on relro creation.
         if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
             if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
-            WebViewLibraryLoader.createRelroFile(false /* is64Bit */, nativeLibraryPaths);
+            WebViewLibraryLoader.createRelroFile(false /* is64Bit */, nativeLibraryPaths[0]);
             numRelros++;
         }
 
         if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
             if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
-            WebViewLibraryLoader.createRelroFile(true /* is64Bit */, nativeLibraryPaths);
+            WebViewLibraryLoader.createRelroFile(true /* is64Bit */, nativeLibraryPaths[1]);
             numRelros++;
         }
         return numRelros;
diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java
index 21316e7..fa1a390 100644
--- a/core/java/android/webkit/WebViewLibraryLoader.java
+++ b/core/java/android/webkit/WebViewLibraryLoader.java
@@ -62,20 +62,18 @@
             boolean result = false;
             boolean is64Bit = VMRuntime.getRuntime().is64Bit();
             try {
-                if (args.length != 2 || args[0] == null || args[1] == null) {
+                if (args.length != 1 || args[0] == null) {
                     Log.e(LOGTAG, "Invalid RelroFileCreator args: " + Arrays.toString(args));
                     return;
                 }
-                Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), "
-                        + " 32-bit lib: " + args[0] + ", 64-bit lib: " + args[1]);
+                Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), lib: " + args[0]);
                 if (!sAddressSpaceReserved) {
                     Log.e(LOGTAG, "can't create relro file; address space not reserved");
                     return;
                 }
-                result = nativeCreateRelroFile(args[0] /* path32 */,
-                                               args[1] /* path64 */,
-                                               CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
-                                               CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
+                result = nativeCreateRelroFile(args[0] /* path */,
+                                               is64Bit ? CHROMIUM_WEBVIEW_NATIVE_RELRO_64 :
+                                                         CHROMIUM_WEBVIEW_NATIVE_RELRO_32);
                 if (result && DEBUG) Log.v(LOGTAG, "created relro file");
             } finally {
                 // We must do our best to always notify the update service, even if something fails.
@@ -96,7 +94,7 @@
     /**
      * Create a single relro file by invoking an isolated process that to do the actual work.
      */
-    static void createRelroFile(final boolean is64Bit, String[] nativeLibraryPaths) {
+    static void createRelroFile(final boolean is64Bit, String nativeLibraryPath) {
         final String abi =
                 is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
 
@@ -114,13 +112,12 @@
         };
 
         try {
-            if (nativeLibraryPaths == null
-                    || nativeLibraryPaths[0] == null || nativeLibraryPaths[1] == null) {
+            if (nativeLibraryPath == null) {
                 throw new IllegalArgumentException(
                         "Native library paths to the WebView RelRo process must not be null!");
             }
             int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess(
-                    RelroFileCreator.class.getName(), nativeLibraryPaths,
+                    RelroFileCreator.class.getName(), new String[] { nativeLibraryPath },
                     "WebViewLoader-" + abi, abi, Process.SHARED_RELRO_UID, crashHandler);
             if (pid <= 0) throw new Exception("Failed to start the relro file creator process");
         } catch (Throwable t) {
@@ -217,8 +214,9 @@
 
         final String libraryFileName =
                 WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo);
-        int result = nativeLoadWithRelroFile(libraryFileName, CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
-                                             CHROMIUM_WEBVIEW_NATIVE_RELRO_64, clazzLoader);
+        String relroPath = VMRuntime.getRuntime().is64Bit() ? CHROMIUM_WEBVIEW_NATIVE_RELRO_64 :
+                                                              CHROMIUM_WEBVIEW_NATIVE_RELRO_32;
+        int result = nativeLoadWithRelroFile(libraryFileName, relroPath, clazzLoader);
         if (result != WebViewFactory.LIBLOAD_SUCCESS) {
             Log.w(LOGTAG, "failed to load with relro file, proceeding without");
         } else if (DEBUG) {
@@ -313,8 +311,6 @@
     }
 
     static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
-    static native boolean nativeCreateRelroFile(String lib32, String lib64,
-                                                        String relro32, String relro64);
-    static native int nativeLoadWithRelroFile(String lib, String relro32, String relro64,
-                                                      ClassLoader clazzLoader);
+    static native boolean nativeCreateRelroFile(String lib, String relro);
+    static native int nativeLoadWithRelroFile(String lib, String relro, ClassLoader clazzLoader);
 }
diff --git a/core/java/com/android/internal/alsa/AlsaDevicesParser.java b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
index 7cdd897..6e3d596 100644
--- a/core/java/com/android/internal/alsa/AlsaDevicesParser.java
+++ b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
@@ -258,7 +258,7 @@
         return line.charAt(kIndex_CardDeviceField) == '[';
     }
 
-    public void scan() {
+    public boolean scan() {
         mDeviceRecords.clear();
 
         File devicesFile = new File(kDevicesFilePath);
@@ -274,11 +274,13 @@
                 }
             }
             reader.close();
+            return true;
         } catch (FileNotFoundException e) {
             e.printStackTrace();
         } catch (IOException e) {
             e.printStackTrace();
         }
+        return false;
     }
 
     //
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 0bd2981..36fd991 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -119,7 +119,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 166 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 167 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS;
@@ -341,8 +341,8 @@
     protected final TimeBase mOnBatteryTimeBase = new TimeBase();
 
     // These are the objects that will want to do something when the device
-    // is unplugged from power *and* the screen is off.
-    final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase();
+    // is unplugged from power *and* the screen is off or doze.
+    protected final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase();
 
     // Set to true when we want to distribute CPU across wakelocks for the next
     // CPU update, even if we aren't currently running wake locks.
@@ -436,8 +436,12 @@
     public boolean mRecordAllHistory;
     boolean mNoAutoReset;
 
-    int mScreenState = Display.STATE_UNKNOWN;
-    StopwatchTimer mScreenOnTimer;
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    protected int mScreenState = Display.STATE_UNKNOWN;
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    protected StopwatchTimer mScreenOnTimer;
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    protected StopwatchTimer mScreenDozeTimer;
 
     int mScreenBrightnessBin = -1;
     final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -583,12 +587,16 @@
     int mHighDischargeAmountSinceCharge;
     int mDischargeScreenOnUnplugLevel;
     int mDischargeScreenOffUnplugLevel;
+    int mDischargeScreenDozeUnplugLevel;
     int mDischargeAmountScreenOn;
     int mDischargeAmountScreenOnSinceCharge;
     int mDischargeAmountScreenOff;
     int mDischargeAmountScreenOffSinceCharge;
+    int mDischargeAmountScreenDoze;
+    int mDischargeAmountScreenDozeSinceCharge;
 
     private LongSamplingCounter mDischargeScreenOffCounter;
+    private LongSamplingCounter mDischargeScreenDozeCounter;
     private LongSamplingCounter mDischargeCounter;
 
     static final int MAX_LEVEL_STEPS = 200;
@@ -673,13 +681,18 @@
     }
 
     @Override
-    public LongCounter getDischargeScreenOffCoulombCounter() {
-        return mDischargeScreenOffCounter;
+    public long getMahDischarge(int which) {
+        return mDischargeCounter.getCountLocked(which);
     }
 
     @Override
-    public LongCounter getDischargeCoulombCounter() {
-        return mDischargeCounter;
+    public long getMahDischargeScreenOff(int which) {
+        return mDischargeScreenOffCounter.getCountLocked(which);
+    }
+
+    @Override
+    public long getMahDischargeScreenDoze(int which) {
+        return mDischargeScreenDozeCounter.getCountLocked(which);
     }
 
     @Override
@@ -3573,8 +3586,9 @@
         mActiveHistoryStates2 = 0xffffffff;
     }
 
-    public void updateTimeBasesLocked(boolean unplugged, boolean screenOff, long uptime,
+    public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime,
             long realtime) {
+        final boolean screenOff = isScreenOff(screenState) || isScreenDoze(screenState);
         final boolean updateOnBatteryTimeBase = unplugged != mOnBatteryTimeBase.isRunning();
         final boolean updateOnBatteryScreenOffTimeBase =
                 (unplugged && screenOff) != mOnBatteryScreenOffTimeBase.isRunning();
@@ -3591,20 +3605,22 @@
                 updateRpmStatsLocked(); // if either OnBattery or OnBatteryScreenOff timebase changes.
             }
             if (DEBUG_ENERGY_CPU) {
-                Slog.d(TAG, "Updating cpu time because screen is now " + (screenOff ? "off" : "on")
+                Slog.d(TAG, "Updating cpu time because screen is now "
+                        + Display.stateToString(screenState)
                         + " and battery is " + (unplugged ? "on" : "off"));
             }
             updateCpuTimeLocked();
 
             mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime);
-            mOnBatteryScreenOffTimeBase.setRunning(unplugged && screenOff, uptime, realtime);
-            for (int i = mUidStats.size() - 1; i >= 0; --i) {
-                final Uid u = mUidStats.valueAt(i);
-                if (updateOnBatteryTimeBase) {
-                    u.updateOnBatteryBgTimeBase(uptime, realtime);
+            if (updateOnBatteryTimeBase) {
+                for (int i = mUidStats.size() - 1; i >= 0; --i) {
+                    mUidStats.valueAt(i).updateOnBatteryBgTimeBase(uptime, realtime);
                 }
-                if (updateOnBatteryScreenOffTimeBase) {
-                    u.updateOnBatteryScreenOffBgTimeBase(uptime, realtime);
+            }
+            if (updateOnBatteryScreenOffTimeBase) {
+                mOnBatteryScreenOffTimeBase.setRunning(unplugged && screenOff, uptime, realtime);
+                for (int i = mUidStats.size() - 1; i >= 0; --i) {
+                    mUidStats.valueAt(i).updateOnBatteryScreenOffBgTimeBase(uptime, realtime);
                 }
             }
         }
@@ -3864,8 +3880,10 @@
     }
 
     public void setPretendScreenOff(boolean pretendScreenOff) {
-        mPretendScreenOff = pretendScreenOff;
-        noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON);
+        if (mPretendScreenOff != pretendScreenOff) {
+            mPretendScreenOff = pretendScreenOff;
+            noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON);
+        }
     }
 
     private String mInitialAcquireWakeName;
@@ -4195,54 +4213,58 @@
                 }
             }
 
-            if (state == Display.STATE_ON) {
-                // Screen turning on.
-                final long elapsedRealtime = mClocks.elapsedRealtime();
-                final long uptime = mClocks.uptimeMillis();
+            final long elapsedRealtime = mClocks.elapsedRealtime();
+            final long uptime = mClocks.uptimeMillis();
+
+            boolean updateHistory = false;
+            if (isScreenDoze(state)) {
+                mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
+                mScreenDozeTimer.startRunningLocked(elapsedRealtime);
+                updateHistory = true;
+            } else if (isScreenDoze(oldState)) {
+                mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG;
+                mScreenDozeTimer.stopRunningLocked(elapsedRealtime);
+                updateHistory = true;
+            }
+            if (isScreenOn(state)) {
                 mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
                         + Integer.toHexString(mHistoryCur.states));
-                addHistoryRecordLocked(elapsedRealtime, uptime);
                 mScreenOnTimer.startRunningLocked(elapsedRealtime);
                 if (mScreenBrightnessBin >= 0) {
                     mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(elapsedRealtime);
                 }
-
-                updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), false,
-                        mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
-
-                // Fake a wake lock, so we consider the device waked as long
-                // as the screen is on.
-                noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false,
-                        elapsedRealtime, uptime);
-
-                // Update discharge amounts.
-                if (mOnBatteryInternal) {
-                    updateDischargeScreenLevelsLocked(false, true);
-                }
-            } else if (oldState == Display.STATE_ON) {
-                // Screen turning off or dozing.
-                final long elapsedRealtime = mClocks.elapsedRealtime();
-                final long uptime = mClocks.uptimeMillis();
+                updateHistory = true;
+            } else if (isScreenOn(oldState)) {
                 mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
                         + Integer.toHexString(mHistoryCur.states));
-                addHistoryRecordLocked(elapsedRealtime, uptime);
                 mScreenOnTimer.stopRunningLocked(elapsedRealtime);
                 if (mScreenBrightnessBin >= 0) {
                     mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
                 }
-
+                updateHistory = true;
+            }
+            if (updateHistory) {
+                if (DEBUG_HISTORY) Slog.v(TAG, "Screen state to: "
+                        + Display.stateToString(state));
+                addHistoryRecordLocked(elapsedRealtime, uptime);
+            }
+            if (isScreenOn(state)) {
+                updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
+                        mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
+                // Fake a wake lock, so we consider the device waked as long as the screen is on.
+                noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false,
+                        elapsedRealtime, uptime);
+            } else if (isScreenOn(oldState)) {
                 noteStopWakeLocked(-1, -1, "screen", "screen", WAKE_TYPE_PARTIAL,
                         elapsedRealtime, uptime);
-
-                updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), true,
+                updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
                         mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
-
-                // Update discharge amounts.
-                if (mOnBatteryInternal) {
-                    updateDischargeScreenLevelsLocked(true, false);
-                }
+            }
+            // Update discharge amounts.
+            if (mOnBatteryInternal) {
+                updateDischargeScreenLevelsLocked(oldState, state);
             }
         }
     }
@@ -5391,6 +5413,14 @@
         return mScreenOnTimer.getCountLocked(which);
     }
 
+    @Override public long getScreenDozeTime(long elapsedRealtimeUs, int which) {
+        return mScreenDozeTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+    }
+
+    @Override public int getScreenDozeCount(int which) {
+        return mScreenDozeTimer.getCountLocked(which);
+    }
+
     @Override public long getScreenBrightnessTime(int brightnessBin,
             long elapsedRealtimeUs, int which) {
         return mScreenBrightnessTimer[brightnessBin].getTotalTimeLocked(
@@ -8829,6 +8859,7 @@
         mHandler = new MyHandler(handler.getLooper());
         mStartCount++;
         mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
+        mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
             mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null,
                     mOnBatteryTimeBase);
@@ -8887,6 +8918,7 @@
         mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase);
         mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
         mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
+        mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
         mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
         mOnBattery = mOnBatteryInternal = false;
         long uptime = mClocks.uptimeMillis() * 1000;
@@ -9430,8 +9462,16 @@
         return mCharging;
     }
 
-    public boolean isScreenOn() {
-        return mScreenState == Display.STATE_ON;
+    public boolean isScreenOn(int state) {
+        return state == Display.STATE_ON;
+    }
+
+    public boolean isScreenOff(int state) {
+        return state == Display.STATE_OFF;
+    }
+
+    public boolean isScreenDoze(int state) {
+        return state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND;
     }
 
     void initTimes(long uptime, long realtime) {
@@ -9451,9 +9491,12 @@
         mDischargeAmountScreenOnSinceCharge = 0;
         mDischargeAmountScreenOff = 0;
         mDischargeAmountScreenOffSinceCharge = 0;
+        mDischargeAmountScreenDoze = 0;
+        mDischargeAmountScreenDozeSinceCharge = 0;
         mDischargeStepTracker.init();
         mChargeStepTracker.init();
         mDischargeScreenOffCounter.reset(false);
+        mDischargeScreenDozeCounter.reset(false);
         mDischargeCounter.reset(false);
     }
 
@@ -9471,15 +9514,22 @@
         mOnBatteryTimeBase.reset(uptime, realtime);
         mOnBatteryScreenOffTimeBase.reset(uptime, realtime);
         if ((mHistoryCur.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) == 0) {
-            if (mScreenState == Display.STATE_ON) {
+            if (isScreenOn(mScreenState)) {
                 mDischargeScreenOnUnplugLevel = mHistoryCur.batteryLevel;
+                mDischargeScreenDozeUnplugLevel = 0;
+                mDischargeScreenOffUnplugLevel = 0;
+            } else if (isScreenDoze(mScreenState)) {
+                mDischargeScreenOnUnplugLevel = 0;
+                mDischargeScreenDozeUnplugLevel = mHistoryCur.batteryLevel;
                 mDischargeScreenOffUnplugLevel = 0;
             } else {
                 mDischargeScreenOnUnplugLevel = 0;
+                mDischargeScreenDozeUnplugLevel = 0;
                 mDischargeScreenOffUnplugLevel = mHistoryCur.batteryLevel;
             }
             mDischargeAmountScreenOn = 0;
             mDischargeAmountScreenOff = 0;
+            mDischargeAmountScreenDoze = 0;
         }
         initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
     }
@@ -9490,6 +9540,7 @@
         mStartCount = 0;
         initTimes(uptimeMillis * 1000, elapsedRealtimeMillis * 1000);
         mScreenOnTimer.reset(false);
+        mScreenDozeTimer.reset(false);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
             mScreenBrightnessTimer[i].reset(false);
         }
@@ -9626,33 +9677,52 @@
         }
     }
 
-    void updateDischargeScreenLevelsLocked(boolean oldScreenOn, boolean newScreenOn) {
-        if (oldScreenOn) {
+    void updateDischargeScreenLevelsLocked(int oldState, int newState) {
+        updateOldDischargeScreenLevelLocked(oldState);
+        updateNewDischargeScreenLevelLocked(newState);
+    }
+
+    private void updateOldDischargeScreenLevelLocked(int state) {
+        if (isScreenOn(state)) {
             int diff = mDischargeScreenOnUnplugLevel - mDischargeCurrentLevel;
             if (diff > 0) {
                 mDischargeAmountScreenOn += diff;
                 mDischargeAmountScreenOnSinceCharge += diff;
             }
-        } else {
+        } else if (isScreenDoze(state)) {
+            int diff = mDischargeScreenDozeUnplugLevel - mDischargeCurrentLevel;
+            if (diff > 0) {
+                mDischargeAmountScreenDoze += diff;
+                mDischargeAmountScreenDozeSinceCharge += diff;
+            }
+        } else if (isScreenOff(state)){
             int diff = mDischargeScreenOffUnplugLevel - mDischargeCurrentLevel;
             if (diff > 0) {
                 mDischargeAmountScreenOff += diff;
                 mDischargeAmountScreenOffSinceCharge += diff;
             }
         }
-        if (newScreenOn) {
+    }
+
+    private void updateNewDischargeScreenLevelLocked(int state) {
+        if (isScreenOn(state)) {
             mDischargeScreenOnUnplugLevel = mDischargeCurrentLevel;
             mDischargeScreenOffUnplugLevel = 0;
-        } else {
+            mDischargeScreenDozeUnplugLevel = 0;
+        } else if (isScreenDoze(state)){
             mDischargeScreenOnUnplugLevel = 0;
+            mDischargeScreenDozeUnplugLevel = mDischargeCurrentLevel;
+            mDischargeScreenOffUnplugLevel = 0;
+        } else if (isScreenOff(state)) {
+            mDischargeScreenOnUnplugLevel = 0;
+            mDischargeScreenDozeUnplugLevel = 0;
             mDischargeScreenOffUnplugLevel = mDischargeCurrentLevel;
         }
     }
 
     public void pullPendingStateUpdatesLocked() {
         if (mOnBatteryInternal) {
-            final boolean screenOn = mScreenState == Display.STATE_ON;
-            updateDischargeScreenLevelsLocked(screenOn, screenOn);
+            updateDischargeScreenLevelsLocked(mScreenState, mScreenState);
         }
     }
 
@@ -10785,8 +10855,8 @@
         return false;
     }
 
-    void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
-            final int oldStatus, final int level, final int chargeUAh) {
+    protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
+            final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) {
         boolean doWrite = false;
         Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
         m.arg1 = onBattery ? 1 : 0;
@@ -10794,7 +10864,7 @@
 
         final long uptime = mSecUptime * 1000;
         final long realtime = mSecRealtime * 1000;
-        final boolean screenOn = mScreenState == Display.STATE_ON;
+        final int screenState = mScreenState;
         if (onBattery) {
             // We will reset our status if we are unplugging after the
             // battery was last full, or the level is at 100, or
@@ -10870,16 +10940,23 @@
             }
             addHistoryRecordLocked(mSecRealtime, mSecUptime);
             mDischargeCurrentLevel = mDischargeUnplugLevel = level;
-            if (screenOn) {
+            if (isScreenOn(screenState)) {
                 mDischargeScreenOnUnplugLevel = level;
+                mDischargeScreenDozeUnplugLevel = 0;
+                mDischargeScreenOffUnplugLevel = 0;
+            } else if (isScreenDoze(screenState)) {
+                mDischargeScreenOnUnplugLevel = 0;
+                mDischargeScreenDozeUnplugLevel = level;
                 mDischargeScreenOffUnplugLevel = 0;
             } else {
                 mDischargeScreenOnUnplugLevel = 0;
+                mDischargeScreenDozeUnplugLevel = 0;
                 mDischargeScreenOffUnplugLevel = level;
             }
             mDischargeAmountScreenOn = 0;
+            mDischargeAmountScreenDoze = 0;
             mDischargeAmountScreenOff = 0;
-            updateTimeBasesLocked(true, !screenOn, uptime, realtime);
+            updateTimeBasesLocked(true, screenState, uptime, realtime);
         } else {
             mLastChargingStateLevel = level;
             mOnBattery = mOnBatteryInternal = false;
@@ -10894,8 +10971,8 @@
                 mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
                 mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
             }
-            updateDischargeScreenLevelsLocked(screenOn, screenOn);
-            updateTimeBasesLocked(false, !screenOn, uptime, realtime);
+            updateDischargeScreenLevelsLocked(screenState, screenState);
+            updateTimeBasesLocked(false, screenState, uptime, realtime);
             mChargeStepTracker.init();
             mLastChargeStepLevel = level;
             mMaxChargeStepLevel = level;
@@ -11012,6 +11089,9 @@
                 final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
                 mDischargeCounter.addCountLocked(chargeDiff);
                 mDischargeScreenOffCounter.addCountLocked(chargeDiff);
+                if (isScreenDoze(mScreenState)) {
+                    mDischargeScreenDozeCounter.addCountLocked(chargeDiff);
+                }
             }
             mHistoryCur.batteryChargeUAh = chargeUAh;
             setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level, chargeUAh);
@@ -11054,6 +11134,9 @@
                     final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
                     mDischargeCounter.addCountLocked(chargeDiff);
                     mDischargeScreenOffCounter.addCountLocked(chargeDiff);
+                    if (isScreenDoze(mScreenState)) {
+                        mDischargeScreenDozeCounter.addCountLocked(chargeDiff);
+                    }
                 }
                 mHistoryCur.batteryChargeUAh = chargeUAh;
                 changed = true;
@@ -11362,10 +11445,11 @@
         return dischargeAmount;
     }
 
+    @Override
     public int getDischargeAmountScreenOn() {
         synchronized(this) {
             int val = mDischargeAmountScreenOn;
-            if (mOnBattery && mScreenState == Display.STATE_ON
+            if (mOnBattery && isScreenOn(mScreenState)
                     && mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) {
                 val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel;
             }
@@ -11373,10 +11457,11 @@
         }
     }
 
+    @Override
     public int getDischargeAmountScreenOnSinceCharge() {
         synchronized(this) {
             int val = mDischargeAmountScreenOnSinceCharge;
-            if (mOnBattery && mScreenState == Display.STATE_ON
+            if (mOnBattery && isScreenOn(mScreenState)
                     && mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) {
                 val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel;
             }
@@ -11384,23 +11469,51 @@
         }
     }
 
+    @Override
     public int getDischargeAmountScreenOff() {
         synchronized(this) {
             int val = mDischargeAmountScreenOff;
-            if (mOnBattery && mScreenState != Display.STATE_ON
+            if (mOnBattery && isScreenOff(mScreenState)
                     && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) {
                 val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel;
             }
+            // For backward compatibility, doze discharge is counted into screen off.
+            return val + getDischargeAmountScreenDoze();
+        }
+    }
+
+    @Override
+    public int getDischargeAmountScreenOffSinceCharge() {
+        synchronized(this) {
+            int val = mDischargeAmountScreenOffSinceCharge;
+            if (mOnBattery && isScreenOff(mScreenState)
+                    && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) {
+                val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel;
+            }
+            // For backward compatibility, doze discharge is counted into screen off.
+            return val + getDischargeAmountScreenDozeSinceCharge();
+        }
+    }
+
+    @Override
+    public int getDischargeAmountScreenDoze() {
+        synchronized(this) {
+            int val = mDischargeAmountScreenDoze;
+            if (mOnBattery && isScreenDoze(mScreenState)
+                    && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) {
+                val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel;
+            }
             return val;
         }
     }
 
-    public int getDischargeAmountScreenOffSinceCharge() {
+    @Override
+    public int getDischargeAmountScreenDozeSinceCharge() {
         synchronized(this) {
-            int val = mDischargeAmountScreenOffSinceCharge;
-            if (mOnBattery && mScreenState != Display.STATE_ON
-                    && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) {
-                val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel;
+            int val = mDischargeAmountScreenDozeSinceCharge;
+            if (mOnBattery && isScreenDoze(mScreenState)
+                    && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) {
+                val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel;
             }
             return val;
         }
@@ -11759,12 +11872,14 @@
         mHighDischargeAmountSinceCharge = in.readInt();
         mDischargeAmountScreenOnSinceCharge = in.readInt();
         mDischargeAmountScreenOffSinceCharge = in.readInt();
+        mDischargeAmountScreenDozeSinceCharge = in.readInt();
         mDischargeStepTracker.readFromParcel(in);
         mChargeStepTracker.readFromParcel(in);
         mDailyDischargeStepTracker.readFromParcel(in);
         mDailyChargeStepTracker.readFromParcel(in);
         mDischargeCounter.readSummaryFromParcelLocked(in);
         mDischargeScreenOffCounter.readSummaryFromParcelLocked(in);
+        mDischargeScreenDozeCounter.readSummaryFromParcelLocked(in);
         int NPKG = in.readInt();
         if (NPKG > 0) {
             mDailyPackageChanges = new ArrayList<>(NPKG);
@@ -11787,6 +11902,7 @@
 
         mScreenState = Display.STATE_UNKNOWN;
         mScreenOnTimer.readSummaryFromParcelLocked(in);
+        mScreenDozeTimer.readSummaryFromParcelLocked(in);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
             mScreenBrightnessTimer[i].readSummaryFromParcelLocked(in);
         }
@@ -12180,12 +12296,14 @@
         out.writeInt(getHighDischargeAmountSinceCharge());
         out.writeInt(getDischargeAmountScreenOnSinceCharge());
         out.writeInt(getDischargeAmountScreenOffSinceCharge());
+        out.writeInt(getDischargeAmountScreenDozeSinceCharge());
         mDischargeStepTracker.writeToParcel(out);
         mChargeStepTracker.writeToParcel(out);
         mDailyDischargeStepTracker.writeToParcel(out);
         mDailyChargeStepTracker.writeToParcel(out);
         mDischargeCounter.writeSummaryFromParcelLocked(out);
         mDischargeScreenOffCounter.writeSummaryFromParcelLocked(out);
+        mDischargeScreenDozeCounter.writeSummaryFromParcelLocked(out);
         if (mDailyPackageChanges != null) {
             final int NPKG = mDailyPackageChanges.size();
             out.writeInt(NPKG);
@@ -12203,6 +12321,7 @@
         out.writeLong(mNextMaxDailyDeadline);
 
         mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+        mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
             mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         }
@@ -12635,6 +12754,7 @@
 
         mScreenState = Display.STATE_UNKNOWN;
         mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase, in);
+        mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase, in);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
             mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null,
                     mOnBatteryTimeBase, in);
@@ -12728,10 +12848,13 @@
         mDischargeAmountScreenOnSinceCharge = in.readInt();
         mDischargeAmountScreenOff = in.readInt();
         mDischargeAmountScreenOffSinceCharge = in.readInt();
+        mDischargeAmountScreenDoze = in.readInt();
+        mDischargeAmountScreenDozeSinceCharge = in.readInt();
         mDischargeStepTracker.readFromParcel(in);
         mChargeStepTracker.readFromParcel(in);
         mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
-        mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
+        mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase, in);
+        mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mLastWriteTime = in.readLong();
 
         mRpmStats.clear();
@@ -12848,6 +12971,7 @@
         mOnBatteryScreenOffTimeBase.writeToParcel(out, uSecUptime, uSecRealtime);
 
         mScreenOnTimer.writeToParcel(out, uSecRealtime);
+        mScreenDozeTimer.writeToParcel(out, uSecRealtime);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
             mScreenBrightnessTimer[i].writeToParcel(out, uSecRealtime);
         }
@@ -12910,10 +13034,13 @@
         out.writeInt(mDischargeAmountScreenOnSinceCharge);
         out.writeInt(mDischargeAmountScreenOff);
         out.writeInt(mDischargeAmountScreenOffSinceCharge);
+        out.writeInt(mDischargeAmountScreenDoze);
+        out.writeInt(mDischargeAmountScreenDozeSinceCharge);
         mDischargeStepTracker.writeToParcel(out);
         mChargeStepTracker.writeToParcel(out);
         mDischargeCounter.writeToParcel(out);
         mDischargeScreenOffCounter.writeToParcel(out);
+        mDischargeScreenDozeCounter.writeToParcel(out);
         out.writeLong(mLastWriteTime);
 
         out.writeInt(mRpmStats.size());
@@ -13020,8 +13147,10 @@
             pw.println("mOnBatteryScreenOffTimeBase:");
             mOnBatteryScreenOffTimeBase.dump(pw, "  ");
             Printer pr = new PrintWriterPrinter(pw);
-            pr.println("*** Screen timer:");
+            pr.println("*** Screen on timer:");
             mScreenOnTimer.logState(pr, "  ");
+            pr.println("*** Screen doze timer:");
+            mScreenDozeTimer.logState(pr, "  ");
             for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
                 pr.println("*** Screen brightness #" + i + ":");
                 mScreenBrightnessTimer[i].logState(pr, "  ");
diff --git a/core/java/com/android/internal/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java
index 28f12eb..ba80aea 100644
--- a/core/java/com/android/internal/util/BitUtils.java
+++ b/core/java/com/android/internal/util/BitUtils.java
@@ -93,6 +93,10 @@
         return s & 0xffff;
     }
 
+    public static int uint16(byte hi, byte lo) {
+        return ((hi & 0xff) << 8) | (lo & 0xff);
+    }
+
     public static long uint32(int i) {
         return i & 0xffffffffL;
     }
diff --git a/core/java/com/android/internal/widget/LinearLayoutManager.java b/core/java/com/android/internal/widget/LinearLayoutManager.java
index d82c746..0000a74 100644
--- a/core/java/com/android/internal/widget/LinearLayoutManager.java
+++ b/core/java/com/android/internal/widget/LinearLayoutManager.java
@@ -168,10 +168,6 @@
     /**
      * Constructor used when layout manager is set in XML by RecyclerView attribute
      * "layoutManager". Defaults to vertical orientation.
-     *
-     * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
-     * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
-     * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
      */
     public LinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 18f3177..09e37e1 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -40,6 +40,7 @@
 #include "android_view_InputChannel.h"
 #include "android_view_KeyEvent.h"
 
+#include "android-base/stringprintf.h"
 #include "nativebridge/native_bridge.h"
 #include "nativeloader/native_loader.h"
 
@@ -265,6 +266,8 @@
 
 // ------------------------------------------------------------------------
 
+static thread_local std::string g_error_msg;
+
 static jlong
 loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
         jobject messageQueue, jstring internalDataDir, jstring obbDir,
@@ -277,7 +280,6 @@
     ScopedUtfChars pathStr(env, path);
     std::unique_ptr<NativeCode> code;
     bool needs_native_bridge = false;
-    std::string error_msg;
 
     void* handle = OpenNativeLibrary(env,
                                      sdkVersion,
@@ -285,12 +287,12 @@
                                      classLoader,
                                      libraryPath,
                                      &needs_native_bridge,
-                                     &error_msg);
+                                     &g_error_msg);
 
     if (handle == nullptr) {
         ALOGW("NativeActivity LoadNativeLibrary(\"%s\") failed: %s",
               pathStr.c_str(),
-              error_msg.c_str());
+              g_error_msg.c_str());
         return 0;
     }
 
@@ -306,19 +308,22 @@
     env->ReleaseStringUTFChars(funcName, funcStr);
 
     if (code->createActivityFunc == NULL) {
-        ALOGW("ANativeActivity_onCreate not found");
+        g_error_msg = needs_native_bridge ? NativeBridgeGetError() : dlerror();
+        ALOGW("ANativeActivity_onCreate not found: %s", g_error_msg.c_str());
         return 0;
     }
 
     code->messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueue);
     if (code->messageQueue == NULL) {
-        ALOGW("Unable to retrieve native MessageQueue");
+        g_error_msg = "Unable to retrieve native MessageQueue";
+        ALOGW("%s", g_error_msg.c_str());
         return 0;
     }
 
     int msgpipe[2];
     if (pipe(msgpipe)) {
-        ALOGW("could not create pipe: %s", strerror(errno));
+        g_error_msg = android::base::StringPrintf("could not create pipe: %s", strerror(errno));
+        ALOGW("%s", g_error_msg.c_str());
         return 0;
     }
     code->mainWorkRead = msgpipe[0];
@@ -334,7 +339,8 @@
 
     code->ANativeActivity::callbacks = &code->callbacks;
     if (env->GetJavaVM(&code->vm) < 0) {
-        ALOGW("NativeActivity GetJavaVM failed");
+        g_error_msg = "NativeActivity GetJavaVM failed";
+        ALOGW("%s", g_error_msg.c_str());
         return 0;
     }
     code->env = env;
@@ -381,7 +387,9 @@
 }
 
 static jstring getDlError_native(JNIEnv* env, jobject clazz) {
-  return env->NewStringUTF(dlerror());
+  jstring result = env->NewStringUTF(g_error_msg.c_str());
+  g_error_msg.clear();
+  return result;
 }
 
 static void
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a9fda8e..6a9afbc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3106,6 +3106,13 @@
     <permission android:name="android.permission.BIND_APPWIDGET"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to bind app's slices and get their
+         content. This content will be surfaced to the
+         user and not to leave the device.
+         <p>Not for use by third-party applications. @hide -->
+    <permission android:name="android.permission.BIND_SLICE"
+        android:protectionLevel="signature|privileged|development" />
+
     <!-- @SystemApi Private permission, to restrict who can bring up a dialog to add a new
          keyguard widget
          @hide -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6bf5c63..dcb56a2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3092,4 +3092,8 @@
          booted.  -->
     <integer name="config_stableDeviceDisplayWidth">-1</integer>
     <integer name="config_stableDeviceDisplayHeight">-1</integer>
+
+    <!-- Decide whether to display 'No service' on status bar instead of 'Emergency calls only'
+         when SIM is unready. -->
+    <bool name="config_display_no_service_when_sim_unready">false</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a5dbe26..cc74f17 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3099,4 +3099,5 @@
 
   <java-symbol type="integer" name="config_stableDeviceDisplayWidth" />
   <java-symbol type="integer" name="config_stableDeviceDisplayHeight" />
+  <java-symbol type="bool" name="config_display_no_service_when_sim_unready" />
 </resources>
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
index eff6ad9..c611a01 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
@@ -22,6 +22,7 @@
 import android.os.WorkSource;
 import android.support.test.filters.SmallTest;
 import android.util.ArrayMap;
+import android.view.Display;
 
 import junit.framework.TestCase;
 
@@ -44,7 +45,7 @@
         // Off-battery, non-existent
         clocks.realtime = clocks.uptime = 10;
         cur = clocks.realtime * 1000;
-        bi.updateTimeBasesLocked(false, false, cur, cur); // off battery
+        bi.updateTimeBasesLocked(false, Display.STATE_ON, cur, cur); // off battery
         assertFalse(bgtb.isRunning());
         assertEquals(0, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED));
 
@@ -65,7 +66,7 @@
         // On-battery, background
         clocks.realtime = clocks.uptime = 303;
         cur = clocks.realtime * 1000;
-        bi.updateTimeBasesLocked(true, false, cur, cur); // on battery
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, cur, cur); // on battery
         // still in ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
         assertTrue(bgtb.isRunning());
         assertEquals(0, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED));
@@ -73,7 +74,7 @@
         // On-battery, background - but change screen state
         clocks.realtime = clocks.uptime = 409;
         cur = clocks.realtime * 1000;
-        bi.updateTimeBasesLocked(true, true, cur, cur); // on battery (again)
+        bi.updateTimeBasesLocked(true, Display.STATE_OFF, cur, cur); // on battery (again)
         assertTrue(bgtb.isRunning());
         assertEquals(106_000, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED));
 
@@ -87,7 +88,7 @@
         // Off-battery, foreground
         clocks.realtime = clocks.uptime = 530;
         cur = clocks.realtime * 1000;
-        bi.updateTimeBasesLocked(false, false, cur, cur); // off battery
+        bi.updateTimeBasesLocked(false, Display.STATE_ON, cur, cur); // off battery
         bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
         assertFalse(bgtb.isRunning());
         assertEquals(227_000, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED));
@@ -112,17 +113,17 @@
         // battery=off, screen=off, background=off
         cur = (clocks.realtime = clocks.uptime = 100) * 1000;
         bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
-        bi.updateTimeBasesLocked(false, false, cur, cur);
+        bi.updateTimeBasesLocked(false, Display.STATE_ON, cur, cur);
         assertFalse(bgtb.isRunning());
 
         // battery=on, screen=off, background=off
         cur = (clocks.realtime = clocks.uptime = 200) * 1000;
-        bi.updateTimeBasesLocked(true, false, cur, cur);
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, cur, cur);
         assertFalse(bgtb.isRunning());
 
         // battery=on, screen=on, background=off
         cur = (clocks.realtime = clocks.uptime = 300) * 1000;
-        bi.updateTimeBasesLocked(true, true, cur, cur);
+        bi.updateTimeBasesLocked(true, Display.STATE_OFF, cur, cur);
         assertFalse(bgtb.isRunning());
 
         // battery=on, screen=on, background=on
@@ -133,12 +134,12 @@
 
         // battery=on, screen=off, background=on
         cur = (clocks.realtime = clocks.uptime = 550) * 1000;
-        bi.updateTimeBasesLocked(true, false, cur, cur);
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, cur, cur);
         assertFalse(bgtb.isRunning());
 
         // battery=off, screen=off, background=on
         cur = (clocks.realtime = clocks.uptime = 660) * 1000;
-        bi.updateTimeBasesLocked(false, false, cur, cur);
+        bi.updateTimeBasesLocked(false, Display.STATE_ON, cur, cur);
         assertFalse(bgtb.isRunning());
 
         // battery=off, screen=off, background=off
@@ -157,7 +158,7 @@
 
         // On battery
         curr = 1000 * (clocks.realtime = clocks.uptime = 100);
-        bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
         // App in foreground
         bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
 
@@ -171,7 +172,7 @@
 
         // Off battery
         curr = 1000 * (clocks.realtime = clocks.uptime = 305);
-        bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+        bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr); // off battery
 
         // Stop timer
         curr = 1000 * (clocks.realtime = clocks.uptime = 409);
@@ -200,7 +201,7 @@
 
         // On battery
         curr = 1000 * (clocks.realtime = clocks.uptime = 100);
-        bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
 
         // App in foreground
         bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
@@ -215,7 +216,7 @@
 
         // Off battery
         curr = 1000 * (clocks.realtime = clocks.uptime = 305);
-        bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+        bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr); // off battery
 
         // Start timer (unoptimized)
         curr = 1000 * (clocks.realtime = clocks.uptime = 1000);
@@ -223,7 +224,7 @@
 
         // On battery
         curr = 1000 * (clocks.realtime = clocks.uptime = 2001);
-        bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
 
         // Move to foreground
         curr = 1000 * (clocks.realtime = clocks.uptime = 3004);
@@ -270,7 +271,7 @@
 
         // On battery
         curr = 1000 * (clocks.realtime = clocks.uptime = 100);
-        bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
         // App in foreground
         bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
 
@@ -292,7 +293,7 @@
 
         // Off battery
         curr = 1000 * (clocks.realtime = clocks.uptime = 305);
-        bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+        bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr); // off battery
 
         // Stop timer
         curr = 1000 * (clocks.realtime = clocks.uptime = 409);
@@ -331,7 +332,7 @@
 
         // On battery
         curr = 1000 * (clocks.realtime = clocks.uptime = 100);
-        bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
         // App in foreground
         bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
 
@@ -353,7 +354,7 @@
 
         // Off battery
         curr = 1000 * (clocks.realtime = clocks.uptime = 305);
-        bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+        bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr); // off battery
 
         // Stop timer
         curr = 1000 * (clocks.realtime = clocks.uptime = 409);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index 97b54b1..6ff0ab2 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -39,6 +39,7 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.SparseLongArray;
+import android.view.Display;
 
 import com.android.internal.util.ArrayUtils;
 
@@ -179,7 +180,7 @@
     @Test
     public void testUpdateClusterSpeedTimes() {
         // PRECONDITIONS
-        updateTimeBasesLocked(true, false, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
         final long[][] clusterSpeedTimesMs = {{20, 30}, {40, 50, 60}};
         initKernelCpuSpeedReaders(clusterSpeedTimesMs.length);
         for (int i = 0; i < clusterSpeedTimesMs.length; ++i) {
@@ -224,7 +225,7 @@
     @Test
     public void testReadKernelUidCpuTimesLocked() {
         //PRECONDITIONS
-        updateTimeBasesLocked(true, false, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
         final int[] testUids = getUids(testUserId, new int[] {
@@ -295,7 +296,7 @@
     @Test
     public void testReadKernelUidCpuTimesLocked_isolatedUid() {
         //PRECONDITIONS
-        updateTimeBasesLocked(true, false, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
         final int isolatedAppId = FIRST_ISOLATED_UID + 27;
@@ -382,7 +383,7 @@
     @Test
     public void testReadKernelUidCpuTimesLocked_invalidUid() {
         //PRECONDITIONS
-        updateTimeBasesLocked(true, false, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
         final int testUserId = 11;
         final int invalidUserId = 15;
         final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
@@ -427,7 +428,7 @@
     @Test
     public void testReadKernelUidCpuTimesLocked_withPartialTimers() {
         //PRECONDITIONS
-        updateTimeBasesLocked(true, false, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
         final int[] testUids = getUids(testUserId, new int[] {
@@ -509,7 +510,7 @@
     @Test
     public void testReadKernelUidCpuFreqTimesLocked() {
         // PRECONDITIONS
-        updateTimeBasesLocked(true, false, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
 
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -550,7 +551,7 @@
         // Repeat the test when the screen is off.
 
         // PRECONDITIONS
-        updateTimeBasesLocked(true, true, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
         final long[][] deltasMs = {
                 {3, 12, 55, 100, 32},
                 {3248327490475l, 232349349845043l, 123, 2398, 0},
@@ -584,7 +585,7 @@
     @Test
     public void testReadKernelUidCpuFreqTimesLocked_perClusterTimesAvailable() {
         // PRECONDITIONS
-        updateTimeBasesLocked(true, false, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
 
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -644,7 +645,7 @@
         // Repeat the test when the screen is off.
 
         // PRECONDITIONS
-        updateTimeBasesLocked(true, true, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
         final long[][] deltasMs = {
                 {3, 12, 55, 100, 32},
                 {3248327490475l, 232349349845043l, 123, 2398, 0},
@@ -688,7 +689,7 @@
     @Test
     public void testReadKernelUidCpuFreqTimesLocked_partialTimers() {
         // PRECONDITIONS
-        updateTimeBasesLocked(true, false, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
 
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -792,7 +793,7 @@
     @Test
     public void testReadKernelUidCpuFreqTimesLocked_freqsChanged() {
         // PRECONDITIONS
-        updateTimeBasesLocked(true, false, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
 
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -833,7 +834,7 @@
         // Repeat the test with the freqs from proc file changed.
 
         // PRECONDITIONS
-        updateTimeBasesLocked(true, true, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
         final long[][] deltasMs = {
                 {3, 12, 55, 100, 32, 34984, 27983},
                 {3248327490475l, 232349349845043l, 123, 2398, 0, 398, 0},
@@ -867,7 +868,7 @@
     @Test
     public void testReadKernelUidCpuFreqTimesLocked_isolatedUid() {
         // PRECONDITIONS
-        updateTimeBasesLocked(true, false, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
 
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -961,7 +962,7 @@
     @Test
     public void testReadKernelUiidCpuFreqTimesLocked_invalidUid() {
         // PRECONDITIONS
-        updateTimeBasesLocked(true, false, 0, 0);
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
 
         final int testUserId = 11;
         final int invalidUserId = 15;
@@ -1008,12 +1009,12 @@
         verify(mKernelUidCpuFreqTimeReader).removeUid(invalidUid);
     }
 
-    private void updateTimeBasesLocked(boolean unplugged, boolean screenOff,
+    private void updateTimeBasesLocked(boolean unplugged, int screenState,
             long upTime, long realTime) {
         // Set PowerProfile=null before calling updateTimeBasesLocked to avoid execution of
         // BatteryStatsImpl.updateCpuTimeLocked
         mBatteryStatsImpl.setPowerProfile(null);
-        mBatteryStatsImpl.updateTimeBasesLocked(unplugged, screenOff, upTime, realTime);
+        mBatteryStatsImpl.updateTimeBasesLocked(unplugged, screenState, upTime, realTime);
         mBatteryStatsImpl.setPowerProfile(mPowerProfile);
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 7769e69..461d537 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -19,9 +19,11 @@
 import static android.os.BatteryStats.WAKE_TYPE_PARTIAL;
 
 import android.app.ActivityManager;
+import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.WorkSource;
 import android.support.test.filters.SmallTest;
+import android.view.Display;
 
 import junit.framework.TestCase;
 
@@ -50,7 +52,7 @@
     @SmallTest
     public void testNoteBluetoothScanResultLocked() throws Exception {
         MockBatteryStatsImpl bi = new MockBatteryStatsImpl(new MockClocks());
-        bi.updateTimeBasesLocked(true, true, 0, 0);
+        bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
         bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
 
         bi.noteBluetoothScanResultsFromSourceLocked(WS, 1);
@@ -82,7 +84,7 @@
         int pid = 10;
         String name = "name";
 
-        bi.updateTimeBasesLocked(true, true, 0, 0);
+        bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
         bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
         bi.getUidStatsLocked(UID).noteStartWakeLocked(pid, name, WAKE_TYPE_PARTIAL, clocks.realtime);
 
@@ -124,7 +126,7 @@
         stateRuntimeMap.put(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT, 5309);
         stateRuntimeMap.put(ActivityManager.PROCESS_STATE_CACHED_EMPTY, 42);
 
-        bi.updateTimeBasesLocked(true, false, 0, 0);
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
 
         for (Map.Entry<Integer, Integer> entry : stateRuntimeMap.entrySet()) {
             bi.noteUidProcessStateLocked(UID, entry.getKey());
@@ -189,4 +191,46 @@
         expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
         assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
     }
+
+    /** Test BatteryStatsImpl.updateTimeBasesLocked. */
+    @SmallTest
+    public void testUpdateTimeBasesLocked() throws Exception {
+        final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+        bi.updateTimeBasesLocked(false, Display.STATE_OFF, 0, 0);
+        assertFalse(bi.getOnBatteryTimeBase().isRunning());
+        bi.updateTimeBasesLocked(false, Display.STATE_DOZE, 10, 10);
+        assertFalse(bi.getOnBatteryTimeBase().isRunning());
+        bi.updateTimeBasesLocked(false, Display.STATE_ON, 20, 20);
+        assertFalse(bi.getOnBatteryTimeBase().isRunning());
+
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, 30, 30);
+        assertTrue(bi.getOnBatteryTimeBase().isRunning());
+        assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning());
+        bi.updateTimeBasesLocked(true, Display.STATE_DOZE, 40, 40);
+        assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning());
+        bi.updateTimeBasesLocked(true, Display.STATE_OFF, 40, 40);
+        assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning());
+    }
+
+    /** Test BatteryStatsImpl.noteScreenStateLocked. */
+    @SmallTest
+    public void testNoteScreenStateLocked() throws Exception {
+        final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+        bi.noteScreenStateLocked(Display.STATE_ON);
+        bi.noteScreenStateLocked(Display.STATE_DOZE);
+        assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning());
+        assertEquals(bi.getScreenState(), Display.STATE_DOZE);
+        bi.noteScreenStateLocked(Display.STATE_ON);
+        assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning());
+        assertEquals(bi.getScreenState(), Display.STATE_ON);
+        bi.noteScreenStateLocked(Display.STATE_OFF);
+        assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning());
+        assertEquals(bi.getScreenState(), Display.STATE_OFF);
+    }
+
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index 0294095..a751f90 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -18,6 +18,7 @@
 import android.app.ActivityManager;
 import android.os.BatteryStats;
 import android.support.test.filters.SmallTest;
+import android.view.Display;
 
 import junit.framework.TestCase;
 
@@ -74,7 +75,7 @@
 
         // Plugged-in (battery=off, sensor=off)
         curr = 1000 * (clocks.realtime = clocks.uptime = 100);
-        bi.updateTimeBasesLocked(false, false, curr, curr);
+        bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr);
 
 
         // Start sensor (battery=off, sensor=on)
@@ -110,7 +111,7 @@
 
         // Unplugged (battery=on, sensor=off)
         curr = 1000 * (clocks.realtime = clocks.uptime = 100);
-        bi.updateTimeBasesLocked(true, false, curr, curr);
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr);
 
         // Start sensor (battery=on, sensor=on)
         curr = 1000 * (clocks.realtime = clocks.uptime = 200);
@@ -145,7 +146,7 @@
 
         // On battery (battery=on, sensor=off)
         curr = 1000 * (clocks.realtime = clocks.uptime = 100);
-        bi.updateTimeBasesLocked(true, false, curr, curr);
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr);
         bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
 
         // Start sensor (battery=on, sensor=on)
@@ -154,7 +155,7 @@
 
         // Off battery (battery=off, sensor=on)
         curr = 1000 * (clocks.realtime = clocks.uptime = 305);
-        bi.updateTimeBasesLocked(false, false, curr, curr);
+        bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr);
 
         // Stop sensor while off battery (battery=off, sensor=off)
         curr = 1000 * (clocks.realtime = clocks.uptime = 409);
@@ -191,7 +192,7 @@
 
         // Plugged-in (battery=off, sensor=off)
         curr = 1000 * (clocks.realtime = clocks.uptime = 100);
-        bi.updateTimeBasesLocked(false, false, curr, curr);
+        bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr);
 
         // Start sensor (battery=off, sensor=on)
         curr = 1000 * (clocks.realtime = clocks.uptime = 200);
@@ -209,7 +210,7 @@
 
         // Unplug (battery=on, sensor=on)
         curr = 1000 * (clocks.realtime = clocks.uptime = 305);
-        bi.updateTimeBasesLocked(true, false, curr, curr);
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr);
 
         //Test situation
         curr = 1000 * (clocks.realtime = clocks.uptime = 410);
@@ -243,7 +244,7 @@
         long curr = 0; // realtime in us
         // Entire test is on-battery
         curr = 1000 * (clocks.realtime = clocks.uptime = 1000);
-        bi.updateTimeBasesLocked(true, false, curr, curr);
+        bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr);
 
         // See below for a diagram of events.
 
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 73ee3e5..63d1e5a 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -28,6 +28,10 @@
     MockBatteryStatsImpl(Clocks clocks) {
         super(clocks);
         this.clocks = mClocks;
+        mScreenOnTimer = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null,
+                mOnBatteryTimeBase);
+        mScreenDozeTimer = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null,
+                mOnBatteryTimeBase);
         mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
     }
 
@@ -39,6 +43,14 @@
         return mOnBatteryTimeBase;
     }
 
+    public TimeBase getOnBatteryScreenOffTimeBase() {
+        return mOnBatteryScreenOffTimeBase;
+    }
+
+    public int getScreenState() {
+        return mScreenState;
+    }
+
     public boolean isOnBattery() {
         return mForceOnBattery ? true : super.isOnBattery();
     }
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 5c6078d..3b72a8b 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -72,15 +72,23 @@
             textureMatrixInv = textureMatrix;
         }
 
-        SkMatrix matrix = SkMatrix::Concat(textureMatrixInv, layerTransform);
+        SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrixInv);
 
         SkPaint paint;
         paint.setAlpha(layer->getAlpha());
         paint.setBlendMode(layer->getMode());
         paint.setColorFilter(sk_ref_sp(layer->getColorFilter()));
-        // draw image with a shader to avoid save/restore of the matrix
-        paint.setShader(layerImage->makeShader(&matrix));
-        canvas->drawRect(SkRect::MakeWH(layerWidth, layerHeight), paint);
+
+        const bool nonIdentityMatrix = !matrix.isIdentity();
+        if (nonIdentityMatrix) {
+            canvas->save();
+            canvas->concat(matrix);
+        }
+        canvas->drawImage(layerImage.get(), 0, 0, &paint);
+        // restore the original matrix
+        if (nonIdentityMatrix) {
+            canvas->restore();
+        }
     }
 
     return layerImage;
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 1f5edfa..ba41a7b 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -2584,22 +2584,21 @@
                             ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder));
                 }
 
-                // Note that the rotation angle from MediaMetadataRetriever for heif images
-                // are CCW, while rotation in ExifInterface orientations are CW.
                 String rotation = retriever.extractMetadata(
                         MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
                 if (rotation != null) {
                     int orientation = ExifInterface.ORIENTATION_NORMAL;
 
+                    // all rotation angles in CW
                     switch (Integer.parseInt(rotation)) {
                         case 90:
-                            orientation = ExifInterface.ORIENTATION_ROTATE_270;
+                            orientation = ExifInterface.ORIENTATION_ROTATE_90;
                             break;
                         case 180:
                             orientation = ExifInterface.ORIENTATION_ROTATE_180;
                             break;
                         case 270:
-                            orientation = ExifInterface.ORIENTATION_ROTATE_90;
+                            orientation = ExifInterface.ORIENTATION_ROTATE_270;
                             break;
                     }
 
diff --git a/native/webview/loader/loader.cpp b/native/webview/loader/loader.cpp
index 376dbb8..adb371d 100644
--- a/native/webview/loader/loader.cpp
+++ b/native/webview/loader/loader.cpp
@@ -143,17 +143,7 @@
   return DoReserveAddressSpace(size);
 }
 
-jboolean CreateRelroFile(JNIEnv* env, jclass, jstring lib32, jstring lib64,
-                         jstring relro32, jstring relro64) {
-#ifdef __LP64__
-  jstring lib = lib64;
-  jstring relro = relro64;
-  (void)lib32; (void)relro32;
-#else
-  jstring lib = lib32;
-  jstring relro = relro32;
-  (void)lib64; (void)relro64;
-#endif
+jboolean CreateRelroFile(JNIEnv* env, jclass, jstring lib, jstring relro) {
   jboolean ret = JNI_FALSE;
   const char* lib_utf8 = env->GetStringUTFChars(lib, NULL);
   if (lib_utf8 != NULL) {
@@ -167,15 +157,8 @@
   return ret;
 }
 
-jint LoadWithRelroFile(JNIEnv* env, jclass, jstring lib, jstring relro32,
-                       jstring relro64, jobject clazzLoader) {
-#ifdef __LP64__
-  jstring relro = relro64;
-  (void)relro32;
-#else
-  jstring relro = relro32;
-  (void)relro64;
-#endif
+jint LoadWithRelroFile(JNIEnv* env, jclass, jstring lib, jstring relro,
+                       jobject clazzLoader) {
   jint ret = LIBLOAD_FAILED_JNI_CALL;
   const char* lib_utf8 = env->GetStringUTFChars(lib, NULL);
   if (lib_utf8 != NULL) {
@@ -196,10 +179,10 @@
   { "nativeReserveAddressSpace", "(J)Z",
       reinterpret_cast<void*>(ReserveAddressSpace) },
   { "nativeCreateRelroFile",
-      "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+      "(Ljava/lang/String;Ljava/lang/String;)Z",
       reinterpret_cast<void*>(CreateRelroFile) },
   { "nativeLoadWithRelroFile",
-      "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)I",
+      "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)I",
       reinterpret_cast<void*>(LoadWithRelroFile) },
 };
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index 7225ba9..432b406 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -66,7 +66,11 @@
                 // again when the PUK locked SIM is re-entered.
                 case ABSENT: {
                     KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
-                    mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+                    // onSimStateChanged callback can fire when the SIM PIN lock is not currently
+                    // active and mCallback is null.
+                    if (mCallback != null) {
+                        mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+                    }
                     break;
                 }
                 default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 171cf23..7f79008 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -72,7 +72,11 @@
                 // move into the READY state and the PUK lock keyguard should be removed.
                 case READY: {
                     KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
-                    mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+                    // mCallback can be null if onSimStateChanged callback is called when keyguard
+                    // isn't active.
+                    if (mCallback != null) {
+                        mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+                    }
                     break;
                 }
                 default:
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index f545556..86b7790 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -358,6 +358,9 @@
         mScrimViews = new SystemBarScrimViews(this);
         getWindow().getAttributes().privateFlags |=
                 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
+        if (Recents.getConfiguration().isLowRamDevice) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+        }
 
         mLastConfig = new Configuration(Utilities.getAppConfiguration(this));
         mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 2cff79d..561fbb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -796,6 +796,24 @@
         }
     }
 
+    /**
+     * This method returns the drawing rect for the view which is different from the regular
+     * drawing rect, since we layout all children at position 0 and usually the translation is
+     * neglected. The standard implementation doesn't account for translation.
+     *
+     * @param outRect The (scrolled) drawing bounds of the view.
+     */
+    @Override
+    public void getDrawingRect(Rect outRect) {
+        super.getDrawingRect(outRect);
+        float translationX = getTranslationX();
+        float translationY = getTranslationY();
+        outRect.left += translationX;
+        outRect.right += translationX;
+        outRect.top += translationY;
+        outRect.bottom += translationY;
+    }
+
     public void setIsInShelf(boolean isInShelf) {
         mIsInShelf = isInShelf;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c191618..7b11ace 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -667,7 +667,7 @@
             return false;
         }
         initDownStates(event);
-        if (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
+        if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
             mIsExpansionFromHeadsUp = true;
             MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
             MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1);
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 54b4e35..d3bb17f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -479,7 +479,8 @@
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
 
     // ensure quick settings is disabled until the current user makes it through the setup wizard
-    private boolean mUserSetup = false;
+    @VisibleForTesting
+    protected boolean mUserSetup = false;
     private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
         @Override
         public void onUserSetupChanged() {
@@ -1978,6 +1979,7 @@
         mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned()
                 && (mUserSetup || mUserSwitcherController == null
                         || !mUserSwitcherController.isSimpleUserSwitcher())
+                && ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0)
                 && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
                 && !mDozing
                 && !ONLY_CORE_APPS);
@@ -2533,6 +2535,8 @@
         flagdbg.append(0 != ((diff2  & StatusBarManager.DISABLE2_QUICK_SETTINGS))       ? '!' : ' ');
         flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_SYSTEM_ICONS))         ? 'I' : 'i');
         flagdbg.append(0 != ((diff2  & StatusBarManager.DISABLE2_SYSTEM_ICONS))         ? '!' : ' ');
+        flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE))   ? 'N' : 'n');
+        flagdbg.append(0 != ((diff2  & StatusBarManager.DISABLE2_NOTIFICATION_SHADE))   ? '!' : ' ');
         flagdbg.append('>');
         Log.d(TAG, flagdbg.toString());
 
@@ -2559,6 +2563,13 @@
         if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
             updateQsExpansionEnabled();
         }
+
+        if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+            updateQsExpansionEnabled();
+            if ((state1 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+                animateCollapsePanels();
+            }
+        }
     }
 
     /**
@@ -2915,7 +2926,9 @@
     }
 
     boolean panelsEnabled() {
-        return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0 && !ONLY_CORE_APPS;
+        return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0
+                && (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
+                && !ONLY_CORE_APPS;
     }
 
     void makeExpandedVisible(boolean force) {
@@ -4844,6 +4857,10 @@
      * @param expandView The view to expand after going to the shade.
      */
     public void goToLockedShade(View expandView) {
+        if ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+            return;
+        }
+
         int userId = mCurrentUserId;
         ExpandableNotificationRow row = null;
         if (expandView instanceof ExpandableNotificationRow) {
@@ -5732,6 +5749,11 @@
         }
 
         private boolean handleRemoteInput(View view, PendingIntent pendingIntent) {
+            if ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+                // Skip remote input as doing so will expand the notification shade.
+                return true;
+            }
+
             Object tag = view.getTag(com.android.internal.R.id.remote_input_tag);
             RemoteInput[] inputs = null;
             if (tag instanceof RemoteInput[]) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index c0de004..3b401a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -21,9 +21,11 @@
 
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
 import com.android.internal.app.NightDisplayController;
+import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.Prefs.Key;
 import com.android.systemui.SysuiTestCase;
@@ -45,6 +47,8 @@
 
     @Before
     public void setUp() throws Exception {
+        mDependency.injectTestDependency(Dependency.BG_LOOPER,
+                TestableLooper.get(this).getLooper());
         Prefs.putBoolean(mContext, Key.QS_NIGHTDISPLAY_ADDED, false);
         mQsTileHost = Mockito.mock(QSTileHost.class);
         mAutoTileManager = new AutoTileManager(mContext, mQsTileHost);
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 ac367d2..899e873 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
@@ -34,8 +34,8 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
+import android.app.StatusBarManager;
 import android.app.trust.TrustManager;
-import android.content.Context;
 import android.hardware.fingerprint.FingerprintManager;
 import android.metrics.LogMaker;
 import android.os.Binder;
@@ -50,21 +50,17 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.metricshelper.MetricsAsserts;
 import android.testing.AndroidTestingRunner;
-import android.testing.LayoutInflaterBuilder;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.MessageHandler;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.DisplayMetrics;
-import android.view.View;
 import android.view.ViewGroup.LayoutParams;
-import android.widget.FrameLayout;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.logging.testing.FakeMetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.keyguard.KeyguardHostView.OnDismissAction;
-import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.assist.AssistManager;
@@ -76,7 +72,6 @@
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.policy.DateView;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -496,6 +491,28 @@
     }
 
     @Test
+    public void testDisableExpandStatusBar() {
+        mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+        mStatusBar.setUserSetupForTest(true);
+        when(mStatusBar.isDeviceProvisioned()).thenReturn(true);
+
+        mStatusBar.disable(StatusBarManager.DISABLE_NONE,
+                StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
+        verify(mNotificationPanelView).setQsExpansionEnabled(false);
+        mStatusBar.animateExpandNotificationsPanel();
+        verify(mNotificationPanelView, never()).expand(anyBoolean());
+        mStatusBar.animateExpandSettingsPanel(null);
+        verify(mNotificationPanelView, never()).expand(anyBoolean());
+
+        mStatusBar.disable(StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NONE, false);
+        verify(mNotificationPanelView).setQsExpansionEnabled(true);
+        mStatusBar.animateExpandNotificationsPanel();
+        verify(mNotificationPanelView).expand(anyBoolean());
+        mStatusBar.animateExpandSettingsPanel(null);
+        verify(mNotificationPanelView).expand(anyBoolean());
+    }
+
+    @Test
     public void testDump_DoesNotCrash() {
         mStatusBar.dump(null, new PrintWriter(new ByteArrayOutputStream()), null);
     }
@@ -546,5 +563,9 @@
         public void setBarStateForTest(int state) {
             mState = state;
         }
+
+        public void setUserSetupForTest(boolean userSetup) {
+            mUserSetup = userSetup;
+        }
     }
 }
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 11478fe..ed00ffe 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -56,9 +56,11 @@
 import android.service.autofill.FillContext;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
+import android.service.autofill.InternalSanitizer;
 import android.service.autofill.InternalValidator;
 import android.service.autofill.SaveInfo;
 import android.service.autofill.SaveRequest;
+import android.service.autofill.Transformation;
 import android.service.autofill.ValueFinder;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -856,6 +858,8 @@
             return true;
         }
 
+        final ArrayMap<AutofillId, InternalSanitizer> sanitizers = createSanitizers(saveInfo);
+
         // Cache used to make sure changed fields do not belong to a dataset.
         final ArrayMap<AutofillId, AutofillValue> currentValues = new ArrayMap<>();
         final ArraySet<AutofillId> allIds = new ArraySet<>();
@@ -895,6 +899,7 @@
                         break;
                     }
                 }
+                value = getSanitizedValue(sanitizers, id, value);
                 currentValues.put(id, value);
                 final AutofillValue filledValue = viewState.getAutofilledValue();
 
@@ -1037,6 +1042,48 @@
         return true;
     }
 
+    @Nullable
+    private ArrayMap<AutofillId, InternalSanitizer> createSanitizers(@Nullable SaveInfo saveInfo) {
+        if (saveInfo == null) return null;
+
+        final InternalSanitizer[] sanitizerKeys = saveInfo.getSanitizerKeys();
+        if (sanitizerKeys == null) return null;
+
+        final int size = sanitizerKeys.length ;
+        final ArrayMap<AutofillId, InternalSanitizer> sanitizers = new ArrayMap<>(size);
+        if (sDebug) Slog.d(TAG, "Service provided " + size + " sanitizers");
+        final AutofillId[][] sanitizerValues = saveInfo.getSanitizerValues();
+        for (int i = 0; i < size; i++) {
+            final InternalSanitizer sanitizer = sanitizerKeys[i];
+            final AutofillId[] ids = sanitizerValues[i];
+            if (sDebug) {
+                Slog.d(TAG, "sanitizer #" + i + " (" + sanitizer + ") for ids "
+                        + Arrays.toString(ids));
+            }
+            for (AutofillId id : ids) {
+                sanitizers.put(id, sanitizer);
+            }
+        }
+        return sanitizers;
+    }
+
+    @NonNull
+    private AutofillValue getSanitizedValue(
+            @Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers,
+            @NonNull AutofillId id,
+            @NonNull AutofillValue value) {
+        if (sanitizers == null) return value;
+
+        final InternalSanitizer sanitizer = sanitizers.get(id);
+        if (sanitizer == null) {
+            return value;
+        }
+
+        final AutofillValue sanitized = sanitizer.sanitize(value);
+        if (sDebug) Slog.d(TAG, "Value for " + id + "(" + value + ") sanitized to " + sanitized);
+        return sanitized;
+    }
+
     /**
      * Returns whether the session is currently showing the save UI
      */
@@ -1100,6 +1147,9 @@
             return;
         }
 
+        final ArrayMap<AutofillId, InternalSanitizer> sanitizers =
+                createSanitizers(getSaveInfoLocked());
+
         final int numContexts = mContexts.size();
 
         for (int contextNum = 0; contextNum < numContexts; contextNum++) {
@@ -1126,7 +1176,9 @@
                 }
                 if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value);
 
-                node.updateAutofillValue(value);
+                final AutofillValue sanitizedValue = getSanitizedValue(sanitizers, id, value);
+
+                node.updateAutofillValue(sanitizedValue);
             }
 
             // Sanitize structure before it's sent to service.
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 371e74d..bf442dc 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -55,6 +55,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.regex.Pattern;
 
 final class FillUi {
     private static final String TAG = "FillUi";
@@ -164,15 +165,18 @@
                         Slog.e(TAG, "Error inflating remote views", e);
                         continue;
                     }
-                    final AutofillValue value = dataset.getFieldValues().get(index);
+                    final Pattern filter = dataset.getFilter(index);
                     String valueText = null;
-                    // If the dataset needs auth - don't add its text to allow guessing
-                    // its content based on how filtering behaves.
-                    if (value != null && value.isText() && dataset.getAuthentication() == null) {
-                        valueText = value.getTextValue().toString().toLowerCase();
+                    if (filter == null) {
+                        final AutofillValue value = dataset.getFieldValues().get(index);
+                        // If the dataset needs auth - don't add its text to allow guessing
+                        // its content based on how filtering behaves.
+                        if (value != null && value.isText() && dataset.getAuthentication() == null) {
+                            valueText = value.getTextValue().toString().toLowerCase();
+                        }
                     }
 
-                    items.add(new ViewItem(dataset, valueText, view));
+                    items.add(new ViewItem(dataset, filter, valueText, view));
                 }
             }
 
@@ -331,11 +335,17 @@
         private final String mValue;
         private final Dataset mDataset;
         private final View mView;
+        private final Pattern mFilter;
 
-        ViewItem(Dataset dataset, String value, View view) {
+        ViewItem(Dataset dataset, Pattern filter, String value, View view) {
             mDataset = dataset;
             mValue = value;
             mView = view;
+            mFilter = filter;
+        }
+
+        public Pattern getFilter() {
+            return mFilter;
         }
 
         public View getView() {
@@ -349,12 +359,6 @@
         public String getValue() {
             return mValue;
         }
-
-        @Override
-        public String toString() {
-            // Used for filtering in the adapter
-            return mValue;
-        }
     }
 
     private final class AutofillWindowPresenter extends IAutofillWindowPresenter.Stub {
@@ -516,10 +520,16 @@
                     for (int i = 0; i < itemCount; i++) {
                         final ViewItem item = mAllItems.get(i);
                         final String value = item.getValue();
-                        // No value, i.e. null, matches any filter
-                        if ((value == null && item.mDataset.getAuthentication() == null)
-                                || (value != null
-                                        && value.toLowerCase().startsWith(constraintLowerCase))) {
+                        final Pattern filter = item.getFilter();
+                        final boolean matches;
+                        if (filter != null) {
+                            matches = filter.matcher(constraintLowerCase).matches();
+                        } else {
+                            matches = (value == null)
+                                    ? (item.mDataset.getAuthentication() == null)
+                                    : value.toLowerCase().startsWith(constraintLowerCase);
+                        }
+                        if (matches) {
                             filteredItems.add(item);
                         }
                     }
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 14a008e..fe4902f 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -653,7 +653,7 @@
 
     // Persistently track the need to do a full init
     private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
-    private ArraySet<String> mPendingInits = new ArraySet<>();  // transport names
+    private final ArraySet<String> mPendingInits = new ArraySet<>();  // transport names
 
     // Round-robin queue for scheduling full backup passes
     private static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file
diff --git a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
index a6897d0..1df0bf0 100644
--- a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
@@ -23,6 +23,7 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.server.backup.RefactoredBackupManagerService;
@@ -38,19 +39,22 @@
     public void onReceive(Context context, Intent intent) {
         if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
             synchronized (backupManagerService.getQueueLock()) {
+                final ArraySet<String> pendingInits = backupManagerService.getPendingInits();
                 if (DEBUG) {
-                    Slog.v(TAG, "Running a device init");
+                    Slog.v(TAG, "Running a device init; " + pendingInits.size() + " pending");
                 }
 
-                String[] pendingInits = (String[]) backupManagerService.getPendingInits().toArray();
-                backupManagerService.clearPendingInits();
-                PerformInitializeTask initTask = new PerformInitializeTask(backupManagerService,
-                        pendingInits, null);
+                if (pendingInits.size() > 0) {
+                    final String[] transports = pendingInits.toArray(new String[pendingInits.size()]);
+                    PerformInitializeTask initTask = new PerformInitializeTask(backupManagerService,
+                            transports, null);
 
-                // Acquire the wakelock and pass it to the init thread.  it will
-                // be released once init concludes.
-                backupManagerService.getWakelock().acquire();
-                backupManagerService.getBackupHandler().post(initTask);
+                    // Acquire the wakelock and pass it to the init thread.  it will
+                    // be released once init concludes.
+                    backupManagerService.clearPendingInits();
+                    backupManagerService.getWakelock().acquire();
+                    backupManagerService.getBackupHandler().post(initTask);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index abbc89e..3a6478e 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -307,6 +307,12 @@
      */
     private int[] mTempWhitelistAppIdArray = new int[0];
 
+    /**
+     * Apps in the system whitelist that have been taken out (probably because the user wanted to).
+     * They can be restored back by calling restoreAppToSystemWhitelist(String).
+     */
+    private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>();
+
     private static final int EVENT_NULL = 0;
     private static final int EVENT_NORMAL = 1;
     private static final int EVENT_LIGHT_IDLE = 2;
@@ -1162,6 +1168,38 @@
             }
         }
 
+        @Override public void removeSystemPowerWhitelistApp(String name) {
+            if (DEBUG) {
+                Slog.d(TAG, "removeAppFromSystemWhitelist(name = " + name + ")");
+            }
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                removeSystemPowerWhitelistAppInternal(name);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override public void restoreSystemPowerWhitelistApp(String name) {
+            if (DEBUG) {
+                Slog.d(TAG, "restoreAppToSystemWhitelist(name = " + name + ")");
+            }
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                restoreSystemPowerWhitelistAppInternal(name);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        public String[] getRemovedSystemPowerWhitelistApps() {
+            return getRemovedSystemPowerWhitelistAppsInternal();
+        }
+
         @Override public String[] getSystemPowerWhitelistExceptIdle() {
             return getSystemPowerWhitelistExceptIdleInternal();
         }
@@ -1504,6 +1542,42 @@
         }
     }
 
+    void resetSystemPowerWhitelistInternal() {
+        synchronized (this) {
+            mPowerSaveWhitelistApps.putAll(mRemovedFromSystemWhitelistApps);
+            mRemovedFromSystemWhitelistApps.clear();
+            reportPowerSaveWhitelistChangedLocked();
+            updateWhitelistAppIdsLocked();
+            writeConfigFileLocked();
+        }
+    }
+
+    public boolean restoreSystemPowerWhitelistAppInternal(String name) {
+        synchronized (this) {
+            if (!mRemovedFromSystemWhitelistApps.containsKey(name)) {
+                return false;
+            }
+            mPowerSaveWhitelistApps.put(name, mRemovedFromSystemWhitelistApps.remove(name));
+            reportPowerSaveWhitelistChangedLocked();
+            updateWhitelistAppIdsLocked();
+            writeConfigFileLocked();
+            return true;
+        }
+    }
+
+    public boolean removeSystemPowerWhitelistAppInternal(String name) {
+        synchronized (this) {
+            if (!mPowerSaveWhitelistApps.containsKey(name)) {
+                return false;
+            }
+            mRemovedFromSystemWhitelistApps.put(name, mPowerSaveWhitelistApps.remove(name));
+            reportPowerSaveWhitelistChangedLocked();
+            updateWhitelistAppIdsLocked();
+            writeConfigFileLocked();
+            return true;
+        }
+    }
+
     public boolean addPowerSaveWhitelistExceptIdleInternal(String name) {
         synchronized (this) {
             try {
@@ -1565,6 +1639,17 @@
         }
     }
 
+    public String[] getRemovedSystemPowerWhitelistAppsInternal() {
+        synchronized (this) {
+            int size = mRemovedFromSystemWhitelistApps.size();
+            final String[] apps = new String[size];
+            for (int i = 0; i < size; i++) {
+                apps[i] = mRemovedFromSystemWhitelistApps.keyAt(i);
+            }
+            return apps;
+        }
+    }
+
     public String[] getUserPowerWhitelistInternal() {
         synchronized (this) {
             int size = mPowerSaveWhitelistUserApps.size();
@@ -2481,21 +2566,31 @@
                 }
 
                 String tagName = parser.getName();
-                if (tagName.equals("wl")) {
-                    String name = parser.getAttributeValue(null, "n");
-                    if (name != null) {
-                        try {
-                            ApplicationInfo ai = pm.getApplicationInfo(name,
-                                    PackageManager.MATCH_ANY_USER);
-                            mPowerSaveWhitelistUserApps.put(ai.packageName,
-                                    UserHandle.getAppId(ai.uid));
-                        } catch (PackageManager.NameNotFoundException e) {
+                switch (tagName) {
+                    case "wl":
+                        String name = parser.getAttributeValue(null, "n");
+                        if (name != null) {
+                            try {
+                                ApplicationInfo ai = pm.getApplicationInfo(name,
+                                        PackageManager.MATCH_ANY_USER);
+                                mPowerSaveWhitelistUserApps.put(ai.packageName,
+                                        UserHandle.getAppId(ai.uid));
+                            } catch (PackageManager.NameNotFoundException e) {
+                            }
                         }
-                    }
-                } else {
-                    Slog.w(TAG, "Unknown element under <config>: "
-                            + parser.getName());
-                    XmlUtils.skipCurrentTag(parser);
+                        break;
+                    case "un-wl":
+                        final String packageName = parser.getAttributeValue(null, "n");
+                        if (mPowerSaveWhitelistApps.containsKey(packageName)) {
+                            mRemovedFromSystemWhitelistApps.put(packageName,
+                                    mPowerSaveWhitelistApps.remove(packageName));
+                        }
+                        break;
+                    default:
+                        Slog.w(TAG, "Unknown element under <config>: "
+                                + parser.getName());
+                        XmlUtils.skipCurrentTag(parser);
+                        break;
                 }
             }
 
@@ -2556,6 +2651,11 @@
             out.attribute(null, "n", name);
             out.endTag(null, "wl");
         }
+        for (int i = 0; i < mRemovedFromSystemWhitelistApps.size(); i++) {
+            out.startTag(null, "un-wl");
+            out.attribute(null, "n", mRemovedFromSystemWhitelistApps.keyAt(i));
+            out.endTag(null, "un-wl");
+        }
         out.endTag(null, "config");
         out.endDocument();
     }
@@ -2584,6 +2684,13 @@
         pw.println("    Print currently whitelisted apps.");
         pw.println("  whitelist [package ...]");
         pw.println("    Add (prefix with +) or remove (prefix with -) packages.");
+        pw.println("  sys-whitelist [package ...|reset]");
+        pw.println("    Prefix the package with '-' to remove it from the system whitelist or '+'"
+                + " to put it back in the system whitelist.");
+        pw.println("    Note that only packages that were"
+                + " earlier removed from the system whitelist can be added back.");
+        pw.println("    reset will reset the whitelist to the original state");
+        pw.println("    Prints the system whitelist if no arguments are specified");
         pw.println("  except-idle-whitelist [package ...|reset]");
         pw.println("    Prefix the package with '+' to add it to whitelist or "
                 + "'=' to check if it is already whitelisted");
@@ -2944,6 +3051,50 @@
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
+        } else if ("sys-whitelist".equals(cmd)) {
+            String arg = shell.getNextArg();
+            if (arg != null) {
+                getContext().enforceCallingOrSelfPermission(
+                        android.Manifest.permission.DEVICE_POWER, null);
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    if ("reset".equals(arg)) {
+                        resetSystemPowerWhitelistInternal();
+                    } else {
+                        do {
+                            if (arg.length() < 1
+                                    || (arg.charAt(0) != '-' && arg.charAt(0) != '+')) {
+                                pw.println("Package must be prefixed with + or - " + arg);
+                                return -1;
+                            }
+                            final char op = arg.charAt(0);
+                            final String pkg = arg.substring(1);
+                            switch (op) {
+                                case '+':
+                                    if (restoreSystemPowerWhitelistAppInternal(pkg)) {
+                                        pw.println("Restored " + pkg);
+                                    }
+                                    break;
+                                case '-':
+                                    if (removeSystemPowerWhitelistAppInternal(pkg)) {
+                                        pw.println("Removed " + pkg);
+                                    }
+                                    break;
+                            }
+                        } while ((arg = shell.getNextArg()) != null);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            } else {
+                synchronized (this) {
+                    for (int j=0; j<mPowerSaveWhitelistApps.size(); j++) {
+                        pw.print(mPowerSaveWhitelistApps.keyAt(j));
+                        pw.print(",");
+                        pw.println(mPowerSaveWhitelistApps.valueAt(j));
+                    }
+                }
+            }
         } else {
             return shell.handleDefaultCommands(cmd);
         }
@@ -3027,6 +3178,14 @@
                     pw.println(mPowerSaveWhitelistApps.keyAt(i));
                 }
             }
+            size = mRemovedFromSystemWhitelistApps.size();
+            if (size > 0) {
+                pw.println("  Removed from whitelist system apps:");
+                for (int i = 0; i < size; i++) {
+                    pw.print("    ");
+                    pw.println(mRemovedFromSystemWhitelistApps.keyAt(i));
+                }
+            }
             size = mPowerSaveWhitelistUserApps.size();
             if (size > 0) {
                 pw.println("  Whitelist user apps:");
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 555874c..2e1f142 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -344,7 +344,7 @@
     private class ManagedResourceArray<T extends ManagedResource> {
         SparseArray<T> mArray = new SparseArray<>();
 
-        T get(int key) {
+        T getAndCheckOwner(int key) {
             T val = mArray.get(key);
             // The value should never be null unless the resource doesn't exist
             // (since we do not allow null resources to be added).
@@ -723,7 +723,7 @@
             throws RemoteException {
         // We want to non-destructively get so that we can check credentials before removing
         // this from the records.
-        T record = resArray.get(resourceId);
+        T record = resArray.getAndCheckOwner(resourceId);
 
         if (record == null) {
             throw new IllegalArgumentException(
@@ -863,7 +863,8 @@
                 break;
             case IpSecTransform.ENCAP_ESPINUDP:
             case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
-                if (mUdpSocketRecords.get(config.getEncapSocketResourceId()) == null) {
+                if (mUdpSocketRecords.getAndCheckOwner(
+                            config.getEncapSocketResourceId()) == null) {
                     throw new IllegalStateException(
                             "No Encapsulation socket for Resource Id: "
                                     + config.getEncapSocketResourceId());
@@ -885,7 +886,7 @@
                 throw new IllegalArgumentException("Encryption and Authentication are both null");
             }
 
-            if (mSpiRecords.get(config.getSpiResourceId(direction)) == null) {
+            if (mSpiRecords.getAndCheckOwner(config.getSpiResourceId(direction)) == null) {
                 throw new IllegalStateException("No SPI for specified Resource Id");
             }
         }
@@ -913,7 +914,7 @@
         UdpSocketRecord socketRecord = null;
         encapType = c.getEncapType();
         if (encapType != IpSecTransform.ENCAP_NONE) {
-            socketRecord = mUdpSocketRecords.get(c.getEncapSocketResourceId());
+            socketRecord = mUdpSocketRecords.getAndCheckOwner(c.getEncapSocketResourceId());
             encapLocalPort = socketRecord.getPort();
             encapRemotePort = c.getEncapRemotePort();
         }
@@ -922,7 +923,7 @@
             IpSecAlgorithm auth = c.getAuthentication(direction);
             IpSecAlgorithm crypt = c.getEncryption(direction);
 
-            spis[direction] = mSpiRecords.get(c.getSpiResourceId(direction));
+            spis[direction] = mSpiRecords.getAndCheckOwner(c.getSpiResourceId(direction));
             int spi = spis[direction].getSpi();
             try {
                 mSrvConfig
@@ -976,7 +977,7 @@
         // Synchronize liberally here because we are using ManagedResources in this block
         TransformRecord info;
         // FIXME: this code should be factored out into a security check + getter
-        info = mTransformRecords.get(resourceId);
+        info = mTransformRecords.getAndCheckOwner(resourceId);
 
         if (info == null) {
             throw new IllegalArgumentException("Transform " + resourceId + " is not active");
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 340d672..0fd59ea 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManager;
 import android.annotation.NonNull;
-import android.content.pm.PackageManagerInternal;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import com.android.internal.content.PackageMonitor;
@@ -56,6 +55,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.Signature;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cf564e4..36cdb66 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -263,8 +263,8 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PathPermission;
 import android.content.pm.PermissionInfo;
@@ -14579,6 +14579,9 @@
                 }
                 sb.append("\n");
             }
+            if (process.info.isInstantApp()) {
+                sb.append("Instant-App: true\n");
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 7eb6eda..34eaab2 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -471,7 +471,8 @@
     }
 
     T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
-        return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds);
+        return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
+                mStackSupervisor.mWindowManager);
     }
 
     T getWindowContainerController() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d2cc557..42f3550 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1235,7 +1235,7 @@
         synchronized (mService) {
             return mService.getPackageManagerInternalLocked().resolveIntent(intent, resolvedType,
                     PackageManager.MATCH_INSTANT | PackageManager.MATCH_DEFAULT_ONLY | flags
-                    | ActivityManagerService.STOCK_PM_FLAGS, userId);
+                    | ActivityManagerService.STOCK_PM_FLAGS, userId, true);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index ccb9181..ed33d58 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1799,15 +1799,8 @@
                     mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
                     mVoiceInteractor, !mLaunchTaskBehind /* toTop */);
             addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
-            if (mLaunchBounds != null) {
-                final int stackId = mTargetStack.mStackId;
-                if (StackId.resizeStackWithLaunchBounds(stackId)) {
-                    mService.resizeStack(
-                            stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
-                } else {
-                    mStartActivity.getTask().updateOverrideConfiguration(mLaunchBounds);
-                }
-            }
+            updateBounds(mStartActivity.getTask(), mLaunchBounds);
+
             if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
                     + " in new task " + mStartActivity.getTask());
         } else {
@@ -1971,19 +1964,15 @@
         }
 
         if (mLaunchBounds != null) {
-            mInTask.updateOverrideConfiguration(mLaunchBounds);
             // TODO: Shouldn't we already know what stack to use by the time we get here?
             ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
             if (stack != mInTask.getStack()) {
                 mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
                         DEFER_RESUME, "inTaskToFront");
-                stack = mInTask.getStack();
                 mTargetStack = mInTask.getStack();
             }
-            if (StackId.resizeStackWithLaunchBounds(stack.mStackId)) {
-                mService.resizeStack(
-                        stack.mStackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
-            }
+
+            updateBounds(mInTask, mLaunchBounds);
         }
 
         mTargetStack.moveTaskToFrontLocked(
@@ -1996,6 +1985,19 @@
         return START_SUCCESS;
     }
 
+    void updateBounds(TaskRecord task, Rect bounds) {
+        if (bounds == null) {
+            return;
+        }
+
+        final int stackId = task.getStackId();
+        if (StackId.resizeStackWithLaunchBounds(stackId)) {
+            mService.resizeStack(stackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
+        } else {
+            task.updateOverrideConfiguration(bounds);
+        }
+    }
+
     private void setTaskToCurrentTopOrCreateNewTask() {
         mTargetStack = computeStackFocus(mStartActivity, false, null /* bounds */, mLaunchFlags,
                 mOptions);
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index 468b867..2726979 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -43,7 +43,8 @@
     @Override
     PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
             Rect outBounds) {
-        return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds);
+        return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
+                mStackSupervisor.mWindowManager);
     }
 
     Rect getDefaultPictureInPictureBounds(float aspectRatio) {
diff --git a/services/core/java/com/android/server/audio/AudioEventLogger.java b/services/core/java/com/android/server/audio/AudioEventLogger.java
index a2c0f76..9ebd75b 100644
--- a/services/core/java/com/android/server/audio/AudioEventLogger.java
+++ b/services/core/java/com/android/server/audio/AudioEventLogger.java
@@ -49,9 +49,9 @@
         }
 
         /**
-         * Causes the string message for the event to appear in the verbose logcat.
+         * Causes the string message for the event to appear in the logcat.
          * Here is an example of how to create a new event (a StringEvent), adding it to the logger
-         * (an instance of AudioEventLogger) while also making it show in the verbose logcat:
+         * (an instance of AudioEventLogger) while also making it show in the logcat:
          * <pre>
          *     myLogger.log(
          *         (new StringEvent("something for logcat and logger")).printLog(MyClass.TAG) );
@@ -60,7 +60,7 @@
          * @return the same instance of the event
          */
         public Event printLog(String tag) {
-            Log.v(tag, eventToString());
+            Log.i(tag, eventToString());
             return this;
         }
 
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 7d742ff..c5f563c7 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -89,6 +89,9 @@
         pw.println("\nMediaFocusControl dump time: "
                 + DateFormat.getTimeInstance().format(new Date()));
         dumpFocusStack(pw);
+        pw.println("\n");
+        // log
+        mEventLogger.dump(pw);
     }
 
     //=================================================================
@@ -120,6 +123,14 @@
     private final static Object mAudioFocusLock = new Object();
 
     /**
+     * Arbitrary maximum size of audio focus stack to prevent apps OOM'ing this process.
+     */
+    private static final int MAX_STACK_SIZE = 100;
+
+    private static final AudioEventLogger mEventLogger = new AudioEventLogger(50,
+            "focus commands as seen by MediaFocusControl");
+
+    /**
      * Discard the current audio focus owner.
      * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
      * focus), remove it from the stack, and clear the remote control display.
@@ -643,11 +654,14 @@
     protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
             IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
             int sdk) {
-        Log.i(TAG, " AudioFocus  requestAudioFocus() from uid/pid " + Binder.getCallingUid()
-                + "/" + Binder.getCallingPid()
-                + " clientId=" + clientId
-                + " req=" + focusChangeHint
-                + " flags=0x" + Integer.toHexString(flags));
+        mEventLogger.log((new AudioEventLogger.StringEvent(
+                "requestAudioFocus() from uid/pid " + Binder.getCallingUid()
+                    + "/" + Binder.getCallingPid()
+                    + " clientId=" + clientId + " callingPack=" + callingPackageName
+                    + " req=" + focusChangeHint
+                    + " flags=0x" + Integer.toHexString(flags)
+                    + " sdk=" + sdk))
+                .printLog(TAG));
         // we need a valid binder callback for clients
         if (!cb.pingBinder()) {
             Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
@@ -660,6 +674,11 @@
         }
 
         synchronized(mAudioFocusLock) {
+            if (mFocusStack.size() > MAX_STACK_SIZE) {
+                Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()");
+                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+            }
+
             boolean enteringRingOrCall = !mRingOrCallActive
                     & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
             if (enteringRingOrCall) { mRingOrCallActive = true; }
@@ -770,10 +789,12 @@
      * */
     protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa,
             String callingPackageName) {
-        // AudioAttributes are currently ignored, to be used for zones
-        Log.i(TAG, " AudioFocus  abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
-                + "/" + Binder.getCallingPid()
-                + " clientId=" + clientId);
+        // AudioAttributes are currently ignored, to be used for zones / a11y
+        mEventLogger.log((new AudioEventLogger.StringEvent(
+                "abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
+                    + "/" + Binder.getCallingPid()
+                    + " clientId=" + clientId))
+                .printLog(TAG));
         try {
             // this will take care of notifying the new focus owner if needed
             synchronized(mAudioFocusLock) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index 057704a..cff216c 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -596,9 +596,10 @@
         }
 
         mNatUpdateCallbacksReceived++;
+        final String natDescription = String.format("%s (%s, %s) -> (%s, %s)",
+                protoName, srcAddr, srcPort, dstAddr, dstPort);
         if (DBG) {
-            mLog.log(String.format("NAT timeout update: %s (%s, %s) -> (%s, %s)",
-                     protoName, srcAddr, srcPort, dstAddr, dstPort));
+            mLog.log("NAT timeout update: " + natDescription);
         }
 
         final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto);
@@ -609,7 +610,7 @@
             NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg);
         } catch (ErrnoException e) {
             mNatUpdateNetlinkErrors++;
-            mLog.e("Error updating NAT conntrack entry: " + e
+            mLog.e("Error updating NAT conntrack entry >" + natDescription + "<: " + e
                     + ", msg: " + NetlinkConstants.hexify(msg));
             mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived);
             mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 205e828..9cd52d7 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -46,8 +46,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ProviderInfo;
 import android.content.pm.RegisteredServicesCache;
 import android.content.pm.RegisteredServicesCacheListener;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index ef6de4c..fddb81b 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -98,6 +98,12 @@
     public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 9;
 
     /**
+     * Flag: This display will destroy its content on removal.
+     * @hide
+     */
+    public static final int FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 10;
+
+    /**
      * Touch attachment: Display does not receive touch.
      */
     public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 8756484..d61a418c 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -473,17 +473,18 @@
                         }
 
                         // If the state change was from or to VR, then we need to tell the light
-                        // so that it can apply appropriate VR brightness settings. This should
-                        // happen prior to changing the brightness but also if there is no
-                        // brightness change at all.
+                        // so that it can apply appropriate VR brightness settings. Also, update the
+                        // brightness so the state is propogated to light.
+                        boolean vrModeChange = false;
                         if ((state == Display.STATE_VR || currentState == Display.STATE_VR) &&
                                 currentState != state) {
                             setVrMode(state == Display.STATE_VR);
+                            vrModeChange = true;
                         }
 
 
                         // Apply brightness changes given that we are in a non-suspended state.
-                        if (brightnessChanged) {
+                        if (brightnessChanged || vrModeChange) {
                             setDisplayBrightness(brightness);
                         }
 
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index addad0b..78a5407 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -238,6 +238,9 @@
                 // For private displays by default content is destroyed on removal.
                 mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT;
             }
+            if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
+                mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT;
+            }
             if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_PRESENTATION) != 0) {
                 mBaseDisplayInfo.flags |= Display.FLAG_PRESENTATION;
             }
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index d6ab888..f86d576 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -24,6 +24,8 @@
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
+import static android.hardware.display.DisplayManager
+        .VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
 
 import android.content.Context;
 import android.hardware.display.IVirtualDisplayCallback;
@@ -363,6 +365,9 @@
                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT) != 0) {
                     mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
                 }
+                if ((mFlags & VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
+                  mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL;
+                }
 
                 mInfo.type = Display.TYPE_VIRTUAL;
                 mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 8783f47..d7e9cf3 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -231,7 +231,11 @@
                                 if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
                                     NotificationChannel channel = new NotificationChannel(id,
                                             channelName, channelImportance);
-                                    channel.populateFromXml(parser);
+                                    if (forRestore) {
+                                        channel.populateFromXmlForRestore(parser, mContext);
+                                    } else {
+                                        channel.populateFromXml(parser);
+                                    }
                                     r.channels.put(id, channel);
                                 }
                             }
@@ -394,7 +398,11 @@
                     }
 
                     for (NotificationChannel channel : r.channels.values()) {
-                        if (!forBackup || (forBackup && !channel.isDeleted())) {
+                        if (forBackup) {
+                            if (!channel.isDeleted()) {
+                                channel.writeXmlForBackup(out, mContext);
+                            }
+                        } else {
                             channel.writeXml(out);
                         }
                     }
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
deleted file mode 100644
index 59e243a..0000000
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ /dev/null
@@ -1,1246 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.app.ActivityManager;
-import android.app.DownloadManager;
-import android.app.admin.DevicePolicyManager;
-import android.companion.CompanionDeviceManager;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal.PackagesProvider;
-import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider;
-import android.content.pm.PackageParser;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ResolveInfo;
-import android.media.RingtoneManager;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.Message;
-import android.os.UserHandle;
-import android.os.storage.StorageManager;
-import android.print.PrintManager;
-import android.provider.CalendarContract;
-import android.provider.ContactsContract;
-import android.provider.MediaStore;
-import android.provider.Telephony.Sms.Intents;
-import android.telephony.TelephonyManager;
-import android.security.Credentials;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Slog;
-import android.util.Xml;
-import com.android.internal.util.XmlUtils;
-import com.android.server.pm.permission.BasePermission;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static android.os.Process.FIRST_APPLICATION_UID;
-
-/**
- * This class is the policy for granting runtime permissions to
- * platform components and default handlers in the system such
- * that the device is usable out-of-the-box. For example, the
- * shell UID is a part of the system and the Phone app should
- * have phone related permission by default.
- */
-final class DefaultPermissionGrantPolicy {
-    private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
-    private static final boolean DEBUG = false;
-
-    private static final int DEFAULT_FLAGS =
-            PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                    | PackageManager.MATCH_UNINSTALLED_PACKAGES;
-
-    private static final String AUDIO_MIME_TYPE = "audio/mpeg";
-
-    private static final String TAG_EXCEPTIONS = "exceptions";
-    private static final String TAG_EXCEPTION = "exception";
-    private static final String TAG_PERMISSION = "permission";
-    private static final String ATTR_PACKAGE = "package";
-    private static final String ATTR_NAME = "name";
-    private static final String ATTR_FIXED = "fixed";
-
-    private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>();
-    static {
-        PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE);
-        PHONE_PERMISSIONS.add(Manifest.permission.CALL_PHONE);
-        PHONE_PERMISSIONS.add(Manifest.permission.READ_CALL_LOG);
-        PHONE_PERMISSIONS.add(Manifest.permission.WRITE_CALL_LOG);
-        PHONE_PERMISSIONS.add(Manifest.permission.ADD_VOICEMAIL);
-        PHONE_PERMISSIONS.add(Manifest.permission.USE_SIP);
-        PHONE_PERMISSIONS.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
-    }
-
-    private static final Set<String> CONTACTS_PERMISSIONS = new ArraySet<>();
-    static {
-        CONTACTS_PERMISSIONS.add(Manifest.permission.READ_CONTACTS);
-        CONTACTS_PERMISSIONS.add(Manifest.permission.WRITE_CONTACTS);
-        CONTACTS_PERMISSIONS.add(Manifest.permission.GET_ACCOUNTS);
-    }
-
-    private static final Set<String> LOCATION_PERMISSIONS = new ArraySet<>();
-    static {
-        LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
-        LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
-    }
-
-    private static final Set<String> CALENDAR_PERMISSIONS = new ArraySet<>();
-    static {
-        CALENDAR_PERMISSIONS.add(Manifest.permission.READ_CALENDAR);
-        CALENDAR_PERMISSIONS.add(Manifest.permission.WRITE_CALENDAR);
-    }
-
-    private static final Set<String> SMS_PERMISSIONS = new ArraySet<>();
-    static {
-        SMS_PERMISSIONS.add(Manifest.permission.SEND_SMS);
-        SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_SMS);
-        SMS_PERMISSIONS.add(Manifest.permission.READ_SMS);
-        SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_WAP_PUSH);
-        SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_MMS);
-        SMS_PERMISSIONS.add(Manifest.permission.READ_CELL_BROADCASTS);
-    }
-
-    private static final Set<String> MICROPHONE_PERMISSIONS = new ArraySet<>();
-    static {
-        MICROPHONE_PERMISSIONS.add(Manifest.permission.RECORD_AUDIO);
-    }
-
-    private static final Set<String> CAMERA_PERMISSIONS = new ArraySet<>();
-    static {
-        CAMERA_PERMISSIONS.add(Manifest.permission.CAMERA);
-    }
-
-    private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>();
-    static {
-        SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
-    }
-
-    private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
-    static {
-        STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
-        STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
-    }
-
-    private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
-
-    private static final String ACTION_TRACK = "com.android.fitness.TRACK";
-
-    private final PackageManagerService mService;
-    private final Handler mHandler;
-
-    private PackagesProvider mLocationPackagesProvider;
-    private PackagesProvider mVoiceInteractionPackagesProvider;
-    private PackagesProvider mSmsAppPackagesProvider;
-    private PackagesProvider mDialerAppPackagesProvider;
-    private PackagesProvider mSimCallManagerPackagesProvider;
-    private SyncAdapterPackagesProvider mSyncAdapterPackagesProvider;
-
-    private ArrayMap<String, List<DefaultPermissionGrant>> mGrantExceptions;
-
-    public DefaultPermissionGrantPolicy(PackageManagerService service) {
-        mService = service;
-        mHandler = new Handler(mService.mHandlerThread.getLooper()) {
-            @Override
-            public void handleMessage(Message msg) {
-                if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) {
-                    synchronized (mService.mPackages) {
-                        if (mGrantExceptions == null) {
-                            mGrantExceptions = readDefaultPermissionExceptionsLPw();
-                        }
-                    }
-                }
-            }
-        };
-    }
-
-    public void setLocationPackagesProviderLPw(PackagesProvider provider) {
-        mLocationPackagesProvider = provider;
-    }
-
-    public void setVoiceInteractionPackagesProviderLPw(PackagesProvider provider) {
-        mVoiceInteractionPackagesProvider = provider;
-    }
-
-    public void setSmsAppPackagesProviderLPw(PackagesProvider provider) {
-        mSmsAppPackagesProvider = provider;
-    }
-
-    public void setDialerAppPackagesProviderLPw(PackagesProvider provider) {
-        mDialerAppPackagesProvider = provider;
-    }
-
-    public void setSimCallManagerPackagesProviderLPw(PackagesProvider provider) {
-        mSimCallManagerPackagesProvider = provider;
-    }
-
-    public void setSyncAdapterPackagesProviderLPw(SyncAdapterPackagesProvider provider) {
-        mSyncAdapterPackagesProvider = provider;
-    }
-
-    public void grantDefaultPermissions(int userId) {
-        if (mService.hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
-            grantAllRuntimePermissions(userId);
-        } else {
-            grantPermissionsToSysComponentsAndPrivApps(userId);
-            grantDefaultSystemHandlerPermissions(userId);
-            grantDefaultPermissionExceptions(userId);
-        }
-    }
-
-    private void grantRuntimePermissionsForPackageLocked(int userId, PackageParser.Package pkg) {
-        Set<String> permissions = new ArraySet<>();
-        for (String permission :  pkg.requestedPermissions) {
-            BasePermission bp = mService.mSettings.mPermissions.get(permission);
-            if (bp != null && bp.isRuntime()) {
-                permissions.add(permission);
-            }
-        }
-        if (!permissions.isEmpty()) {
-            grantRuntimePermissionsLPw(pkg, permissions, true, userId);
-        }
-    }
-
-    private void grantAllRuntimePermissions(int userId) {
-        Log.i(TAG, "Granting all runtime permissions for user " + userId);
-        synchronized (mService.mPackages) {
-            for (PackageParser.Package pkg : mService.mPackages.values()) {
-                grantRuntimePermissionsForPackageLocked(userId, pkg);
-            }
-        }
-    }
-
-    public void scheduleReadDefaultPermissionExceptions() {
-        mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
-    }
-
-    private void grantPermissionsToSysComponentsAndPrivApps(int userId) {
-        Log.i(TAG, "Granting permissions to platform components for user " + userId);
-
-        synchronized (mService.mPackages) {
-            for (PackageParser.Package pkg : mService.mPackages.values()) {
-                if (!isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg)
-                        || !doesPackageSupportRuntimePermissions(pkg)
-                        || pkg.requestedPermissions.isEmpty()) {
-                    continue;
-                }
-                grantRuntimePermissionsForPackageLocked(userId, pkg);
-            }
-        }
-    }
-
-    private void grantDefaultSystemHandlerPermissions(int userId) {
-        Log.i(TAG, "Granting permissions to default platform handlers for user " + userId);
-
-        final PackagesProvider locationPackagesProvider;
-        final PackagesProvider voiceInteractionPackagesProvider;
-        final PackagesProvider smsAppPackagesProvider;
-        final PackagesProvider dialerAppPackagesProvider;
-        final PackagesProvider simCallManagerPackagesProvider;
-        final SyncAdapterPackagesProvider syncAdapterPackagesProvider;
-
-        synchronized (mService.mPackages) {
-            locationPackagesProvider = mLocationPackagesProvider;
-            voiceInteractionPackagesProvider = mVoiceInteractionPackagesProvider;
-            smsAppPackagesProvider = mSmsAppPackagesProvider;
-            dialerAppPackagesProvider = mDialerAppPackagesProvider;
-            simCallManagerPackagesProvider = mSimCallManagerPackagesProvider;
-            syncAdapterPackagesProvider = mSyncAdapterPackagesProvider;
-        }
-
-        String[] voiceInteractPackageNames = (voiceInteractionPackagesProvider != null)
-                ? voiceInteractionPackagesProvider.getPackages(userId) : null;
-        String[] locationPackageNames = (locationPackagesProvider != null)
-                ? locationPackagesProvider.getPackages(userId) : null;
-        String[] smsAppPackageNames = (smsAppPackagesProvider != null)
-                ? smsAppPackagesProvider.getPackages(userId) : null;
-        String[] dialerAppPackageNames = (dialerAppPackagesProvider != null)
-                ? dialerAppPackagesProvider.getPackages(userId) : null;
-        String[] simCallManagerPackageNames = (simCallManagerPackagesProvider != null)
-                ? simCallManagerPackagesProvider.getPackages(userId) : null;
-        String[] contactsSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
-                syncAdapterPackagesProvider.getPackages(ContactsContract.AUTHORITY, userId) : null;
-        String[] calendarSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
-                syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null;
-
-        synchronized (mService.mPackages) {
-            // Installer
-            PackageParser.Package installerPackage = getSystemPackageLPr(
-                    mService.mRequiredInstallerPackage);
-            if (installerPackage != null
-                    && doesPackageSupportRuntimePermissions(installerPackage)) {
-                grantRuntimePermissionsLPw(installerPackage, STORAGE_PERMISSIONS, true, userId);
-            }
-
-            // Verifier
-            PackageParser.Package verifierPackage = getSystemPackageLPr(
-                    mService.mRequiredVerifierPackage);
-            if (verifierPackage != null
-                    && doesPackageSupportRuntimePermissions(verifierPackage)) {
-                grantRuntimePermissionsLPw(verifierPackage, STORAGE_PERMISSIONS, true, userId);
-                grantRuntimePermissionsLPw(verifierPackage, PHONE_PERMISSIONS, false, userId);
-                grantRuntimePermissionsLPw(verifierPackage, SMS_PERMISSIONS, false, userId);
-            }
-
-            // SetupWizard
-            PackageParser.Package setupPackage = getSystemPackageLPr(
-                    mService.mSetupWizardPackage);
-            if (setupPackage != null
-                    && doesPackageSupportRuntimePermissions(setupPackage)) {
-                grantRuntimePermissionsLPw(setupPackage, PHONE_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(setupPackage, CONTACTS_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(setupPackage, LOCATION_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(setupPackage, CAMERA_PERMISSIONS, userId);
-            }
-
-            // Camera
-            Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
-            PackageParser.Package cameraPackage = getDefaultSystemHandlerActivityPackageLPr(
-                    cameraIntent, userId);
-            if (cameraPackage != null
-                    && doesPackageSupportRuntimePermissions(cameraPackage)) {
-                grantRuntimePermissionsLPw(cameraPackage, CAMERA_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(cameraPackage, MICROPHONE_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(cameraPackage, STORAGE_PERMISSIONS, userId);
-            }
-
-            // Media provider
-            PackageParser.Package mediaStorePackage = getDefaultProviderAuthorityPackageLPr(
-                    MediaStore.AUTHORITY, userId);
-            if (mediaStorePackage != null) {
-                grantRuntimePermissionsLPw(mediaStorePackage, STORAGE_PERMISSIONS, true, userId);
-            }
-
-            // Downloads provider
-            PackageParser.Package downloadsPackage = getDefaultProviderAuthorityPackageLPr(
-                    "downloads", userId);
-            if (downloadsPackage != null) {
-                grantRuntimePermissionsLPw(downloadsPackage, STORAGE_PERMISSIONS, true, userId);
-            }
-
-            // Downloads UI
-            Intent downloadsUiIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
-            PackageParser.Package downloadsUiPackage = getDefaultSystemHandlerActivityPackageLPr(
-                    downloadsUiIntent, userId);
-            if (downloadsUiPackage != null
-                    && doesPackageSupportRuntimePermissions(downloadsUiPackage)) {
-                grantRuntimePermissionsLPw(downloadsUiPackage, STORAGE_PERMISSIONS, true, userId);
-            }
-
-            // Storage provider
-            PackageParser.Package storagePackage = getDefaultProviderAuthorityPackageLPr(
-                    "com.android.externalstorage.documents", userId);
-            if (storagePackage != null) {
-                grantRuntimePermissionsLPw(storagePackage, STORAGE_PERMISSIONS, true, userId);
-            }
-
-            // CertInstaller
-            Intent certInstallerIntent = new Intent(Credentials.INSTALL_ACTION);
-            PackageParser.Package certInstallerPackage = getDefaultSystemHandlerActivityPackageLPr(
-                    certInstallerIntent, userId);
-            if (certInstallerPackage != null
-                    && doesPackageSupportRuntimePermissions(certInstallerPackage)) {
-                grantRuntimePermissionsLPw(certInstallerPackage, STORAGE_PERMISSIONS, true, userId);
-            }
-
-            // Dialer
-            if (dialerAppPackageNames == null) {
-                Intent dialerIntent = new Intent(Intent.ACTION_DIAL);
-                PackageParser.Package dialerPackage = getDefaultSystemHandlerActivityPackageLPr(
-                        dialerIntent, userId);
-                if (dialerPackage != null) {
-                    grantDefaultPermissionsToDefaultSystemDialerAppLPr(dialerPackage, userId);
-                }
-            } else {
-                for (String dialerAppPackageName : dialerAppPackageNames) {
-                    PackageParser.Package dialerPackage = getSystemPackageLPr(dialerAppPackageName);
-                    if (dialerPackage != null) {
-                        grantDefaultPermissionsToDefaultSystemDialerAppLPr(dialerPackage, userId);
-                    }
-                }
-            }
-
-            // Sim call manager
-            if (simCallManagerPackageNames != null) {
-                for (String simCallManagerPackageName : simCallManagerPackageNames) {
-                    PackageParser.Package simCallManagerPackage =
-                            getSystemPackageLPr(simCallManagerPackageName);
-                    if (simCallManagerPackage != null) {
-                        grantDefaultPermissionsToDefaultSimCallManagerLPr(simCallManagerPackage,
-                                userId);
-                    }
-                }
-            }
-
-            // SMS
-            if (smsAppPackageNames == null) {
-                Intent smsIntent = new Intent(Intent.ACTION_MAIN);
-                smsIntent.addCategory(Intent.CATEGORY_APP_MESSAGING);
-                PackageParser.Package smsPackage = getDefaultSystemHandlerActivityPackageLPr(
-                        smsIntent, userId);
-                if (smsPackage != null) {
-                   grantDefaultPermissionsToDefaultSystemSmsAppLPr(smsPackage, userId);
-                }
-            } else {
-                for (String smsPackageName : smsAppPackageNames) {
-                    PackageParser.Package smsPackage = getSystemPackageLPr(smsPackageName);
-                    if (smsPackage != null) {
-                        grantDefaultPermissionsToDefaultSystemSmsAppLPr(smsPackage, userId);
-                    }
-                }
-            }
-
-            // Cell Broadcast Receiver
-            Intent cbrIntent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-            PackageParser.Package cbrPackage =
-                    getDefaultSystemHandlerActivityPackageLPr(cbrIntent, userId);
-            if (cbrPackage != null && doesPackageSupportRuntimePermissions(cbrPackage)) {
-                grantRuntimePermissionsLPw(cbrPackage, SMS_PERMISSIONS, userId);
-            }
-
-            // Carrier Provisioning Service
-            Intent carrierProvIntent = new Intent(Intents.SMS_CARRIER_PROVISION_ACTION);
-            PackageParser.Package carrierProvPackage =
-                    getDefaultSystemHandlerServicePackageLPr(carrierProvIntent, userId);
-            if (carrierProvPackage != null && doesPackageSupportRuntimePermissions(carrierProvPackage)) {
-                grantRuntimePermissionsLPw(carrierProvPackage, SMS_PERMISSIONS, false, userId);
-            }
-
-            // Calendar
-            Intent calendarIntent = new Intent(Intent.ACTION_MAIN);
-            calendarIntent.addCategory(Intent.CATEGORY_APP_CALENDAR);
-            PackageParser.Package calendarPackage = getDefaultSystemHandlerActivityPackageLPr(
-                    calendarIntent, userId);
-            if (calendarPackage != null
-                    && doesPackageSupportRuntimePermissions(calendarPackage)) {
-                grantRuntimePermissionsLPw(calendarPackage, CALENDAR_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(calendarPackage, CONTACTS_PERMISSIONS, userId);
-            }
-
-            // Calendar provider
-            PackageParser.Package calendarProviderPackage = getDefaultProviderAuthorityPackageLPr(
-                    CalendarContract.AUTHORITY, userId);
-            if (calendarProviderPackage != null) {
-                grantRuntimePermissionsLPw(calendarProviderPackage, CONTACTS_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(calendarProviderPackage, CALENDAR_PERMISSIONS,
-                        true, userId);
-                grantRuntimePermissionsLPw(calendarProviderPackage, STORAGE_PERMISSIONS, userId);
-            }
-
-            // Calendar provider sync adapters
-            List<PackageParser.Package> calendarSyncAdapters = getHeadlessSyncAdapterPackagesLPr(
-                    calendarSyncAdapterPackages, userId);
-            final int calendarSyncAdapterCount = calendarSyncAdapters.size();
-            for (int i = 0; i < calendarSyncAdapterCount; i++) {
-                PackageParser.Package calendarSyncAdapter = calendarSyncAdapters.get(i);
-                if (doesPackageSupportRuntimePermissions(calendarSyncAdapter)) {
-                    grantRuntimePermissionsLPw(calendarSyncAdapter, CALENDAR_PERMISSIONS, userId);
-                }
-            }
-
-            // Contacts
-            Intent contactsIntent = new Intent(Intent.ACTION_MAIN);
-            contactsIntent.addCategory(Intent.CATEGORY_APP_CONTACTS);
-            PackageParser.Package contactsPackage = getDefaultSystemHandlerActivityPackageLPr(
-                    contactsIntent, userId);
-            if (contactsPackage != null
-                    && doesPackageSupportRuntimePermissions(contactsPackage)) {
-                grantRuntimePermissionsLPw(contactsPackage, CONTACTS_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(contactsPackage, PHONE_PERMISSIONS, userId);
-            }
-
-            // Contacts provider sync adapters
-            List<PackageParser.Package> contactsSyncAdapters = getHeadlessSyncAdapterPackagesLPr(
-                    contactsSyncAdapterPackages, userId);
-            final int contactsSyncAdapterCount = contactsSyncAdapters.size();
-            for (int i = 0; i < contactsSyncAdapterCount; i++) {
-                PackageParser.Package contactsSyncAdapter = contactsSyncAdapters.get(i);
-                if (doesPackageSupportRuntimePermissions(contactsSyncAdapter)) {
-                    grantRuntimePermissionsLPw(contactsSyncAdapter, CONTACTS_PERMISSIONS, userId);
-                }
-            }
-
-            // Contacts provider
-            PackageParser.Package contactsProviderPackage = getDefaultProviderAuthorityPackageLPr(
-                    ContactsContract.AUTHORITY, userId);
-            if (contactsProviderPackage != null) {
-                grantRuntimePermissionsLPw(contactsProviderPackage, CONTACTS_PERMISSIONS,
-                        true, userId);
-                grantRuntimePermissionsLPw(contactsProviderPackage, PHONE_PERMISSIONS,
-                        true, userId);
-                grantRuntimePermissionsLPw(contactsProviderPackage, STORAGE_PERMISSIONS, userId);
-            }
-
-            // Device provisioning
-            Intent deviceProvisionIntent = new Intent(
-                    DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE);
-            PackageParser.Package deviceProvisionPackage =
-                    getDefaultSystemHandlerActivityPackageLPr(deviceProvisionIntent, userId);
-            if (deviceProvisionPackage != null
-                    && doesPackageSupportRuntimePermissions(deviceProvisionPackage)) {
-                grantRuntimePermissionsLPw(deviceProvisionPackage, CONTACTS_PERMISSIONS, userId);
-            }
-
-            // Maps
-            Intent mapsIntent = new Intent(Intent.ACTION_MAIN);
-            mapsIntent.addCategory(Intent.CATEGORY_APP_MAPS);
-            PackageParser.Package mapsPackage = getDefaultSystemHandlerActivityPackageLPr(
-                    mapsIntent, userId);
-            if (mapsPackage != null
-                    && doesPackageSupportRuntimePermissions(mapsPackage)) {
-                grantRuntimePermissionsLPw(mapsPackage, LOCATION_PERMISSIONS, userId);
-            }
-
-            // Gallery
-            Intent galleryIntent = new Intent(Intent.ACTION_MAIN);
-            galleryIntent.addCategory(Intent.CATEGORY_APP_GALLERY);
-            PackageParser.Package galleryPackage = getDefaultSystemHandlerActivityPackageLPr(
-                    galleryIntent, userId);
-            if (galleryPackage != null
-                    && doesPackageSupportRuntimePermissions(galleryPackage)) {
-                grantRuntimePermissionsLPw(galleryPackage, STORAGE_PERMISSIONS, userId);
-            }
-
-            // Email
-            Intent emailIntent = new Intent(Intent.ACTION_MAIN);
-            emailIntent.addCategory(Intent.CATEGORY_APP_EMAIL);
-            PackageParser.Package emailPackage = getDefaultSystemHandlerActivityPackageLPr(
-                    emailIntent, userId);
-            if (emailPackage != null
-                    && doesPackageSupportRuntimePermissions(emailPackage)) {
-                grantRuntimePermissionsLPw(emailPackage, CONTACTS_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(emailPackage, CALENDAR_PERMISSIONS, userId);
-            }
-
-            // Browser
-            PackageParser.Package browserPackage = null;
-            String defaultBrowserPackage = mService.getDefaultBrowserPackageName(userId);
-            if (defaultBrowserPackage != null) {
-                browserPackage = getPackageLPr(defaultBrowserPackage);
-            }
-            if (browserPackage == null) {
-                Intent browserIntent = new Intent(Intent.ACTION_MAIN);
-                browserIntent.addCategory(Intent.CATEGORY_APP_BROWSER);
-                browserPackage = getDefaultSystemHandlerActivityPackageLPr(
-                        browserIntent, userId);
-            }
-            if (browserPackage != null
-                    && doesPackageSupportRuntimePermissions(browserPackage)) {
-                grantRuntimePermissionsLPw(browserPackage, LOCATION_PERMISSIONS, userId);
-            }
-
-            // Voice interaction
-            if (voiceInteractPackageNames != null) {
-                for (String voiceInteractPackageName : voiceInteractPackageNames) {
-                    PackageParser.Package voiceInteractPackage = getSystemPackageLPr(
-                            voiceInteractPackageName);
-                    if (voiceInteractPackage != null
-                            && doesPackageSupportRuntimePermissions(voiceInteractPackage)) {
-                        grantRuntimePermissionsLPw(voiceInteractPackage,
-                                CONTACTS_PERMISSIONS, userId);
-                        grantRuntimePermissionsLPw(voiceInteractPackage,
-                                CALENDAR_PERMISSIONS, userId);
-                        grantRuntimePermissionsLPw(voiceInteractPackage,
-                                MICROPHONE_PERMISSIONS, userId);
-                        grantRuntimePermissionsLPw(voiceInteractPackage,
-                                PHONE_PERMISSIONS, userId);
-                        grantRuntimePermissionsLPw(voiceInteractPackage,
-                                SMS_PERMISSIONS, userId);
-                        grantRuntimePermissionsLPw(voiceInteractPackage,
-                                LOCATION_PERMISSIONS, userId);
-                    }
-                }
-            }
-
-            if (ActivityManager.isLowRamDeviceStatic()) {
-                // Allow voice search on low-ram devices
-                Intent globalSearchIntent = new Intent("android.search.action.GLOBAL_SEARCH");
-                PackageParser.Package globalSearchPickerPackage =
-                    getDefaultSystemHandlerActivityPackageLPr(globalSearchIntent, userId);
-
-                if (globalSearchPickerPackage != null
-                        && doesPackageSupportRuntimePermissions(globalSearchPickerPackage)) {
-                    grantRuntimePermissionsLPw(globalSearchPickerPackage,
-                        MICROPHONE_PERMISSIONS, true, userId);
-                    grantRuntimePermissionsLPw(globalSearchPickerPackage,
-                        LOCATION_PERMISSIONS, true, userId);
-                }
-            }
-
-            // Voice recognition
-            Intent voiceRecoIntent = new Intent("android.speech.RecognitionService");
-            voiceRecoIntent.addCategory(Intent.CATEGORY_DEFAULT);
-            PackageParser.Package voiceRecoPackage = getDefaultSystemHandlerServicePackageLPr(
-                    voiceRecoIntent, userId);
-            if (voiceRecoPackage != null
-                    && doesPackageSupportRuntimePermissions(voiceRecoPackage)) {
-                grantRuntimePermissionsLPw(voiceRecoPackage, MICROPHONE_PERMISSIONS, userId);
-            }
-
-            // Location
-            if (locationPackageNames != null) {
-                for (String packageName : locationPackageNames) {
-                    PackageParser.Package locationPackage = getSystemPackageLPr(packageName);
-                    if (locationPackage != null
-                            && doesPackageSupportRuntimePermissions(locationPackage)) {
-                        grantRuntimePermissionsLPw(locationPackage, CONTACTS_PERMISSIONS, userId);
-                        grantRuntimePermissionsLPw(locationPackage, CALENDAR_PERMISSIONS, userId);
-                        grantRuntimePermissionsLPw(locationPackage, MICROPHONE_PERMISSIONS, userId);
-                        grantRuntimePermissionsLPw(locationPackage, PHONE_PERMISSIONS, userId);
-                        grantRuntimePermissionsLPw(locationPackage, SMS_PERMISSIONS, userId);
-                        grantRuntimePermissionsLPw(locationPackage, LOCATION_PERMISSIONS,
-                                true, userId);
-                        grantRuntimePermissionsLPw(locationPackage, CAMERA_PERMISSIONS, userId);
-                        grantRuntimePermissionsLPw(locationPackage, SENSORS_PERMISSIONS, userId);
-                        grantRuntimePermissionsLPw(locationPackage, STORAGE_PERMISSIONS, userId);
-                    }
-                }
-            }
-
-            // Music
-            Intent musicIntent = new Intent(Intent.ACTION_VIEW);
-            musicIntent.addCategory(Intent.CATEGORY_DEFAULT);
-            musicIntent.setDataAndType(Uri.fromFile(new File("foo.mp3")),
-                    AUDIO_MIME_TYPE);
-            PackageParser.Package musicPackage = getDefaultSystemHandlerActivityPackageLPr(
-                    musicIntent, userId);
-            if (musicPackage != null
-                    && doesPackageSupportRuntimePermissions(musicPackage)) {
-                grantRuntimePermissionsLPw(musicPackage, STORAGE_PERMISSIONS, userId);
-            }
-
-            // Home
-            Intent homeIntent = new Intent(Intent.ACTION_MAIN);
-            homeIntent.addCategory(Intent.CATEGORY_HOME);
-            homeIntent.addCategory(Intent.CATEGORY_LAUNCHER_APP);
-            PackageParser.Package homePackage = getDefaultSystemHandlerActivityPackageLPr(
-                    homeIntent, userId);
-            if (homePackage != null
-                    && doesPackageSupportRuntimePermissions(homePackage)) {
-                grantRuntimePermissionsLPw(homePackage, LOCATION_PERMISSIONS, false, userId);
-            }
-
-            // Watches
-            if (mService.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
-                // Home application on watches
-                Intent wearHomeIntent = new Intent(Intent.ACTION_MAIN);
-                wearHomeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
-
-                PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackageLPr(
-                        wearHomeIntent, userId);
-
-                if (wearHomePackage != null
-                        && doesPackageSupportRuntimePermissions(wearHomePackage)) {
-                    grantRuntimePermissionsLPw(wearHomePackage, CONTACTS_PERMISSIONS, false,
-                            userId);
-                    grantRuntimePermissionsLPw(wearHomePackage, PHONE_PERMISSIONS, true, userId);
-                    grantRuntimePermissionsLPw(wearHomePackage, MICROPHONE_PERMISSIONS, false,
-                            userId);
-                    grantRuntimePermissionsLPw(wearHomePackage, LOCATION_PERMISSIONS, false,
-                            userId);
-                }
-
-                // Fitness tracking on watches
-                Intent trackIntent = new Intent(ACTION_TRACK);
-                PackageParser.Package trackPackage = getDefaultSystemHandlerActivityPackageLPr(
-                        trackIntent, userId);
-                if (trackPackage != null
-                        && doesPackageSupportRuntimePermissions(trackPackage)) {
-                    grantRuntimePermissionsLPw(trackPackage, SENSORS_PERMISSIONS, false, userId);
-                    grantRuntimePermissionsLPw(trackPackage, LOCATION_PERMISSIONS, false, userId);
-                }
-            }
-
-            // Print Spooler
-            PackageParser.Package printSpoolerPackage = getSystemPackageLPr(
-                    PrintManager.PRINT_SPOOLER_PACKAGE_NAME);
-            if (printSpoolerPackage != null
-                    && doesPackageSupportRuntimePermissions(printSpoolerPackage)) {
-                grantRuntimePermissionsLPw(printSpoolerPackage, LOCATION_PERMISSIONS, true, userId);
-            }
-
-            // EmergencyInfo
-            Intent emergencyInfoIntent = new Intent(TelephonyManager.ACTION_EMERGENCY_ASSISTANCE);
-            PackageParser.Package emergencyInfoPckg = getDefaultSystemHandlerActivityPackageLPr(
-                    emergencyInfoIntent, userId);
-            if (emergencyInfoPckg != null
-                    && doesPackageSupportRuntimePermissions(emergencyInfoPckg)) {
-                grantRuntimePermissionsLPw(emergencyInfoPckg, CONTACTS_PERMISSIONS, true, userId);
-                grantRuntimePermissionsLPw(emergencyInfoPckg, PHONE_PERMISSIONS, true, userId);
-            }
-
-            // NFC Tag viewer
-            Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW);
-            nfcTagIntent.setType("vnd.android.cursor.item/ndef_msg");
-            PackageParser.Package nfcTagPkg = getDefaultSystemHandlerActivityPackageLPr(
-                    nfcTagIntent, userId);
-            if (nfcTagPkg != null
-                    && doesPackageSupportRuntimePermissions(nfcTagPkg)) {
-                grantRuntimePermissionsLPw(nfcTagPkg, CONTACTS_PERMISSIONS, false, userId);
-                grantRuntimePermissionsLPw(nfcTagPkg, PHONE_PERMISSIONS, false, userId);
-            }
-
-            // Storage Manager
-            Intent storageManagerIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
-            PackageParser.Package storageManagerPckg = getDefaultSystemHandlerActivityPackageLPr(
-                    storageManagerIntent, userId);
-            if (storageManagerPckg != null
-                    && doesPackageSupportRuntimePermissions(storageManagerPckg)) {
-                grantRuntimePermissionsLPw(storageManagerPckg, STORAGE_PERMISSIONS, true, userId);
-            }
-
-            // Companion devices
-            PackageParser.Package companionDeviceDiscoveryPackage = getSystemPackageLPr(
-                    CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME);
-            if (companionDeviceDiscoveryPackage != null
-                    && doesPackageSupportRuntimePermissions(companionDeviceDiscoveryPackage)) {
-                grantRuntimePermissionsLPw(companionDeviceDiscoveryPackage,
-                        LOCATION_PERMISSIONS, true, userId);
-            }
-
-            // Ringtone Picker
-            Intent ringtonePickerIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
-            PackageParser.Package ringtonePickerPackage =
-                    getDefaultSystemHandlerActivityPackageLPr(ringtonePickerIntent, userId);
-            if (ringtonePickerPackage != null
-                    && doesPackageSupportRuntimePermissions(ringtonePickerPackage)) {
-                grantRuntimePermissionsLPw(ringtonePickerPackage,
-                        STORAGE_PERMISSIONS, true, userId);
-            }
-
-            mService.mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
-        }
-    }
-
-    private void grantDefaultPermissionsToDefaultSystemDialerAppLPr(
-            PackageParser.Package dialerPackage, int userId) {
-        if (doesPackageSupportRuntimePermissions(dialerPackage)) {
-            boolean isPhonePermFixed =
-                    mService.hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
-            grantRuntimePermissionsLPw(
-                    dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
-            grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, userId);
-            grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, userId);
-            grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, userId);
-            grantRuntimePermissionsLPw(dialerPackage, CAMERA_PERMISSIONS, userId);
-        }
-    }
-
-    private void grantDefaultPermissionsToDefaultSystemSmsAppLPr(
-            PackageParser.Package smsPackage, int userId) {
-        if (doesPackageSupportRuntimePermissions(smsPackage)) {
-            grantRuntimePermissionsLPw(smsPackage, PHONE_PERMISSIONS, userId);
-            grantRuntimePermissionsLPw(smsPackage, CONTACTS_PERMISSIONS, userId);
-            grantRuntimePermissionsLPw(smsPackage, SMS_PERMISSIONS, userId);
-            grantRuntimePermissionsLPw(smsPackage, STORAGE_PERMISSIONS, userId);
-            grantRuntimePermissionsLPw(smsPackage, MICROPHONE_PERMISSIONS, userId);
-            grantRuntimePermissionsLPw(smsPackage, CAMERA_PERMISSIONS, userId);
-        }
-    }
-
-    public void grantDefaultPermissionsToDefaultSmsAppLPr(String packageName, int userId) {
-        Log.i(TAG, "Granting permissions to default sms app for user:" + userId);
-        if (packageName == null) {
-            return;
-        }
-        PackageParser.Package smsPackage = getPackageLPr(packageName);
-        if (smsPackage != null && doesPackageSupportRuntimePermissions(smsPackage)) {
-            grantRuntimePermissionsLPw(smsPackage, PHONE_PERMISSIONS, false, true, userId);
-            grantRuntimePermissionsLPw(smsPackage, CONTACTS_PERMISSIONS, false, true, userId);
-            grantRuntimePermissionsLPw(smsPackage, SMS_PERMISSIONS, false, true, userId);
-            grantRuntimePermissionsLPw(smsPackage, STORAGE_PERMISSIONS, false, true, userId);
-            grantRuntimePermissionsLPw(smsPackage, MICROPHONE_PERMISSIONS, false, true, userId);
-            grantRuntimePermissionsLPw(smsPackage, CAMERA_PERMISSIONS, false, true, userId);
-        }
-    }
-
-    public void grantDefaultPermissionsToDefaultDialerAppLPr(String packageName, int userId) {
-        Log.i(TAG, "Granting permissions to default dialer app for user:" + userId);
-        if (packageName == null) {
-            return;
-        }
-        PackageParser.Package dialerPackage = getPackageLPr(packageName);
-        if (dialerPackage != null
-                && doesPackageSupportRuntimePermissions(dialerPackage)) {
-            grantRuntimePermissionsLPw(dialerPackage, PHONE_PERMISSIONS, false, true, userId);
-            grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, false, true, userId);
-            grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, false, true, userId);
-            grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, false, true, userId);
-            grantRuntimePermissionsLPw(dialerPackage, CAMERA_PERMISSIONS, false, true, userId);
-        }
-    }
-
-    private void grantDefaultPermissionsToDefaultSimCallManagerLPr(
-            PackageParser.Package simCallManagerPackage, int userId) {
-        Log.i(TAG, "Granting permissions to sim call manager for user:" + userId);
-        if (doesPackageSupportRuntimePermissions(simCallManagerPackage)) {
-            grantRuntimePermissionsLPw(simCallManagerPackage, PHONE_PERMISSIONS, userId);
-            grantRuntimePermissionsLPw(simCallManagerPackage, MICROPHONE_PERMISSIONS, userId);
-        }
-    }
-
-    public void grantDefaultPermissionsToDefaultSimCallManagerLPr(String packageName, int userId) {
-        if (packageName == null) {
-            return;
-        }
-        PackageParser.Package simCallManagerPackage = getPackageLPr(packageName);
-        if (simCallManagerPackage != null) {
-            grantDefaultPermissionsToDefaultSimCallManagerLPr(simCallManagerPackage, userId);
-        }
-    }
-
-    public void grantDefaultPermissionsToEnabledCarrierAppsLPr(String[] packageNames, int userId) {
-        Log.i(TAG, "Granting permissions to enabled carrier apps for user:" + userId);
-        if (packageNames == null) {
-            return;
-        }
-        for (String packageName : packageNames) {
-            PackageParser.Package carrierPackage = getSystemPackageLPr(packageName);
-            if (carrierPackage != null
-                    && doesPackageSupportRuntimePermissions(carrierPackage)) {
-                grantRuntimePermissionsLPw(carrierPackage, PHONE_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(carrierPackage, LOCATION_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(carrierPackage, SMS_PERMISSIONS, userId);
-            }
-        }
-    }
-
-    public void grantDefaultPermissionsToEnabledImsServicesLPr(String[] packageNames, int userId) {
-        Log.i(TAG, "Granting permissions to enabled ImsServices for user:" + userId);
-        if (packageNames == null) {
-            return;
-        }
-        for (String packageName : packageNames) {
-            PackageParser.Package imsServicePackage = getSystemPackageLPr(packageName);
-            if (imsServicePackage != null
-                    && doesPackageSupportRuntimePermissions(imsServicePackage)) {
-                grantRuntimePermissionsLPw(imsServicePackage, PHONE_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(imsServicePackage, MICROPHONE_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(imsServicePackage, LOCATION_PERMISSIONS, userId);
-                grantRuntimePermissionsLPw(imsServicePackage, CAMERA_PERMISSIONS, userId);
-            }
-        }
-    }
-
-    public void grantDefaultPermissionsToDefaultBrowserLPr(String packageName, int userId) {
-        Log.i(TAG, "Granting permissions to default browser for user:" + userId);
-        if (packageName == null) {
-            return;
-        }
-        PackageParser.Package browserPackage = getSystemPackageLPr(packageName);
-        if (browserPackage != null
-                && doesPackageSupportRuntimePermissions(browserPackage)) {
-            grantRuntimePermissionsLPw(browserPackage, LOCATION_PERMISSIONS, false, false, userId);
-        }
-    }
-
-    private PackageParser.Package getDefaultSystemHandlerActivityPackageLPr(
-            Intent intent, int userId) {
-        ResolveInfo handler = mService.resolveIntent(intent,
-                intent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS, userId);
-        if (handler == null || handler.activityInfo == null) {
-            return null;
-        }
-        ActivityInfo activityInfo = handler.activityInfo;
-        if (activityInfo.packageName.equals(mService.mResolveActivity.packageName)
-                && activityInfo.name.equals(mService.mResolveActivity.name)) {
-            return null;
-        }
-        return getSystemPackageLPr(handler.activityInfo.packageName);
-    }
-
-    private PackageParser.Package getDefaultSystemHandlerServicePackageLPr(
-            Intent intent, int userId) {
-        List<ResolveInfo> handlers = mService.queryIntentServices(intent,
-                intent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS, userId)
-                .getList();
-        if (handlers == null) {
-            return null;
-        }
-        final int handlerCount = handlers.size();
-        for (int i = 0; i < handlerCount; i++) {
-            ResolveInfo handler = handlers.get(i);
-            PackageParser.Package handlerPackage = getSystemPackageLPr(
-                    handler.serviceInfo.packageName);
-            if (handlerPackage != null) {
-                return handlerPackage;
-            }
-        }
-        return null;
-    }
-
-    private List<PackageParser.Package> getHeadlessSyncAdapterPackagesLPr(
-            String[] syncAdapterPackageNames, int userId) {
-        List<PackageParser.Package> syncAdapterPackages = new ArrayList<>();
-
-        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
-        homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
-        for (String syncAdapterPackageName : syncAdapterPackageNames) {
-            homeIntent.setPackage(syncAdapterPackageName);
-
-            ResolveInfo homeActivity = mService.resolveIntent(homeIntent,
-                    homeIntent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS,
-                    userId);
-            if (homeActivity != null) {
-                continue;
-            }
-
-            PackageParser.Package syncAdapterPackage = getSystemPackageLPr(syncAdapterPackageName);
-            if (syncAdapterPackage != null) {
-                syncAdapterPackages.add(syncAdapterPackage);
-            }
-        }
-
-        return syncAdapterPackages;
-    }
-
-    private PackageParser.Package getDefaultProviderAuthorityPackageLPr(
-            String authority, int userId) {
-        ProviderInfo provider = mService.resolveContentProvider(authority, DEFAULT_FLAGS, userId);
-        if (provider != null) {
-            return getSystemPackageLPr(provider.packageName);
-        }
-        return null;
-    }
-
-    private PackageParser.Package getPackageLPr(String packageName) {
-        return mService.mPackages.get(packageName);
-    }
-
-    private PackageParser.Package getSystemPackageLPr(String packageName) {
-        PackageParser.Package pkg = getPackageLPr(packageName);
-        if (pkg != null && pkg.isSystemApp()) {
-            return !isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg) ? pkg : null;
-        }
-        return null;
-    }
-
-    private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
-            int userId) {
-        grantRuntimePermissionsLPw(pkg, permissions, false, false, userId);
-    }
-
-    private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
-            boolean systemFixed, int userId) {
-        grantRuntimePermissionsLPw(pkg, permissions, systemFixed, false, userId);
-    }
-
-    private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
-            boolean systemFixed, boolean isDefaultPhoneOrSms, int userId) {
-        if (pkg.requestedPermissions.isEmpty()) {
-            return;
-        }
-
-        List<String> requestedPermissions = pkg.requestedPermissions;
-        Set<String> grantablePermissions = null;
-
-        // If this is the default Phone or SMS app we grant permissions regardless
-        // whether the version on the system image declares the permission as used since
-        // selecting the app as the default Phone or SMS the user makes a deliberate
-        // choice to grant this app the permissions needed to function. For all other
-        // apps, (default grants on first boot and user creation) we don't grant default
-        // permissions if the version on the system image does not declare them.
-        if (!isDefaultPhoneOrSms && pkg.isUpdatedSystemApp()) {
-            PackageSetting sysPs = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
-            if (sysPs != null && sysPs.pkg != null) {
-                if (sysPs.pkg.requestedPermissions.isEmpty()) {
-                    return;
-                }
-                if (!requestedPermissions.equals(sysPs.pkg.requestedPermissions)) {
-                    grantablePermissions = new ArraySet<>(requestedPermissions);
-                    requestedPermissions = sysPs.pkg.requestedPermissions;
-                }
-            }
-        }
-
-        final int grantablePermissionCount = requestedPermissions.size();
-        for (int i = 0; i < grantablePermissionCount; i++) {
-            String permission = requestedPermissions.get(i);
-
-            // If there is a disabled system app it may request a permission the updated
-            // version ot the data partition doesn't, In this case skip the permission.
-            if (grantablePermissions != null && !grantablePermissions.contains(permission)) {
-                continue;
-            }
-
-            if (permissions.contains(permission)) {
-                final int flags = mService.getPermissionFlags(permission, pkg.packageName, userId);
-
-                // If any flags are set to the permission, then it is either set in
-                // its current state by the system or device/profile owner or the user.
-                // In all these cases we do not want to clobber the current state.
-                // Unless the caller wants to override user choices. The override is
-                // to make sure we can grant the needed permission to the default
-                // sms and phone apps after the user chooses this in the UI.
-                if (flags == 0 || isDefaultPhoneOrSms) {
-                    // Never clobber policy or system.
-                    final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
-                            | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
-                    if ((flags & fixedFlags) != 0) {
-                        continue;
-                    }
-
-                    mService.grantRuntimePermission(pkg.packageName, permission, userId);
-                    if (DEBUG) {
-                        Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")
-                                + permission + " to default handler " + pkg.packageName);
-                    }
-
-                    int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
-                    if (systemFixed) {
-                        newFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
-                    }
-
-                    mService.updatePermissionFlags(permission, pkg.packageName,
-                            newFlags, newFlags, userId);
-                }
-
-                // If a component gets a permission for being the default handler A
-                // and also default handler B, we grant the weaker grant form.
-                if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0
-                        && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
-                        && !systemFixed) {
-                    if (DEBUG) {
-                        Log.i(TAG, "Granted not fixed " + permission + " to default handler "
-                                + pkg.packageName);
-                    }
-                    mService.updatePermissionFlags(permission, pkg.packageName,
-                            PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, userId);
-                }
-            }
-        }
-    }
-
-    private boolean isSysComponentOrPersistentPlatformSignedPrivAppLPr(PackageParser.Package pkg) {
-        if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
-            return true;
-        }
-        if (!pkg.isPrivilegedApp()) {
-            return false;
-        }
-        PackageSetting sysPkg = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
-        if (sysPkg != null && sysPkg.pkg != null) {
-            if ((sysPkg.pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
-                return false;
-            }
-        } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
-            return false;
-        }
-        return PackageManagerService.compareSignatures(mService.mPlatformPackage.mSignatures,
-                pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
-    }
-
-    private void grantDefaultPermissionExceptions(int userId) {
-        synchronized (mService.mPackages) {
-            mHandler.removeMessages(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
-
-            if (mGrantExceptions == null) {
-                mGrantExceptions = readDefaultPermissionExceptionsLPw();
-            }
-
-            // mGrantExceptions is null only before the first read and then
-            // it serves as a cache of the default grants that should be
-            // performed for every user. If there is an entry then the app
-            // is on the system image and supports runtime permissions.
-            Set<String> permissions = null;
-            final int exceptionCount = mGrantExceptions.size();
-            for (int i = 0; i < exceptionCount; i++) {
-                String packageName = mGrantExceptions.keyAt(i);
-                PackageParser.Package pkg = getSystemPackageLPr(packageName);
-                List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i);
-                final int permissionGrantCount = permissionGrants.size();
-                for (int j = 0; j < permissionGrantCount; j++) {
-                    DefaultPermissionGrant permissionGrant = permissionGrants.get(j);
-                    if (permissions == null) {
-                        permissions = new ArraySet<>();
-                    } else {
-                        permissions.clear();
-                    }
-                    permissions.add(permissionGrant.name);
-                    grantRuntimePermissionsLPw(pkg, permissions,
-                            permissionGrant.fixed, userId);
-                }
-            }
-        }
-    }
-
-    private File[] getDefaultPermissionFiles() {
-        ArrayList<File> ret = new ArrayList<File>();
-        File dir = new File(Environment.getRootDirectory(), "etc/default-permissions");
-        if (dir.isDirectory() && dir.canRead()) {
-            Collections.addAll(ret, dir.listFiles());
-        }
-        dir = new File(Environment.getVendorDirectory(), "etc/default-permissions");
-        if (dir.isDirectory() && dir.canRead()) {
-            Collections.addAll(ret, dir.listFiles());
-        }
-        return ret.isEmpty() ? null : ret.toArray(new File[0]);
-    }
-
-    private @NonNull ArrayMap<String, List<DefaultPermissionGrant>>
-            readDefaultPermissionExceptionsLPw() {
-        File[] files = getDefaultPermissionFiles();
-        if (files == null) {
-            return new ArrayMap<>(0);
-        }
-
-        ArrayMap<String, List<DefaultPermissionGrant>> grantExceptions = new ArrayMap<>();
-
-        // Iterate over the files in the directory and scan .xml files
-        for (File file : files) {
-            if (!file.getPath().endsWith(".xml")) {
-                Slog.i(TAG, "Non-xml file " + file + " in " + file.getParent() + " directory, ignoring");
-                continue;
-            }
-            if (!file.canRead()) {
-                Slog.w(TAG, "Default permissions file " + file + " cannot be read");
-                continue;
-            }
-            try (
-                InputStream str = new BufferedInputStream(new FileInputStream(file))
-            ) {
-                XmlPullParser parser = Xml.newPullParser();
-                parser.setInput(str, null);
-                parse(parser, grantExceptions);
-            } catch (XmlPullParserException | IOException e) {
-                Slog.w(TAG, "Error reading default permissions file " + file, e);
-            }
-        }
-
-        return grantExceptions;
-    }
-
-    private void parse(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
-            outGrantExceptions) throws IOException, XmlPullParserException {
-        final int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-            if (TAG_EXCEPTIONS.equals(parser.getName())) {
-                parseExceptions(parser, outGrantExceptions);
-            } else {
-                Log.e(TAG, "Unknown tag " + parser.getName());
-            }
-        }
-    }
-
-    private void parseExceptions(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
-            outGrantExceptions) throws IOException, XmlPullParserException {
-        final int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-            if (TAG_EXCEPTION.equals(parser.getName())) {
-                String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
-
-                List<DefaultPermissionGrant> packageExceptions =
-                        outGrantExceptions.get(packageName);
-                if (packageExceptions == null) {
-                    // The package must be on the system image
-                    PackageParser.Package pkg = getSystemPackageLPr(packageName);
-                    if (pkg == null) {
-                        Log.w(TAG, "Unknown package:" + packageName);
-                        XmlUtils.skipCurrentTag(parser);
-                        continue;
-                    }
-
-                    // The package must support runtime permissions
-                    if (!doesPackageSupportRuntimePermissions(pkg)) {
-                        Log.w(TAG, "Skipping non supporting runtime permissions package:"
-                                + packageName);
-                        XmlUtils.skipCurrentTag(parser);
-                        continue;
-                    }
-                    packageExceptions = new ArrayList<>();
-                    outGrantExceptions.put(packageName, packageExceptions);
-                }
-
-                parsePermission(parser, packageExceptions);
-            } else {
-                Log.e(TAG, "Unknown tag " + parser.getName() + "under <exceptions>");
-            }
-        }
-    }
-
-    private void parsePermission(XmlPullParser parser, List<DefaultPermissionGrant>
-            outPackageExceptions) throws IOException, XmlPullParserException {
-        final int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-
-            if (TAG_PERMISSION.contains(parser.getName())) {
-                String name = parser.getAttributeValue(null, ATTR_NAME);
-                if (name == null) {
-                    Log.w(TAG, "Mandatory name attribute missing for permission tag");
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
-                }
-
-                final boolean fixed = XmlUtils.readBooleanAttribute(parser, ATTR_FIXED);
-
-                DefaultPermissionGrant exception = new DefaultPermissionGrant(name, fixed);
-                outPackageExceptions.add(exception);
-            } else {
-                Log.e(TAG, "Unknown tag " + parser.getName() + "under <exception>");
-            }
-        }
-    }
-
-    private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) {
-        return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
-    }
-
-    private static final class DefaultPermissionGrant {
-        final String name;
-        final boolean fixed;
-
-        public DefaultPermissionGrant(String name, boolean fixed) {
-            this.name = name;
-            this.fixed = fixed;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 37b8ebb..c964f91 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -880,8 +880,9 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             for (String grantedPermission : appInfo.getGrantedPermissions()) {
-                BasePermission bp = mService.mSettings.mPermissions.get(grantedPermission);
-                if (bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant()) {
+                final boolean propagatePermission =
+                        mService.mSettings.canPropagatePermissionToInstantApp(grantedPermission);
+                if (propagatePermission) {
                     mService.grantRuntimePermission(packageName, grantedPermission, userId);
                 }
             }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 925f595..8ebeeae 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -365,18 +365,13 @@
                 + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
                 + " target-filter=" + compilerFilter);
 
-        String classLoaderContext;
-        if (dexUseInfo.isUnknownClassLoaderContext() ||
-                dexUseInfo.isUnsupportedClassLoaderContext() ||
-                dexUseInfo.isVariableClassLoaderContext()) {
-            // If we have an unknown (not yet set), unsupported (custom class loaders), or a
-            // variable class loader chain, compile without a context and mark the oat file with
-            // SKIP_SHARED_LIBRARY_CHECK. Note that his might lead to a incorrect compilation.
-            // TODO(calin): We should just extract in this case.
-            classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
-        } else {
-            classLoaderContext = dexUseInfo.getClassLoaderContext();
-        }
+        // TODO(calin): b/64530081 b/66984396. Use SKIP_SHARED_LIBRARY_CHECK for the context
+        // (instead of dexUseInfo.getClassLoaderContext()) in order to compile secondary dex files
+        // in isolation (and avoid to extract/verify the main apk if it's in the class path).
+        // Note this trades correctness for performance since the resulting slow down is
+        // unacceptable in some cases until b/64530081 is fixed.
+        String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+
         try {
             for (String isa : dexUseInfo.getLoaderIsas()) {
                 // Reuse the same dexopt path as for the primary apks. We don't need all the
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index a32ed17..09f9cb8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -80,6 +80,8 @@
 import com.android.internal.util.ImageUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.IoThread;
+import com.android.server.LocalServices;
+import com.android.server.pm.permission.PermissionManagerInternal;
 
 import libcore.io.IoUtils;
 
@@ -122,6 +124,7 @@
 
     private final Context mContext;
     private final PackageManagerService mPm;
+    private final PermissionManagerInternal mPermissionManager;
 
     private AppOpsManager mAppOps;
 
@@ -177,6 +180,7 @@
     public PackageInstallerService(Context context, PackageManagerService pm) {
         mContext = context;
         mPm = pm;
+        mPermissionManager = LocalServices.getService(PermissionManagerInternal.class);
 
         mInstallThread = new HandlerThread(TAG);
         mInstallThread.start();
@@ -397,7 +401,8 @@
     private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
             throws IOException {
         final int callingUid = Binder.getCallingUid();
-        mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");
+        mPermissionManager.enforceCrossUserPermission(
+                callingUid, userId, true, true, "createSession");
 
         if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
             throw new SecurityException("User restriction prevents installing");
@@ -652,7 +657,8 @@
 
     @Override
     public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
-        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getAllSessions");
+        mPermissionManager.enforceCrossUserPermission(
+                Binder.getCallingUid(), userId, true, false, "getAllSessions");
 
         final List<SessionInfo> result = new ArrayList<>();
         synchronized (mSessions) {
@@ -668,7 +674,8 @@
 
     @Override
     public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) {
-        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getMySessions");
+        mPermissionManager.enforceCrossUserPermission(
+                Binder.getCallingUid(), userId, true, false, "getMySessions");
         mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
 
         final List<SessionInfo> result = new ArrayList<>();
@@ -690,7 +697,7 @@
     public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
                 IntentSender statusReceiver, int userId) throws RemoteException {
         final int callingUid = Binder.getCallingUid();
-        mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
         if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
             mAppOps.checkPackage(callingUid, callerPackageName);
         }
@@ -739,7 +746,8 @@
 
     @Override
     public void registerCallback(IPackageInstallerCallback callback, int userId) {
-        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback");
+        mPermissionManager.enforceCrossUserPermission(
+                Binder.getCallingUid(), userId, true, false, "registerCallback");
         mCallbacks.register(callback, userId);
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d05c95f..7d1a647 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -102,10 +102,9 @@
 import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
-import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
-import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
-import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
-
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
 import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
 
 import android.Manifest;
@@ -159,10 +158,11 @@
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.ActivityIntentInfo;
+import android.content.pm.PackageParser.Package;
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.PackageStats;
@@ -282,13 +282,19 @@
 import com.android.server.Watchdog;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.PermissionsState.PermissionState;
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.permission.BasePermission;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
+import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.PermissionManagerInternal;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
+import com.android.server.pm.permission.PermissionsState;
+import com.android.server.pm.permission.PermissionsState.PermissionState;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 
 import dalvik.system.CloseGuard;
@@ -338,6 +344,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -427,9 +434,6 @@
     private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
     private static final int SHELL_UID = Process.SHELL_UID;
 
-    // Cap the size of permission trees that 3rd party apps can define
-    private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768;     // characters of text
-
     // Suffix used during package installation when copying/moving
     // package apks to install directory.
     private static final String INSTALL_PACKAGE_SUFFIX = "-";
@@ -860,7 +864,7 @@
                 String targetPath) {
             return getStaticOverlayPaths(targetPackageName, targetPath);
         }
-    };
+    }
 
     class ParallelPackageParserCallback extends PackageParserCallback {
         List<PackageParser.Package> mOverlayPackages = null;
@@ -1002,7 +1006,9 @@
     final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
             = new SparseArray<IntentFilterVerificationState>();
 
+    // TODO remove this and go through mPermissonManager directly
     final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
+    private final PermissionManagerInternal mPermissionManager;
 
     // List of packages names to keep cached, even if they are uninstalled for all users
     private List<String> mKeepUninstalledPackages;
@@ -1881,6 +1887,69 @@
         }
     }
 
+    private PermissionCallback mPermissionCallback = new PermissionCallback() {
+        @Override
+        public void onGidsChanged(int appId, int userId) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
+                }
+            });
+        }
+        @Override
+        public void onPermissionGranted(int uid, int userId) {
+            mOnPermissionChangeListeners.onPermissionsChanged(uid);
+
+            // Not critical; if this is lost, the application has to request again.
+            synchronized (mPackages) {
+                mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+            }
+        }
+        @Override
+        public void onInstallPermissionGranted() {
+            synchronized (mPackages) {
+                scheduleWriteSettingsLocked();
+            }
+        }
+        @Override
+        public void onPermissionRevoked(int uid, int userId) {
+            mOnPermissionChangeListeners.onPermissionsChanged(uid);
+
+            synchronized (mPackages) {
+                // Critical; after this call the application should never have the permission
+                mSettings.writeRuntimePermissionsForUserLPr(userId, true);
+            }
+
+            final int appId = UserHandle.getAppId(uid);
+            killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
+        }
+        @Override
+        public void onInstallPermissionRevoked() {
+            synchronized (mPackages) {
+                scheduleWriteSettingsLocked();
+            }
+        }
+        @Override
+        public void onPermissionUpdated(int userId) {
+            synchronized (mPackages) {
+                mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+            }
+        }
+        @Override
+        public void onInstallPermissionUpdated() {
+            synchronized (mPackages) {
+                scheduleWriteSettingsLocked();
+            }
+        }
+        @Override
+        public void onPermissionRemoved() {
+            synchronized (mPackages) {
+                mSettings.writeLPr();
+            }
+        }
+    };
+
     private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
             boolean killApp, boolean virtualPreload, String[] grantedPermissions,
             boolean launchedForRestore, String installerPackage,
@@ -1897,7 +1966,10 @@
             // review flag which is used to emulate runtime permissions for
             // legacy apps.
             if (grantPermissions) {
-                grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions);
+                final int callingUid = Binder.getCallingUid();
+                mPermissionManager.grantRequestedRuntimePermissions(
+                        res.pkg, res.newUsers, grantedPermissions, callingUid,
+                        mPermissionCallback);
             }
 
             final boolean update = res.removedInfo != null
@@ -1913,9 +1985,9 @@
             // app that had no children, we grant requested runtime permissions to the new
             // children if the parent on the system image had them already granted.
             if (res.pkg.parentPackage != null) {
-                synchronized (mPackages) {
-                    grantRuntimePermissionsGrantedToDisabledPrivSysPackageParentLPw(res.pkg);
-                }
+                final int callingUid = Binder.getCallingUid();
+                mPermissionManager.grantRuntimePermissionsGrantedToDisabledPackage(
+                        res.pkg, callingUid, mPermissionCallback);
             }
 
             synchronized (mPackages) {
@@ -2080,39 +2152,6 @@
         }
     }
 
-    private void grantRuntimePermissionsGrantedToDisabledPrivSysPackageParentLPw(
-            PackageParser.Package pkg) {
-        if (pkg.parentPackage == null) {
-            return;
-        }
-        if (pkg.requestedPermissions == null) {
-            return;
-        }
-        final PackageSetting disabledSysParentPs = mSettings
-                .getDisabledSystemPkgLPr(pkg.parentPackage.packageName);
-        if (disabledSysParentPs == null || disabledSysParentPs.pkg == null
-                || !disabledSysParentPs.isPrivileged()
-                || (disabledSysParentPs.childPackageNames != null
-                        && !disabledSysParentPs.childPackageNames.isEmpty())) {
-            return;
-        }
-        final int[] allUserIds = sUserManager.getUserIds();
-        final int permCount = pkg.requestedPermissions.size();
-        for (int i = 0; i < permCount; i++) {
-            String permission = pkg.requestedPermissions.get(i);
-            BasePermission bp = mSettings.mPermissions.get(permission);
-            if (bp == null || !(bp.isRuntime() || bp.isDevelopment())) {
-                continue;
-            }
-            for (int userId : allUserIds) {
-                if (disabledSysParentPs.getPermissionsState().hasRuntimePermission(
-                        permission, userId)) {
-                    grantRuntimePermission(pkg.packageName, permission, userId);
-                }
-            }
-        }
-    }
-
     private StorageEventListener mStorageListener = new StorageEventListener() {
         @Override
         public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
@@ -2165,58 +2204,6 @@
         }
     };
 
-    private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
-            String[] grantedPermissions) {
-        for (int userId : userIds) {
-            grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions);
-        }
-    }
-
-    private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId,
-            String[] grantedPermissions) {
-        PackageSetting ps = (PackageSetting) pkg.mExtras;
-        if (ps == null) {
-            return;
-        }
-
-        PermissionsState permissionsState = ps.getPermissionsState();
-
-        final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
-                | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
-
-        final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
-                >= Build.VERSION_CODES.M;
-
-        final boolean instantApp = isInstantApp(pkg.packageName, userId);
-
-        for (String permission : pkg.requestedPermissions) {
-            final BasePermission bp;
-            synchronized (mPackages) {
-                bp = mSettings.mPermissions.get(permission);
-            }
-            if (bp != null && (bp.isRuntime() || bp.isDevelopment())
-                    && (!instantApp || bp.isInstant())
-                    && (supportsRuntimePermissions || !bp.isRuntimeOnly())
-                    && (grantedPermissions == null
-                           || ArrayUtils.contains(grantedPermissions, permission))) {
-                final int flags = permissionsState.getPermissionFlags(permission, userId);
-                if (supportsRuntimePermissions) {
-                    // Installer cannot change immutable permissions.
-                    if ((flags & immutableFlags) == 0) {
-                        grantRuntimePermission(pkg.packageName, permission, userId);
-                    }
-                } else if (mPermissionReviewRequired) {
-                    // In permission review mode we clear the review flag when we
-                    // are asked to install the app with all permissions granted.
-                    if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
-                        updatePermissionFlags(permission, pkg.packageName,
-                                PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, userId);
-                    }
-                }
-            }
-        }
-    }
-
     Bundle extrasForInstallResult(PackageInstalledInfo res) {
         Bundle extras = null;
         switch (res.returnCode) {
@@ -2385,7 +2372,29 @@
         mFactoryTest = factoryTest;
         mOnlyCore = onlyCore;
         mMetrics = new DisplayMetrics();
-        mSettings = new Settings(mPackages);
+        mInstaller = installer;
+
+        // Create sub-components that provide services / data. Order here is important.
+        synchronized (mInstallLock) {
+        synchronized (mPackages) {
+            // Expose private service for system components to use.
+            LocalServices.addService(
+                    PackageManagerInternal.class, new PackageManagerInternalImpl());
+            sUserManager = new UserManagerService(context, this,
+                    new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
+            mPermissionManager = PermissionManagerService.create(context,
+                    new DefaultPermissionGrantedCallback() {
+                        @Override
+                        public void onDefaultRuntimePermissionsGranted(int userId) {
+                            synchronized(mPackages) {
+                                mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
+                            }
+                        }
+                    }, mPackages /*externalLock*/);
+            mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
+            mSettings = new Settings(mPermissionManager.getPermissionSettings(), mPackages);
+        }
+        }
         mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
         mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
@@ -2416,7 +2425,6 @@
             mSeparateProcesses = null;
         }
 
-        mInstaller = installer;
         mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
                 "*dexopt*");
         mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
@@ -2445,31 +2453,12 @@
             mHandler = new PackageHandler(mHandlerThread.getLooper());
             mProcessLoggingHandler = new ProcessLoggingHandler();
             Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
-
-            mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this);
             mInstantAppRegistry = new InstantAppRegistry(this);
 
             File dataDir = Environment.getDataDirectory();
             mAppInstallDir = new File(dataDir, "app");
             mAppLib32InstallDir = new File(dataDir, "app-lib");
             mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
-            sUserManager = new UserManagerService(context, this,
-                    new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
-
-            // Propagate permission configuration in to package manager.
-            ArrayMap<String, SystemConfig.PermissionEntry> permConfig
-                    = systemConfig.getPermissions();
-            for (int i=0; i<permConfig.size(); i++) {
-                SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
-                BasePermission bp = mSettings.mPermissions.get(perm.name);
-                if (bp == null) {
-                    bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
-                    mSettings.mPermissions.put(perm.name, bp);
-                }
-                if (perm.gids != null) {
-                    bp.setGids(perm.gids, perm.perUser);
-                }
-            }
 
             ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
             final int builtInLibCount = libConfig.size();
@@ -3072,8 +3061,6 @@
         // once we have a booted system.
         mInstaller.setWarnIfHeld(mPackages);
 
-        // Expose private service for system components to use.
-        LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
     }
 
@@ -3889,7 +3876,7 @@
     public boolean isPackageAvailable(String packageName, int userId) {
         if (!sUserManager.exists(userId)) return false;
         final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /*requireFullPermission*/, false /*checkShell*/, "is package available");
         synchronized (mPackages) {
             PackageParser.Package p = mPackages.get(packageName);
@@ -3932,7 +3919,7 @@
             int flags, int filterCallingUid, int userId) {
         if (!sUserManager.exists(userId)) return null;
         flags = updateFlagsForPackage(flags, userId, packageName);
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 false /* requireFullPermission */, false /* checkShell */, "get package info");
 
         // reader
@@ -4194,7 +4181,7 @@
         if (!sUserManager.exists(userId)) return -1;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForPackage(flags, userId, packageName);
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid");
 
         // reader
@@ -4224,7 +4211,7 @@
         if (!sUserManager.exists(userId)) return null;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForPackage(flags, userId, packageName);
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids");
 
         // reader
@@ -4253,86 +4240,21 @@
 
     @Override
     public PermissionInfo getPermissionInfo(String name, String packageName, int flags) {
-        final int callingUid = Binder.getCallingUid();
-        if (getInstantAppPackageName(callingUid) != null) {
-            return null;
-        }
-        // reader
-        synchronized (mPackages) {
-            final BasePermission bp = mSettings.mPermissions.get(name);
-            if (bp == null) {
-                return null;
-            }
-            final int adjustedProtectionLevel = adjustPermissionProtectionFlagsLPr(
-                    bp.getProtectionLevel(), packageName, callingUid);
-            return bp.generatePermissionInfo(adjustedProtectionLevel, flags);
-        }
-    }
-
-    private int adjustPermissionProtectionFlagsLPr(int protectionLevel,
-            String packageName, int uid) {
-        // Signature permission flags area always reported
-        final int protectionLevelMasked = protectionLevel
-                & (PermissionInfo.PROTECTION_NORMAL
-                | PermissionInfo.PROTECTION_DANGEROUS
-                | PermissionInfo.PROTECTION_SIGNATURE);
-        if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) {
-            return protectionLevel;
-        }
-
-        // System sees all flags.
-        final int appId = UserHandle.getAppId(uid);
-        if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID
-                || appId == Process.SHELL_UID) {
-            return protectionLevel;
-        }
-
-        // Normalize package name to handle renamed packages and static libs
-        packageName = resolveInternalPackageNameLPr(packageName,
-                PackageManager.VERSION_CODE_HIGHEST);
-
-        // Apps that target O see flags for all protection levels.
-        final PackageSetting ps = mSettings.mPackages.get(packageName);
-        if (ps == null) {
-            return protectionLevel;
-        }
-        if (ps.appId != appId) {
-            return protectionLevel;
-        }
-
-        final PackageParser.Package pkg = mPackages.get(packageName);
-        if (pkg == null) {
-            return protectionLevel;
-        }
-        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
-            return protectionLevelMasked;
-        }
-
-        return protectionLevel;
+        return mPermissionManager.getPermissionInfo(name, packageName, flags, getCallingUid());
     }
 
     @Override
     public @Nullable ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String groupName,
             int flags) {
-        if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
-            return null;
-        }
-        // reader
+        // TODO Move this to PermissionManager when mPermissionGroups is moved there
         synchronized (mPackages) {
             if (groupName != null && !mPermissionGroups.containsKey(groupName)) {
                 // This is thrown as NameNotFoundException
                 return null;
             }
-
-            ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
-            for (BasePermission bp : mSettings.mPermissions.values()) {
-                final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags);
-                if (pi != null) {
-                    out.add(pi);
-                }
-            }
-            return new ParceledListSlice<>(out);
         }
+        return new ParceledListSlice<>(
+                mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid()));
     }
 
     @Override
@@ -4407,7 +4329,7 @@
             int filterCallingUid, int userId) {
         if (!sUserManager.exists(userId)) return null;
         flags = updateFlagsForApplication(flags, userId, packageName);
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 false /* requireFullPermission */, false /* checkShell */, "get application info");
 
         // writer
@@ -4716,7 +4638,8 @@
             triaged = false;
         }
         if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
-            enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
+            mPermissionManager.enforceCrossUserPermission(
+                    Binder.getCallingUid(), userId, false, false,
                     "MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission at "
                     + Debug.getCallers(5));
         } else if ((flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0 && isCallerSystemUser
@@ -4845,7 +4768,7 @@
             int filterCallingUid, int userId) {
         if (!sUserManager.exists(userId)) return null;
         flags = updateFlagsForComponent(flags, userId, component);
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 false /* requireFullPermission */, false /* checkShell */, "get activity info");
         synchronized (mPackages) {
             PackageParser.Activity a = mActivities.mActivities.get(component);
@@ -4904,7 +4827,7 @@
         if (!sUserManager.exists(userId)) return null;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForComponent(flags, userId, component);
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /* requireFullPermission */, false /* checkShell */, "get receiver info");
         synchronized (mPackages) {
             PackageParser.Activity a = mReceivers.mActivities.get(component);
@@ -5041,7 +4964,7 @@
         if (!sUserManager.exists(userId)) return null;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForComponent(flags, userId, component);
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /* requireFullPermission */, false /* checkShell */, "get service info");
         synchronized (mPackages) {
             PackageParser.Service s = mServices.mServices.get(component);
@@ -5065,7 +4988,7 @@
         if (!sUserManager.exists(userId)) return null;
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForComponent(flags, userId, component);
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /* requireFullPermission */, false /* checkShell */, "get provider info");
         synchronized (mPackages) {
             PackageParser.Provider p = mProviders.mProviders.get(component);
@@ -5228,39 +5151,7 @@
 
     @Override
     public int checkPermission(String permName, String pkgName, int userId) {
-        if (!sUserManager.exists(userId)) {
-            return PackageManager.PERMISSION_DENIED;
-        }
-        final int callingUid = Binder.getCallingUid();
-
-        synchronized (mPackages) {
-            final PackageParser.Package p = mPackages.get(pkgName);
-            if (p != null && p.mExtras != null) {
-                final PackageSetting ps = (PackageSetting) p.mExtras;
-                if (filterAppAccessLPr(ps, callingUid, userId)) {
-                    return PackageManager.PERMISSION_DENIED;
-                }
-                final boolean instantApp = ps.getInstantApp(userId);
-                final PermissionsState permissionsState = ps.getPermissionsState();
-                if (permissionsState.hasPermission(permName, userId)) {
-                    if (instantApp) {
-                        BasePermission bp = mSettings.mPermissions.get(permName);
-                        if (bp != null && bp.isInstant()) {
-                            return PackageManager.PERMISSION_GRANTED;
-                        }
-                    } else {
-                        return PackageManager.PERMISSION_GRANTED;
-                    }
-                }
-                // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
-                if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
-                        .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
-                    return PackageManager.PERMISSION_GRANTED;
-                }
-            }
-        }
-
-        return PackageManager.PERMISSION_DENIED;
+        return mPermissionManager.checkPermission(permName, pkgName, getCallingUid(), userId);
     }
 
     @Override
@@ -5291,8 +5182,7 @@
                 final PermissionsState permissionsState = settingBase.getPermissionsState();
                 if (permissionsState.hasPermission(permName, userId)) {
                     if (isUidInstantApp) {
-                        BasePermission bp = mSettings.mPermissions.get(permName);
-                        if (bp != null && bp.isInstant()) {
+                        if (mPermissionManager.isPermissionInstant(permName)) {
                             return PackageManager.PERMISSION_GRANTED;
                         }
                     } else {
@@ -5361,359 +5251,49 @@
         }
     }
 
-    /**
-     * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS
-     * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller.
-     * @param checkShell whether to prevent shell from access if there's a debugging restriction
-     * @param message the message to log on security exception
-     */
-    void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission,
-            boolean checkShell, String message) {
-        if (userId < 0) {
-            throw new IllegalArgumentException("Invalid userId " + userId);
-        }
-        if (checkShell) {
-            enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
-        }
-        if (userId == UserHandle.getUserId(callingUid)) return;
-        if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
-            if (requireFullPermission) {
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
-            } else {
-                try {
-                    mContext.enforceCallingOrSelfPermission(
-                            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
-                } catch (SecurityException se) {
-                    mContext.enforceCallingOrSelfPermission(
-                            android.Manifest.permission.INTERACT_ACROSS_USERS, message);
-                }
-            }
-        }
-    }
-
-    void enforceShellRestriction(String restriction, int callingUid, int userHandle) {
-        if (callingUid == Process.SHELL_UID) {
-            if (userHandle >= 0
-                    && sUserManager.hasUserRestriction(restriction, userHandle)) {
-                throw new SecurityException("Shell does not have permission to access user "
-                        + userHandle);
-            } else if (userHandle < 0) {
-                Slog.e(TAG, "Unable to check shell permission for user " + userHandle + "\n\t"
-                        + Debug.getCallers(3));
-            }
-        }
-    }
-
-    int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
-        int size = 0;
-        for (BasePermission perm : mSettings.mPermissions.values()) {
-            size += tree.calculateFootprint(perm);
-        }
-        return size;
-    }
-
-    void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
-        // We calculate the max size of permissions defined by this uid and throw
-        // if that plus the size of 'info' would exceed our stated maximum.
-        if (tree.getUid() != Process.SYSTEM_UID) {
-            final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree);
-            if (curTreeSize + info.calculateFootprint() > MAX_PERMISSION_TREE_FOOTPRINT) {
-                throw new SecurityException("Permission tree size cap exceeded");
-            }
-        }
-    }
-
-    boolean addPermissionLocked(PermissionInfo info, boolean async) {
-        if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
-            throw new SecurityException("Instant apps can't add permissions");
-        }
-        if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
-            throw new SecurityException("Label must be specified in permission");
-        }
-        BasePermission tree = BasePermission.enforcePermissionTreeLP(
-                mSettings.mPermissionTrees, info.name, Binder.getCallingUid());
-        BasePermission bp = mSettings.mPermissions.get(info.name);
-        boolean added = bp == null;
-        int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
-        if (added) {
-            enforcePermissionCapLocked(info, tree);
-            bp = new BasePermission(info.name, tree.getSourcePackageName(),
-                    BasePermission.TYPE_DYNAMIC);
-        } else if (bp.isDynamic()) {
-            throw new SecurityException(
-                    "Not allowed to modify non-dynamic permission "
-                    + info.name);
-        }
-        final boolean changed = bp.addToTree(fixedLevel, info, tree);
-        if (added) {
-            mSettings.mPermissions.put(info.name, bp);
-        }
-        if (changed) {
-            if (!async) {
-                mSettings.writeLPr();
-            } else {
-                scheduleWriteSettingsLocked();
-            }
-        }
-        return added;
+    boolean addPermission(PermissionInfo info, final boolean async) {
+        return mPermissionManager.addPermission(
+                info, async, getCallingUid(), new PermissionCallback() {
+                    @Override
+                    public void onPermissionChanged() {
+                        if (!async) {
+                            mSettings.writeLPr();
+                        } else {
+                            scheduleWriteSettingsLocked();
+                        }
+                    }
+                });
     }
 
     @Override
     public boolean addPermission(PermissionInfo info) {
         synchronized (mPackages) {
-            return addPermissionLocked(info, false);
+            return addPermission(info, false);
         }
     }
 
     @Override
     public boolean addPermissionAsync(PermissionInfo info) {
         synchronized (mPackages) {
-            return addPermissionLocked(info, true);
+            return addPermission(info, true);
         }
     }
 
     @Override
-    public void removePermission(String name) {
-        if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
-            throw new SecurityException("Instant applications don't have access to this method");
-        }
-        synchronized (mPackages) {
-            BasePermission.enforcePermissionTreeLP(
-                    mSettings.mPermissionTrees, name, Binder.getCallingUid());
-            BasePermission bp = mSettings.mPermissions.get(name);
-            if (bp != null) {
-                if (bp.isDynamic()) {
-                    throw new SecurityException(
-                            "Not allowed to modify non-dynamic permission "
-                            + name);
-                }
-                mSettings.mPermissions.remove(name);
-                mSettings.writeLPr();
-            }
-        }
+    public void removePermission(String permName) {
+        mPermissionManager.removePermission(permName, getCallingUid(), mPermissionCallback);
     }
 
     @Override
-    public void grantRuntimePermission(String packageName, String name, final int userId) {
-        grantRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
-    }
-
-    private void grantRuntimePermission(String packageName, String name, final int userId,
-            boolean overridePolicy) {
-        if (!sUserManager.exists(userId)) {
-            Log.e(TAG, "No such user:" + userId);
-            return;
-        }
-        final int callingUid = Binder.getCallingUid();
-
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
-                "grantRuntimePermission");
-
-        enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, true /* checkShell */,
-                "grantRuntimePermission");
-
-        final int uid;
-        final PackageSetting ps;
-
-        synchronized (mPackages) {
-            final PackageParser.Package pkg = mPackages.get(packageName);
-            if (pkg == null) {
-                throw new IllegalArgumentException("Unknown package: " + packageName);
-            }
-            final BasePermission bp = mSettings.mPermissions.get(name);
-            if (bp == null) {
-                throw new IllegalArgumentException("Unknown permission: " + name);
-            }
-            ps = (PackageSetting) pkg.mExtras;
-            if (ps == null
-                    || filterAppAccessLPr(ps, callingUid, userId)) {
-                throw new IllegalArgumentException("Unknown package: " + packageName);
-            }
-
-            bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);
-
-            // If a permission review is required for legacy apps we represent
-            // their permissions as always granted runtime ones since we need
-            // to keep the review required permission flag per user while an
-            // install permission's state is shared across all users.
-            if (mPermissionReviewRequired
-                    && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
-                    && bp.isRuntime()) {
-                return;
-            }
-
-            uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
-
-            final PermissionsState permissionsState = ps.getPermissionsState();
-
-            final int flags = permissionsState.getPermissionFlags(name, userId);
-            if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
-                throw new SecurityException("Cannot grant system fixed permission "
-                        + name + " for package " + packageName);
-            }
-            if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
-                throw new SecurityException("Cannot grant policy fixed permission "
-                        + name + " for package " + packageName);
-            }
-
-            if (bp.isDevelopment()) {
-                // Development permissions must be handled specially, since they are not
-                // normal runtime permissions.  For now they apply to all users.
-                if (permissionsState.grantInstallPermission(bp) !=
-                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                    scheduleWriteSettingsLocked();
-                }
-                return;
-            }
-
-            if (ps.getInstantApp(userId) && !bp.isInstant()) {
-                throw new SecurityException("Cannot grant non-ephemeral permission"
-                        + name + " for package " + packageName);
-            }
-
-            if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
-                Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
-                return;
-            }
-
-            final int result = permissionsState.grantRuntimePermission(bp, userId);
-            switch (result) {
-                case PermissionsState.PERMISSION_OPERATION_FAILURE: {
-                    return;
-                }
-
-                case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
-                    final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
-                    mHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
-                        }
-                    });
-                }
-                break;
-            }
-
-            if (bp.isRuntime()) {
-                logPermissionGranted(mContext, name, packageName);
-            }
-
-            mOnPermissionChangeListeners.onPermissionsChanged(uid);
-
-            // Not critical if that is lost - app has to request again.
-            mSettings.writeRuntimePermissionsForUserLPr(userId, false);
-        }
-
-        // Only need to do this if user is initialized. Otherwise it's a new user
-        // and there are no processes running as the user yet and there's no need
-        // to make an expensive call to remount processes for the changed permissions.
-        if (READ_EXTERNAL_STORAGE.equals(name)
-                || WRITE_EXTERNAL_STORAGE.equals(name)) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                if (sUserManager.isInitialized(userId)) {
-                    StorageManagerInternal storageManagerInternal = LocalServices.getService(
-                            StorageManagerInternal.class);
-                    storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
+    public void grantRuntimePermission(String packageName, String permName, final int userId) {
+        mPermissionManager.grantRuntimePermission(permName, packageName, false /*overridePolicy*/,
+                getCallingUid(), userId, mPermissionCallback);
     }
 
     @Override
-    public void revokeRuntimePermission(String packageName, String name, int userId) {
-        revokeRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
-    }
-
-    private void revokeRuntimePermission(String packageName, String name, int userId,
-            boolean overridePolicy) {
-        if (!sUserManager.exists(userId)) {
-            Log.e(TAG, "No such user:" + userId);
-            return;
-        }
-
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
-                "revokeRuntimePermission");
-
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
-                true /* requireFullPermission */, true /* checkShell */,
-                "revokeRuntimePermission");
-
-        final int appId;
-
-        synchronized (mPackages) {
-            final PackageParser.Package pkg = mPackages.get(packageName);
-            if (pkg == null) {
-                throw new IllegalArgumentException("Unknown package: " + packageName);
-            }
-            final PackageSetting ps = (PackageSetting) pkg.mExtras;
-            if (ps == null
-                    || filterAppAccessLPr(ps, Binder.getCallingUid(), userId)) {
-                throw new IllegalArgumentException("Unknown package: " + packageName);
-            }
-            final BasePermission bp = mSettings.mPermissions.get(name);
-            if (bp == null) {
-                throw new IllegalArgumentException("Unknown permission: " + name);
-            }
-
-            bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);
-
-            // If a permission review is required for legacy apps we represent
-            // their permissions as always granted runtime ones since we need
-            // to keep the review required permission flag per user while an
-            // install permission's state is shared across all users.
-            if (mPermissionReviewRequired
-                    && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
-                    && bp.isRuntime()) {
-                return;
-            }
-
-            final PermissionsState permissionsState = ps.getPermissionsState();
-
-            final int flags = permissionsState.getPermissionFlags(name, userId);
-            if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
-                throw new SecurityException("Cannot revoke system fixed permission "
-                        + name + " for package " + packageName);
-            }
-            if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
-                throw new SecurityException("Cannot revoke policy fixed permission "
-                        + name + " for package " + packageName);
-            }
-
-            if (bp.isDevelopment()) {
-                // Development permissions must be handled specially, since they are not
-                // normal runtime permissions.  For now they apply to all users.
-                if (permissionsState.revokeInstallPermission(bp) !=
-                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                    scheduleWriteSettingsLocked();
-                }
-                return;
-            }
-
-            if (permissionsState.revokeRuntimePermission(bp, userId) ==
-                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                return;
-            }
-
-            if (bp.isRuntime()) {
-                logPermissionRevoked(mContext, name, packageName);
-            }
-
-            mOnPermissionChangeListeners.onPermissionsChanged(pkg.applicationInfo.uid);
-
-            // Critical, after this call app should never have the permission.
-            mSettings.writeRuntimePermissionsForUserLPr(userId, true);
-
-            appId = UserHandle.getAppId(pkg.applicationInfo.uid);
-        }
-
-        killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
+    public void revokeRuntimePermission(String packageName, String permName, int userId) {
+        mPermissionManager.revokeRuntimePermission(permName, packageName, false /*overridePolicy*/,
+                getCallingUid(), userId, mPermissionCallback);
     }
 
     /**
@@ -5806,91 +5386,16 @@
     }
 
     @Override
-    public int getPermissionFlags(String name, String packageName, int userId) {
-        if (!sUserManager.exists(userId)) {
-            return 0;
-        }
-
-        enforceGrantRevokeRuntimePermissionPermissions("getPermissionFlags");
-
-        final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, false /* checkShell */,
-                "getPermissionFlags");
-
-        synchronized (mPackages) {
-            final PackageParser.Package pkg = mPackages.get(packageName);
-            if (pkg == null) {
-                return 0;
-            }
-            final BasePermission bp = mSettings.mPermissions.get(name);
-            if (bp == null) {
-                return 0;
-            }
-            final PackageSetting ps = (PackageSetting) pkg.mExtras;
-            if (ps == null
-                    || filterAppAccessLPr(ps, callingUid, userId)) {
-                return 0;
-            }
-            PermissionsState permissionsState = ps.getPermissionsState();
-            return permissionsState.getPermissionFlags(name, userId);
-        }
+    public int getPermissionFlags(String permName, String packageName, int userId) {
+        return mPermissionManager.getPermissionFlags(permName, packageName, getCallingUid(), userId);
     }
 
     @Override
-    public void updatePermissionFlags(String name, String packageName, int flagMask,
+    public void updatePermissionFlags(String permName, String packageName, int flagMask,
             int flagValues, int userId) {
-        if (!sUserManager.exists(userId)) {
-            return;
-        }
-
-        enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags");
-
-        final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
-                true /* requireFullPermission */, true /* checkShell */,
-                "updatePermissionFlags");
-
-        // Only the system can change these flags and nothing else.
-        if (getCallingUid() != Process.SYSTEM_UID) {
-            flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
-            flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
-            flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
-            flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
-            flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
-        }
-
-        synchronized (mPackages) {
-            final PackageParser.Package pkg = mPackages.get(packageName);
-            if (pkg == null) {
-                throw new IllegalArgumentException("Unknown package: " + packageName);
-            }
-            final PackageSetting ps = (PackageSetting) pkg.mExtras;
-            if (ps == null
-                    || filterAppAccessLPr(ps, callingUid, userId)) {
-                throw new IllegalArgumentException("Unknown package: " + packageName);
-            }
-
-            final BasePermission bp = mSettings.mPermissions.get(name);
-            if (bp == null) {
-                throw new IllegalArgumentException("Unknown permission: " + name);
-            }
-
-            PermissionsState permissionsState = ps.getPermissionsState();
-
-            boolean hadState = permissionsState.getRuntimePermissionState(name, userId) != null;
-
-            if (permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues)) {
-                // Install and runtime permissions are stored in different places,
-                // so figure out what permission changed and persist the change.
-                if (permissionsState.getInstallPermissionState(name) != null) {
-                    scheduleWriteSettingsLocked();
-                } else if (permissionsState.getRuntimePermissionState(name, userId) != null
-                        || hadState) {
-                    mSettings.writeRuntimePermissionsForUserLPr(userId, false);
-                }
-            }
-        }
+        mPermissionManager.updatePermissionFlags(
+                permName, packageName, flagMask, flagValues, getCallingUid(), userId,
+                mPermissionCallback);
     }
 
     /**
@@ -5899,52 +5404,16 @@
      */
     @Override
     public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) {
-        if (!sUserManager.exists(userId)) {
-            return;
-        }
-
-        enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlagsForAllApps");
-
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
-                true /* requireFullPermission */, true /* checkShell */,
-                "updatePermissionFlagsForAllApps");
-
-        // Only the system can change system fixed flags.
-        if (getCallingUid() != Process.SYSTEM_UID) {
-            flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
-            flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
-        }
-
         synchronized (mPackages) {
-            boolean changed = false;
-            final int packageCount = mPackages.size();
-            for (int pkgIndex = 0; pkgIndex < packageCount; pkgIndex++) {
-                final PackageParser.Package pkg = mPackages.valueAt(pkgIndex);
-                final PackageSetting ps = (PackageSetting) pkg.mExtras;
-                if (ps == null) {
-                    continue;
-                }
-                PermissionsState permissionsState = ps.getPermissionsState();
-                changed |= permissionsState.updatePermissionFlagsForAllPermissions(
-                        userId, flagMask, flagValues);
-            }
+            final boolean changed = mPermissionManager.updatePermissionFlagsForAllApps(
+                    flagMask, flagValues, getCallingUid(), userId, mPackages.values(),
+                    mPermissionCallback);
             if (changed) {
                 mSettings.writeRuntimePermissionsForUserLPr(userId, false);
             }
         }
     }
 
-    private void enforceGrantRevokeRuntimePermissionPermissions(String message) {
-        if (mContext.checkCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
-                != PackageManager.PERMISSION_GRANTED
-            && mContext.checkCallingOrSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException(message + " requires "
-                    + Manifest.permission.GRANT_RUNTIME_PERMISSIONS + " or "
-                    + Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
-        }
-    }
-
     @Override
     public boolean shouldShowRequestPermissionRationale(String permissionName,
             String packageName, int userId) {
@@ -6136,7 +5605,7 @@
      * <br />
      * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ.
      */
-    static int compareSignatures(Signature[] s1, Signature[] s2) {
+    public static int compareSignatures(Signature[] s1, Signature[] s2) {
         if (s1 == null) {
             return s2 == null
                     ? PackageManager.SIGNATURE_NEITHER_SIGNED
@@ -6490,9 +5959,14 @@
     public ResolveInfo resolveIntent(Intent intent, String resolvedType,
             int flags, int userId) {
         return resolveIntentInternal(
-                intent, resolvedType, flags, userId, false /*includeInstantApps*/);
+                intent, resolvedType, flags, userId, false /*resolveForStart*/);
     }
 
+    /**
+     * Normally instant apps can only be resolved when they're visible to the caller.
+     * However, if {@code resolveForStart} is {@code true}, all instant apps are visible
+     * since we need to allow the system to start any installed application.
+     */
     private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
             int flags, int userId, boolean resolveForStart) {
         try {
@@ -6501,7 +5975,7 @@
             if (!sUserManager.exists(userId)) return null;
             final int callingUid = Binder.getCallingUid();
             flags = updateFlagsForResolve(flags, userId, intent, callingUid, resolveForStart);
-            enforceCrossUserPermission(callingUid, userId,
+            mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                     false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
 
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
@@ -7082,7 +6556,7 @@
             boolean resolveForStart, boolean allowDynamicSplits) {
         if (!sUserManager.exists(userId)) return Collections.emptyList();
         final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 false /* requireFullPermission */, false /* checkShell */,
                 "query intent activities");
         final String pkgName = intent.getPackage();
@@ -7837,7 +7311,7 @@
         final int callingUid = Binder.getCallingUid();
         flags = updateFlagsForResolve(flags, userId, intent, callingUid,
                 false /*includeInstantApps*/);
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /*requireFullPermission*/, false /*checkShell*/,
                 "query intent activity options");
         final String resultsAction = intent.getAction();
@@ -8018,7 +7492,7 @@
             String resolvedType, int flags, int userId, boolean allowDynamicSplits) {
         if (!sUserManager.exists(userId)) return Collections.emptyList();
         final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /*requireFullPermission*/, false /*checkShell*/,
                 "query intent receivers");
         final String instantAppPkgName = getInstantAppPackageName(callingUid);
@@ -8130,7 +7604,7 @@
             String resolvedType, int flags, int userId, int callingUid,
             boolean includeInstantApps) {
         if (!sUserManager.exists(userId)) return Collections.emptyList();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /*requireFullPermission*/, false /*checkShell*/,
                 "query intent receivers");
         final String instantAppPkgName = getInstantAppPackageName(callingUid);
@@ -8370,7 +7844,7 @@
         if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
         flags = updateFlagsForPackage(flags, userId, null);
         final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "get installed packages");
 
@@ -8457,7 +7931,7 @@
             String[] permissions, int flags, int userId) {
         if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
         flags = updateFlagsForPackage(flags, userId, permissions);
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "get packages holding permissions");
         final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
@@ -8562,7 +8036,7 @@
             mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
                     "getEphemeralApplications");
         }
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "getEphemeralApplications");
         synchronized (mPackages) {
@@ -8577,7 +8051,7 @@
 
     @Override
     public boolean isInstantApp(String packageName, int userId) {
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "isInstantApp");
         if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
@@ -8610,7 +8084,7 @@
             return null;
         }
 
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "getInstantAppCookie");
         if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
@@ -8628,7 +8102,7 @@
             return true;
         }
 
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, true /* checkShell */,
                 "setInstantAppCookie");
         if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
@@ -8650,7 +8124,7 @@
             mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
                     "getInstantAppIcon");
         }
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "getInstantAppIcon");
 
@@ -8710,6 +8184,10 @@
 
     @Override
     public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
+        return resolveContentProviderInternal(name, flags, userId);
+    }
+
+    private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
         flags = updateFlagsForComponent(flags, userId, name);
         final String instantAppPkgName = getInstantAppPackageName(Binder.getCallingUid());
@@ -10255,7 +9733,8 @@
         }
     }
 
-    private void addSharedLibraryLPr(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file,
+    private void addSharedLibraryLPr(Set<String> usesLibraryFiles,
+            SharedLibraryEntry file,
             PackageParser.Package changingLib) {
         if (file.path != null) {
             usesLibraryFiles.add(file.path);
@@ -10284,7 +9763,10 @@
         if (pkg == null) {
             return;
         }
-        ArraySet<String> usesLibraryFiles = null;
+        // The collection used here must maintain the order of addition (so
+        // that libraries are searched in the correct order) and must have no
+        // duplicates.
+        Set<String> usesLibraryFiles = null;
         if (pkg.usesLibraries != null) {
             usesLibraryFiles = addSharedLibrariesLPw(pkg.usesLibraries,
                     null, null, pkg.packageName, changingLib, true,
@@ -10308,10 +9790,10 @@
         }
     }
 
-    private ArraySet<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
+    private Set<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
             @Nullable int[] requiredVersions, @Nullable String[][] requiredCertDigests,
             @NonNull String packageName, @Nullable PackageParser.Package changingLib,
-            boolean required, int targetSdk, @Nullable ArraySet<String> outUsedLibraries)
+            boolean required, int targetSdk, @Nullable Set<String> outUsedLibraries)
             throws PackageManagerException {
         final int libCount = requestedLibraries.size();
         for (int i = 0; i < libCount; i++) {
@@ -10377,7 +9859,9 @@
                 }
 
                 if (outUsedLibraries == null) {
-                    outUsedLibraries = new ArraySet<>();
+                    // Use LinkedHashSet to preserve the order of files added to
+                    // usesLibraryFiles while eliminating duplicates.
+                    outUsedLibraries = new LinkedHashSet<>();
                 }
                 addSharedLibraryLPr(outUsedLibraries, libEntry, changingLib);
             }
@@ -11739,12 +11223,25 @@
                     }
                 }
 
-                final ArrayMap<String, BasePermission> permissionMap =
-                        p.tree ? mSettings.mPermissionTrees
-                                : mSettings.mPermissions;
-                final BasePermission bp = BasePermission.createOrUpdate(
-                        permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees, chatty);
-                permissionMap.put(p.info.name, bp);
+                // TODO Move to PermissionManager once mPermissionTrees moves there.
+//                        p.tree ? mSettings.mPermissionTrees
+//                                : mSettings.mPermissions;
+//                final BasePermission bp = BasePermission.createOrUpdate(
+//                        permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees, chatty);
+//                permissionMap.put(p.info.name, bp);
+                if (p.tree) {
+                    final ArrayMap<String, BasePermission> permissionMap =
+                            mSettings.mPermissionTrees;
+                    final BasePermission bp = BasePermission.createOrUpdate(
+                            permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees,
+                            chatty);
+                    permissionMap.put(p.info.name, bp);
+                } else {
+                    final BasePermission bp = BasePermission.createOrUpdate(
+                            (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name),
+                            p, pkg, mSettings.mPermissionTrees, chatty);
+                    mPermissionManager.putPermissionTEMP(p.info.name, bp);
+                }
             }
 
             N = pkg.instrumentation.size();
@@ -12474,7 +11971,7 @@
         r = null;
         for (i=0; i<N; i++) {
             PackageParser.Permission p = pkg.permissions.get(i);
-            BasePermission bp = mSettings.mPermissions.get(p.info.name);
+            BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name);
             if (bp == null) {
                 bp = mSettings.mPermissionTrees.get(p.info.name);
             }
@@ -12504,8 +12001,7 @@
         r = null;
         for (i=0; i<N; i++) {
             String perm = pkg.requestedPermissions.get(i);
-            BasePermission bp = mSettings.mPermissions.get(perm);
-            if (bp != null && bp.isAppOp()) {
+            if (mPermissionManager.isPermissionAppOp(perm)) {
                 ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(perm);
                 if (appOpPkgs != null) {
                     appOpPkgs.remove(pkg.packageName);
@@ -12644,9 +12140,10 @@
 
         // Make sure all dynamic permissions have been assigned to a package,
         // and make sure there are no dangling permissions.
-        it = mSettings.mPermissions.values().iterator();
-        while (it.hasNext()) {
-            final BasePermission bp = it.next();
+        final Iterator<BasePermission> permissionIter =
+                mPermissionManager.getPermissionIteratorTEMP();
+        while (permissionIter.hasNext()) {
+            final BasePermission bp = permissionIter.next();
             if (bp.isDynamic()) {
                 bp.updateDynamicPermission(mSettings.mPermissionTrees);
             }
@@ -12658,13 +12155,13 @@
             if (bp.getSourcePackageSetting() == null) {
                 Slog.w(TAG, "Removing dangling permission: " + bp.getName()
                         + " from package " + bp.getSourcePackageName());
-                it.remove();
+                permissionIter.remove();
             } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) {
                 if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) {
                     Slog.i(TAG, "Removing old permission: " + bp.getName()
                             + " from package " + bp.getSourcePackageName());
                     flags |= UPDATE_PERMISSIONS_ALL;
-                    it.remove();
+                    permissionIter.remove();
                 }
             }
         }
@@ -12733,8 +12230,9 @@
                 // the runtime ones are written only if changed. The only cases of
                 // changed runtime permissions here are promotion of an install to
                 // runtime and revocation of a runtime from a shared user.
-                changedRuntimePermissionUserIds = revokeUnusedSharedUserPermissionsLPw(
-                        ps.sharedUser, UserManagerService.getInstance().getUserIds());
+                changedRuntimePermissionUserIds =
+                        mPermissionManager.revokeUnusedSharedUserPermissions(
+                                ps.sharedUser, UserManagerService.getInstance().getUserIds());
                 if (!ArrayUtils.isEmpty(changedRuntimePermissionUserIds)) {
                     runtimePermissionsRevoked = true;
                 }
@@ -12746,7 +12244,7 @@
         final int N = pkg.requestedPermissions.size();
         for (int i=0; i<N; i++) {
             final String name = pkg.requestedPermissions.get(i);
-            final BasePermission bp = mSettings.mPermissions.get(name);
+            final BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(name);
             final boolean appSupportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
                     >= Build.VERSION_CODES.M;
 
@@ -14505,7 +14003,7 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
 
         final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, true /* checkShell */, "installPackageAsUser");
 
         if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
@@ -14744,7 +14242,7 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
         PackageSetting pkgSetting;
         final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, true /* checkShell */,
                 "setApplicationHiddenSetting for user " + userId);
 
@@ -14846,7 +14344,7 @@
     public boolean getApplicationHiddenSettingAsUser(String packageName, int userId) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
         final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "getApplicationHidden for user " + userId);
         PackageSetting ps;
@@ -14878,7 +14376,7 @@
                 null);
         PackageSetting pkgSetting;
         final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, true /* checkShell */,
                 "installExistingPackage for user " + userId);
         if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
@@ -14983,7 +14481,7 @@
             int userId) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
         final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, true /* checkShell */,
                 "setPackagesSuspended for user " + userId);
 
@@ -15044,7 +14542,7 @@
     @Override
     public boolean isPackageSuspendedForUser(String packageName, int userId) {
         final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "isPackageSuspendedForUser for user " + userId);
         synchronized (mPackages) {
@@ -15491,7 +14989,7 @@
         synchronized (mPackages) {
             boolean result = mSettings.setDefaultBrowserPackageNameLPw(packageName, userId);
             if (packageName != null) {
-                mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowserLPr(
+                mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowser(
                         packageName, userId);
             }
             return result;
@@ -17635,66 +17133,6 @@
         }
     }
 
-    private int[] revokeUnusedSharedUserPermissionsLPw(SharedUserSetting su, int[] allUserIds) {
-        // Collect all used permissions in the UID
-        ArraySet<String> usedPermissions = new ArraySet<>();
-        final int packageCount = su.packages.size();
-        for (int i = 0; i < packageCount; i++) {
-            PackageSetting ps = su.packages.valueAt(i);
-            if (ps.pkg == null) {
-                continue;
-            }
-            final int requestedPermCount = ps.pkg.requestedPermissions.size();
-            for (int j = 0; j < requestedPermCount; j++) {
-                String permission = ps.pkg.requestedPermissions.get(j);
-                BasePermission bp = mSettings.mPermissions.get(permission);
-                if (bp != null) {
-                    usedPermissions.add(permission);
-                }
-            }
-        }
-
-        PermissionsState permissionsState = su.getPermissionsState();
-        // Prune install permissions
-        List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates();
-        final int installPermCount = installPermStates.size();
-        for (int i = installPermCount - 1; i >= 0;  i--) {
-            PermissionState permissionState = installPermStates.get(i);
-            if (!usedPermissions.contains(permissionState.getName())) {
-                BasePermission bp = mSettings.mPermissions.get(permissionState.getName());
-                if (bp != null) {
-                    permissionsState.revokeInstallPermission(bp);
-                    permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
-                            PackageManager.MASK_PERMISSION_FLAGS, 0);
-                }
-            }
-        }
-
-        int[] runtimePermissionChangedUserIds = EmptyArray.INT;
-
-        // Prune runtime permissions
-        for (int userId : allUserIds) {
-            List<PermissionState> runtimePermStates = permissionsState
-                    .getRuntimePermissionStates(userId);
-            final int runtimePermCount = runtimePermStates.size();
-            for (int i = runtimePermCount - 1; i >= 0; i--) {
-                PermissionState permissionState = runtimePermStates.get(i);
-                if (!usedPermissions.contains(permissionState.getName())) {
-                    BasePermission bp = mSettings.mPermissions.get(permissionState.getName());
-                    if (bp != null) {
-                        permissionsState.revokeRuntimePermission(bp, userId);
-                        permissionsState.updatePermissionFlags(bp, userId,
-                                PackageManager.MASK_PERMISSION_FLAGS, 0);
-                        runtimePermissionChangedUserIds = ArrayUtils.appendInt(
-                                runtimePermissionChangedUserIds, userId);
-                    }
-                }
-            }
-        }
-
-        return runtimePermissionChangedUserIds;
-    }
-
     private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
             int[] allUsers, PackageInstalledInfo res, UserHandle user, int installReason) {
         // Update the parent package setting
@@ -18095,8 +17533,9 @@
 
             int N = pkg.permissions.size();
             for (int i = N-1; i >= 0; i--) {
-                PackageParser.Permission perm = pkg.permissions.get(i);
-                BasePermission bp = mSettings.mPermissions.get(perm.info.name);
+                final PackageParser.Permission perm = pkg.permissions.get(i);
+                final BasePermission bp =
+                        (BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);
 
                 // Don't allow anyone but the system to define ephemeral permissions.
                 if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
@@ -18266,7 +17705,13 @@
         // TODO: Layering violation
         BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
 
-        startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
+        if (!instantApp) {
+            startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
+        } else {
+            if (DEBUG_DOMAIN_VERIFICATION) {
+                Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
+            }
+        }
 
         try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
                 "installPackageLI")) {
@@ -19851,7 +19296,7 @@
                 android.Manifest.permission.CLEAR_APP_USER_DATA, null);
 
         final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, false /* checkShell */, "clear application data");
 
         final PackageSetting ps = mSettings.getPackageLPr(packageName);
@@ -19990,8 +19435,9 @@
 
         final int permissionCount = ps.pkg.requestedPermissions.size();
         for (int i = 0; i < permissionCount; i++) {
-            final String permissionName = ps.pkg.requestedPermissions.get(i);
-            final BasePermission bp = mSettings.mPermissions.get(permissionName);
+            final String permName = ps.pkg.requestedPermissions.get(i);
+            final BasePermission bp =
+                    (BasePermission) mPermissionManager.getPermissionTEMP(permName);
             if (bp == null) {
                 continue;
             }
@@ -20003,7 +19449,7 @@
                 for (int j = 0; j < packageCount; j++) {
                     PackageSetting pkg = ps.sharedUser.packages.valueAt(j);
                     if (pkg.pkg != null && !pkg.pkg.packageName.equals(ps.pkg.packageName)
-                            && pkg.pkg.requestedPermissions.contains(permissionName)) {
+                            && pkg.pkg.requestedPermissions.contains(permName)) {
                         used = true;
                         break;
                     }
@@ -20015,11 +19461,11 @@
 
             final PermissionsState permissionsState = ps.getPermissionsState();
 
-            final int oldFlags = permissionsState.getPermissionFlags(permissionName, userId);
+            final int oldFlags = permissionsState.getPermissionFlags(permName, userId);
 
             // Always clear the user settable flags.
             final boolean hasInstallState =
-                    permissionsState.getInstallPermissionState(permissionName) != null;
+                    permissionsState.getInstallPermissionState(permName) != null;
             // If permission review is enabled and this is a legacy app, mark the
             // permission as requiring a review as this is the initial state.
             int flags = 0;
@@ -20119,7 +19565,7 @@
         final int callingUid = Binder.getCallingUid();
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.DELETE_CACHE_FILES, null);
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 /* requireFullPermission= */ true, /* checkShell= */ false,
                 "delete application cache files");
         final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
@@ -20239,7 +19685,7 @@
             String opname) {
         // writer
         int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, false /* checkShell */, "add preferred activity");
         if (filter.countActions() == 0) {
             Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
@@ -20304,7 +19750,7 @@
         }
 
         final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "replace preferred activity");
         synchronized (mPackages) {
@@ -20981,8 +20427,11 @@
                     newFlagSet |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
                 }
                 if (DEBUG_BACKUP) {
-                    Slog.v(TAG, "  + Restoring grant: pkg=" + pkgName + " perm=" + permName
-                            + " granted=" + isGranted + " bits=0x" + Integer.toHexString(newFlagSet));
+                    Slog.v(TAG, "  + Restoring grant:"
+                            + " pkg=" + pkgName
+                            + " perm=" + permName
+                            + " granted=" + isGranted
+                            + " bits=0x" + Integer.toHexString(newFlagSet));
                 }
                 final PackageSetting ps = mSettings.mPackages.get(pkgName);
                 if (ps != null) {
@@ -20991,13 +20440,15 @@
                         Slog.v(TAG, "        + already installed; applying");
                     }
                     PermissionsState perms = ps.getPermissionsState();
-                    BasePermission bp = mSettings.mPermissions.get(permName);
+                    BasePermission bp =
+                            (BasePermission) mPermissionManager.getPermissionTEMP(permName);
                     if (bp != null) {
                         if (isGranted) {
                             perms.grantRuntimePermission(bp, userId);
                         }
                         if (newFlagSet != 0) {
-                            perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet);
+                            perms.updatePermissionFlags(
+                                    bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet);
                         }
                     }
                 } else {
@@ -21026,7 +20477,8 @@
                         android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         int callingUid = Binder.getCallingUid();
         enforceOwnerRights(ownerPackage, callingUid);
-        enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
+        PackageManagerServiceUtils.enforceShellRestriction(
+                UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
         if (intentFilter.countActions() == 0) {
             Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions");
             return;
@@ -21057,7 +20509,8 @@
                         android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         final int callingUid = Binder.getCallingUid();
         enforceOwnerRights(ownerPackage, callingUid);
-        enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
+        PackageManagerServiceUtils.enforceShellRestriction(
+                UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
         synchronized (mPackages) {
             CrossProfileIntentResolver resolver =
                     mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
@@ -21287,7 +20740,7 @@
             permission = mContext.checkCallingOrSelfPermission(
                     android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
         }
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /* requireFullPermission */, true /* checkShell */, "set enabled");
         final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
         boolean sendNow = false;
@@ -21576,7 +21029,7 @@
         if (!sUserManager.exists(userId)) {
             return;
         }
-        enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
+        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
                 false /* checkShell */, "flushPackageRestrictions");
         synchronized (mPackages) {
             mSettings.writePackageRestrictionsLPr(userId);
@@ -21618,7 +21071,7 @@
         final int permission = mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
         final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, true /* checkShell */, "stop package");
         // writer
         synchronized (mPackages) {
@@ -21658,7 +21111,7 @@
     public int getApplicationEnabledSetting(String packageName, int userId) {
         if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
         int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /* requireFullPermission */, false /* checkShell */, "get enabled");
         // reader
         synchronized (mPackages) {
@@ -21673,7 +21126,7 @@
     public int getComponentEnabledSetting(ComponentName component, int userId) {
         if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
         int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /*requireFullPermission*/, false /*checkShell*/, "getComponentEnabled");
         synchronized (mPackages) {
             if (filterAppAccessLPr(mSettings.getPackageLPr(component.getPackageName()), callingUid,
@@ -21771,7 +21224,7 @@
 
         // If we upgraded grant all default permissions before kicking off.
         for (int userId : grantPermissionsUserIds) {
-            mDefaultPermissionPolicy.grantDefaultPermissions(userId);
+            mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId);
         }
 
         if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
@@ -23856,7 +23309,9 @@
     }
 
     void onNewUserCreated(final int userId) {
-        mDefaultPermissionPolicy.grantDefaultPermissions(userId);
+        synchronized(mPackages) {
+            mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId);
+        }
         // If permission review for legacy apps is required, we represent
         // dagerous permissions for such apps as always granted runtime
         // permissions to keep per user flag state whether review is needed.
@@ -24296,70 +23751,129 @@
 
     private class PackageManagerInternalImpl extends PackageManagerInternal {
         @Override
-        public void setLocationPackagesProvider(PackagesProvider provider) {
+        public void updatePermissionFlagsTEMP(String permName, String packageName, int flagMask,
+                int flagValues, int userId) {
+            PackageManagerService.this.updatePermissionFlags(
+                    permName, packageName, flagMask, flagValues, userId);
+        }
+
+        @Override
+        public int getPermissionFlagsTEMP(String permName, String packageName, int userId) {
+            return PackageManagerService.this.getPermissionFlags(permName, packageName, userId);
+        }
+
+        @Override
+        public Object enforcePermissionTreeTEMP(String permName, int callingUid) {
             synchronized (mPackages) {
-                mDefaultPermissionPolicy.setLocationPackagesProviderLPw(provider);
+                return BasePermission.enforcePermissionTreeLP(
+                        mSettings.mPermissionTrees, permName, callingUid);
             }
         }
+        @Override
+        public boolean isInstantApp(String packageName, int userId) {
+            return PackageManagerService.this.isInstantApp(packageName, userId);
+        }
+
+        @Override
+        public String getInstantAppPackageName(int uid) {
+            return PackageManagerService.this.getInstantAppPackageName(uid);
+        }
+
+        @Override
+        public boolean filterAppAccess(PackageParser.Package pkg, int callingUid, int userId) {
+            synchronized (mPackages) {
+                return PackageManagerService.this.filterAppAccessLPr(
+                        (PackageSetting) pkg.mExtras, callingUid, userId);
+            }
+        }
+
+        @Override
+        public PackageParser.Package getPackage(String packageName) {
+            synchronized (mPackages) {
+                packageName = resolveInternalPackageNameLPr(
+                        packageName, PackageManager.VERSION_CODE_HIGHEST);
+                return mPackages.get(packageName);
+            }
+        }
+
+        @Override
+        public PackageParser.Package getDisabledPackage(String packageName) {
+            synchronized (mPackages) {
+                final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
+                return (ps != null) ? ps.pkg : null;
+            }
+        }
+
+        @Override
+        public String getKnownPackageName(int knownPackage, int userId) {
+            switch(knownPackage) {
+                case PackageManagerInternal.PACKAGE_BROWSER:
+                    return getDefaultBrowserPackageName(userId);
+                case PackageManagerInternal.PACKAGE_INSTALLER:
+                    return mRequiredInstallerPackage;
+                case PackageManagerInternal.PACKAGE_SETUP_WIZARD:
+                    return mSetupWizardPackage;
+                case PackageManagerInternal.PACKAGE_SYSTEM:
+                    return "android";
+                case PackageManagerInternal.PACKAGE_VERIFIER:
+                    return mRequiredVerifierPackage;
+            }
+            return null;
+        }
+
+        @Override
+        public boolean isResolveActivityComponent(ComponentInfo component) {
+            return mResolveActivity.packageName.equals(component.packageName)
+                    && mResolveActivity.name.equals(component.name);
+        }
+
+        @Override
+        public void setLocationPackagesProvider(PackagesProvider provider) {
+            mDefaultPermissionPolicy.setLocationPackagesProvider(provider);
+        }
 
         @Override
         public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
-            synchronized (mPackages) {
-                mDefaultPermissionPolicy.setVoiceInteractionPackagesProviderLPw(provider);
-            }
+            mDefaultPermissionPolicy.setVoiceInteractionPackagesProvider(provider);
         }
 
         @Override
         public void setSmsAppPackagesProvider(PackagesProvider provider) {
-            synchronized (mPackages) {
-                mDefaultPermissionPolicy.setSmsAppPackagesProviderLPw(provider);
-            }
+            mDefaultPermissionPolicy.setSmsAppPackagesProvider(provider);
         }
 
         @Override
         public void setDialerAppPackagesProvider(PackagesProvider provider) {
-            synchronized (mPackages) {
-                mDefaultPermissionPolicy.setDialerAppPackagesProviderLPw(provider);
-            }
+            mDefaultPermissionPolicy.setDialerAppPackagesProvider(provider);
         }
 
         @Override
         public void setSimCallManagerPackagesProvider(PackagesProvider provider) {
-            synchronized (mPackages) {
-                mDefaultPermissionPolicy.setSimCallManagerPackagesProviderLPw(provider);
-            }
+            mDefaultPermissionPolicy.setSimCallManagerPackagesProvider(provider);
         }
 
         @Override
         public void setSyncAdapterPackagesprovider(SyncAdapterPackagesProvider provider) {
-            synchronized (mPackages) {
-                mDefaultPermissionPolicy.setSyncAdapterPackagesProviderLPw(provider);
-            }
+            mDefaultPermissionPolicy.setSyncAdapterPackagesProvider(provider);
         }
 
         @Override
         public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) {
-            synchronized (mPackages) {
-                mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSmsAppLPr(
-                        packageName, userId);
-            }
+            mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSmsApp(packageName, userId);
         }
 
         @Override
         public void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId) {
             synchronized (mPackages) {
                 mSettings.setDefaultDialerPackageNameLPw(packageName, userId);
-                mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultDialerAppLPr(
-                        packageName, userId);
             }
+            mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultDialerApp(packageName, userId);
         }
 
         @Override
         public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
-            synchronized (mPackages) {
-                mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSimCallManagerLPr(
-                        packageName, userId);
-            }
+            mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSimCallManager(
+                    packageName, userId);
         }
 
         @Override
@@ -24446,6 +23960,15 @@
         }
 
         @Override
+        public List<ResolveInfo> queryIntentServices(
+                Intent intent, int flags, int callingUid, int userId) {
+            final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
+            return PackageManagerService.this
+                    .queryIntentServicesInternal(intent, resolvedType, flags, userId, callingUid,
+                            false);
+        }
+
+        @Override
         public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
                 int userId) {
             return PackageManagerService.this.getHomeActivitiesAsUser(allHomeCandidates, userId);
@@ -24480,17 +24003,19 @@
         }
 
         @Override
-        public void grantRuntimePermission(String packageName, String name, int userId,
+        public void grantRuntimePermission(String packageName, String permName, int userId,
                 boolean overridePolicy) {
-            PackageManagerService.this.grantRuntimePermission(packageName, name, userId,
-                    overridePolicy);
+            PackageManagerService.this.mPermissionManager.grantRuntimePermission(
+                    permName, packageName, overridePolicy, getCallingUid(), userId,
+                    mPermissionCallback);
         }
 
         @Override
-        public void revokeRuntimePermission(String packageName, String name, int userId,
+        public void revokeRuntimePermission(String packageName, String permName, int userId,
                 boolean overridePolicy) {
-            PackageManagerService.this.revokeRuntimePermission(packageName, name, userId,
-                    overridePolicy);
+            mPermissionManager.revokeRuntimePermission(
+                    permName, packageName, overridePolicy, getCallingUid(), userId,
+                    mPermissionCallback);
         }
 
         @Override
@@ -24612,9 +24137,9 @@
 
         @Override
         public ResolveInfo resolveIntent(Intent intent, String resolvedType,
-                int flags, int userId) {
+                int flags, int userId, boolean resolveForStart) {
             return resolveIntentInternal(
-                    intent, resolvedType, flags, userId, true /*resolveForStart*/);
+                    intent, resolvedType, flags, userId, resolveForStart);
         }
 
         @Override
@@ -24624,6 +24149,12 @@
         }
 
         @Override
+        public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
+            return PackageManagerService.this.resolveContentProviderInternal(
+                    name, flags, userId);
+        }
+
+        @Override
         public void addIsolatedUid(int isolatedUid, int ownerUid) {
             synchronized (mPackages) {
                 mIsolatedOwners.put(isolatedUid, ownerUid);
@@ -24670,7 +24201,7 @@
         synchronized (mPackages) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledCarrierAppsLPr(
+                mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledCarrierApps(
                         packageNames, userId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -24684,7 +24215,7 @@
         synchronized (mPackages) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledImsServicesLPr(
+                mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledImsServices(
                         packageNames, userId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -24759,7 +24290,7 @@
     @Override
     public int getInstallReason(String packageName, int userId) {
         final int callingUid = Binder.getCallingUid();
-        enforceCrossUserPermission(callingUid, userId,
+        mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "get install reason");
         synchronized (mPackages) {
@@ -24837,7 +24368,7 @@
     public String getInstantAppAndroidId(String packageName, int userId) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_INSTANT_APPS,
                 "getInstantAppAndroidId");
-        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "getInstantAppAndroidId");
         // Make sure the target is an Instant App.
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 48ddf5e..8f7971e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -30,6 +30,8 @@
 import android.content.pm.PackageParser;
 import android.content.pm.ResolveInfo;
 import android.os.Build;
+import android.os.Debug;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.system.ErrnoException;
@@ -314,4 +316,18 @@
             }
         }
     }
+
+    public static void enforceShellRestriction(String restriction, int callingUid, int userHandle) {
+        if (callingUid == Process.SHELL_UID) {
+            if (userHandle >= 0
+                    && PackageManagerService.sUserManager.hasUserRestriction(
+                            restriction, userHandle)) {
+                throw new SecurityException("Shell does not have permission to access user "
+                        + userHandle);
+            } else if (userHandle < 0) {
+                Slog.e(PackageManagerService.TAG, "Unable to check shell permission for user "
+                        + userHandle + "\n\t" + Debug.getCallers(3));
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 88fe3c1..83cb2db2 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -23,13 +23,15 @@
 import android.service.pm.PackageProto;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.server.pm.permission.PermissionsState;
+
 import java.io.File;
 import java.util.List;
 
 /**
  * Settings data for a particular package we know about.
  */
-final class PackageSetting extends PackageSettingBase {
+public final class PackageSetting extends PackageSettingBase {
     int appId;
     PackageParser.Package pkg;
     /**
@@ -110,6 +112,14 @@
                 : super.getPermissionsState();
     }
 
+    public PackageParser.Package getPackage() {
+        return pkg;
+    }
+
+    public int getAppId() {
+        return appId;
+    }
+
     public boolean isPrivileged() {
         return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
     }
@@ -138,6 +148,10 @@
         return true;
     }
 
+    public boolean hasChildPackages() {
+        return childPackageNames != null && !childPackageNames.isEmpty();
+    }
+
     public void writeToProto(ProtoOutputStream proto, long fieldId, List<UserInfo> users) {
         final long packageToken = proto.start(fieldId);
         proto.write(PackageProto.NAME, (realName != null ? realName : name));
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 0d4878f..e19e83f 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -413,7 +413,7 @@
         modifyUserState(userId).suspended = suspended;
     }
 
-    boolean getInstantApp(int userId) {
+    public boolean getInstantApp(int userId) {
         return readUserState(userId).instantApp;
     }
 
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index e17cec0..c97f5e5 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -18,6 +18,8 @@
 
 import android.content.pm.ApplicationInfo;
 
+import com.android.server.pm.permission.PermissionsState;
+
 abstract class SettingBase {
     int pkgFlags;
     int pkgPrivateFlags;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 06cf79a..0084411 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -86,8 +86,10 @@
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.PermissionsState.PermissionState;
 import com.android.server.pm.permission.BasePermission;
+import com.android.server.pm.permission.PermissionSettings;
+import com.android.server.pm.permission.PermissionsState;
+import com.android.server.pm.permission.PermissionsState.PermissionState;
 
 import libcore.io.IoUtils;
 
@@ -376,10 +378,6 @@
     private final ArrayMap<Long, Integer> mKeySetRefs =
             new ArrayMap<Long, Integer>();
 
-    // Mapping from permission names to info about them.
-    final ArrayMap<String, BasePermission> mPermissions =
-            new ArrayMap<String, BasePermission>();
-
     // Mapping from permission tree names to info about them.
     final ArrayMap<String, BasePermission> mPermissionTrees =
             new ArrayMap<String, BasePermission>();
@@ -417,14 +415,16 @@
     private final File mSystemDir;
 
     public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
+    /** Settings and other information about permissions */
+    private final PermissionSettings mPermissions;
 
-    Settings(Object lock) {
-        this(Environment.getDataDirectory(), lock);
+    Settings(PermissionSettings permissions, Object lock) {
+        this(Environment.getDataDirectory(), permissions, lock);
     }
 
-    Settings(File dataDir, Object lock) {
+    Settings(File dataDir, PermissionSettings permission, Object lock) {
         mLock = lock;
-
+        mPermissions = permission;
         mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
 
         mSystemDir = new File(dataDir, "system");
@@ -487,7 +487,7 @@
         final PermissionsState perms = ps.getPermissionsState();
 
         for (RestoredPermissionGrant grant : grants) {
-            BasePermission bp = mPermissions.get(grant.permissionName);
+            BasePermission bp = mPermissions.getPermission(grant.permissionName);
             if (bp != null) {
                 if (grant.granted) {
                     perms.grantRuntimePermission(bp, userId);
@@ -504,6 +504,10 @@
         writeRuntimePermissionsForUserLPr(userId, false);
     }
 
+    public boolean canPropagatePermissionToInstantApp(String permName) {
+        return mPermissions.canPropagatePermissionToInstantApp(permName);
+    }
+
     void setInstallerPackageName(String pkgName, String installerPkgName) {
         PackageSetting p = mPackages.get(pkgName);
         if (p != null) {
@@ -661,16 +665,11 @@
         }
     }
 
-    // Transfer ownership of permissions from one package to another.
+    /**
+     * Transfers ownership of permissions from one package to another.
+     */
     void transferPermissionsLPw(String origPackageName, String newPackageName) {
-        // Transfer ownership of permissions to the new package.
-        for (int i=0; i<2; i++) {
-            ArrayMap<String, BasePermission> permissions =
-                    i == 0 ? mPermissionTrees : mPermissions;
-            for (BasePermission bp : permissions.values()) {
-                bp.transfer(origPackageName, newPackageName);
-            }
-        }
+        mPermissions.transferPermissions(origPackageName, newPackageName, mPermissionTrees);
     }
 
     /**
@@ -1058,7 +1057,7 @@
 
         // Update permissions
         for (String eachPerm : deletedPs.pkg.requestedPermissions) {
-            BasePermission bp = mPermissions.get(eachPerm);
+            BasePermission bp = mPermissions.getPermission(eachPerm);
             if (bp == null) {
                 continue;
             }
@@ -2208,7 +2207,7 @@
             if (tagName.equals(TAG_ITEM)) {
                 String name = parser.getAttributeValue(null, ATTR_NAME);
 
-                BasePermission bp = mPermissions.get(name);
+                BasePermission bp = mPermissions.getPermission(name);
                 if (bp == null) {
                     Slog.w(PackageManagerService.TAG, "Unknown permission: " + name);
                     XmlUtils.skipCurrentTag(parser);
@@ -2503,9 +2502,7 @@
             serializer.endTag(null, "permission-trees");
 
             serializer.startTag(null, "permissions");
-            for (BasePermission bp : mPermissions.values()) {
-                writePermissionLPr(serializer, bp);
-            }
+            mPermissions.writePermissions(serializer);
             serializer.endTag(null, "permissions");
 
             for (final PackageSetting pkg : mPackages.values()) {
@@ -3043,9 +3040,9 @@
                 if (tagName.equals("package")) {
                     readPackageLPw(parser);
                 } else if (tagName.equals("permissions")) {
-                    readPermissionsLPw(mPermissions, parser);
+                    mPermissions.readPermissions(parser);
                 } else if (tagName.equals("permission-trees")) {
-                    readPermissionsLPw(mPermissionTrees, parser);
+                    PermissionSettings.readPermissions(mPermissionTrees, parser);
                 } else if (tagName.equals("shared-user")) {
                     readSharedUserLPw(parser);
                 } else if (tagName.equals("preferred-packages")) {
@@ -3549,25 +3546,6 @@
         }
     }
 
-    private void readPermissionsLPw(ArrayMap<String, BasePermission> out, XmlPullParser parser)
-            throws IOException, XmlPullParserException {
-        int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-
-            if (!BasePermission.readLPw(out, parser)) {
-                PackageManagerService.reportSettingsProblem(Log.WARN,
-                        "Unknown element reading permissions: " + parser.getName() + " at "
-                                + parser.getPositionDescription());
-            }
-            XmlUtils.skipCurrentTag(parser);
-        }
-    }
-
     private void readDisabledSysPackageLPw(XmlPullParser parser) throws XmlPullParserException,
             IOException {
         String name = parser.getAttributeValue(null, ATTR_NAME);
@@ -4906,11 +4884,8 @@
 
     void dumpPermissionsLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
             DumpState dumpState) {
-        boolean printedSomething = false;
-        for (BasePermission bp : mPermissions.values()) {
-            printedSomething = bp.dumpPermissionsLPr(pw, packageName, permissionNames,
-                    mReadExternalStorageEnforced == Boolean.TRUE, printedSomething, dumpState);
-        }
+        mPermissions.dumpPermissions(pw, packageName, permissionNames,
+                (mReadExternalStorageEnforced == Boolean.TRUE), dumpState);
     }
 
     void dumpSharedUsersLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
@@ -5341,7 +5316,7 @@
             PermissionsState permissionsState = sb.getPermissionsState();
             for (PermissionState permissionState
                     : permissionsState.getRuntimePermissionStates(userId)) {
-                BasePermission bp = mPermissions.get(permissionState.getName());
+                BasePermission bp = mPermissions.getPermission(permissionState.getName());
                 if (bp != null) {
                     permissionsState.revokeRuntimePermission(bp, userId);
                     permissionsState.updatePermissionFlags(bp, userId,
@@ -5502,7 +5477,7 @@
                 switch (parser.getName()) {
                     case TAG_ITEM: {
                         String name = parser.getAttributeValue(null, ATTR_NAME);
-                        BasePermission bp = mPermissions.get(name);
+                        BasePermission bp = mPermissions.getPermission(name);
                         if (bp == null) {
                             Slog.w(PackageManagerService.TAG, "Unknown permission:" + name);
                             XmlUtils.skipCurrentTag(parser);
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 06e020a..a0dadae 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -16,12 +16,18 @@
 
 package com.android.server.pm;
 
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
 import android.util.ArraySet;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
 /**
  * Settings data for a particular shared user ID we know about.
  */
-final class SharedUserSetting extends SettingBase {
+public final class SharedUserSetting extends SettingBase {
     final String name;
 
     int userId;
@@ -73,4 +79,18 @@
             setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags);
         }
     }
+
+    public @Nullable List<PackageParser.Package> getPackages() {
+        if (packages == null || packages.size() == 0) {
+            return null;
+        }
+        final ArrayList<PackageParser.Package> pkgList = new ArrayList<>(packages.size());
+        for (PackageSetting ps : packages) {
+            if (ps == null) {
+                continue;
+            }
+            pkgList.add(ps.pkg);
+        }
+        return pkgList;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index 3060840..f922ad1 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -25,6 +25,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.ShortcutService.DumpFilter;
 import com.android.server.pm.ShortcutUser.PackageWithUser;
 
 import org.json.JSONException;
@@ -293,7 +294,7 @@
         return ret;
     }
 
-    public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+    public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) {
         pw.println();
 
         pw.print(prefix);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 9309f18..6fc1e73 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -33,6 +33,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
+import com.android.server.pm.ShortcutService.DumpFilter;
 import com.android.server.pm.ShortcutService.ShortcutOperation;
 import com.android.server.pm.ShortcutService.Stats;
 
@@ -1144,7 +1145,7 @@
         return false;
     }
 
-    public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+    public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) {
         pw.println();
 
         pw.print(prefix);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 15d2071..27560c5f 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -40,8 +40,8 @@
 import android.content.pm.LauncherApps.ShortcutQuery;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
@@ -134,6 +134,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
+import java.util.regex.Pattern;
 
 /**
  * TODO:
@@ -3455,121 +3456,265 @@
 
     @VisibleForTesting
     void dumpNoCheck(FileDescriptor fd, PrintWriter pw, String[] args) {
+        final DumpFilter filter = parseDumpArgs(args);
 
-        boolean dumpMain = true;
-        boolean checkin = false;
-        boolean clear = false;
-        boolean dumpUid = false;
-        boolean dumpFiles = false;
-
-        if (args != null) {
-            for (String arg : args) {
-                if ("-c".equals(arg)) {
-                    checkin = true;
-
-                } else if ("--checkin".equals(arg)) {
-                    checkin = true;
-                    clear = true;
-
-                } else if ("-a".equals(arg) || "--all".equals(arg)) {
-                    dumpUid = true;
-                    dumpFiles = true;
-
-                } else if ("-u".equals(arg) || "--uid".equals(arg)) {
-                    dumpUid = true;
-
-                } else if ("-f".equals(arg) || "--files".equals(arg)) {
-                    dumpFiles = true;
-
-                } else if ("-n".equals(arg) || "--no-main".equals(arg)) {
-                    dumpMain = false;
-                }
-            }
-        }
-
-        if (checkin) {
+        if (filter.shouldDumpCheckIn()) {
             // Other flags are not supported for checkin.
-            dumpCheckin(pw, clear);
+            dumpCheckin(pw, filter.shouldCheckInClear());
         } else {
-            if (dumpMain) {
-                dumpInner(pw);
+            if (filter.shouldDumpMain()) {
+                dumpInner(pw, filter);
                 pw.println();
             }
-            if (dumpUid) {
+            if (filter.shouldDumpUid()) {
                 dumpUid(pw);
                 pw.println();
             }
-            if (dumpFiles) {
+            if (filter.shouldDumpFiles()) {
                 dumpDumpFiles(pw);
                 pw.println();
             }
         }
     }
 
-    private void dumpInner(PrintWriter pw) {
-        synchronized (mLock) {
-            final long now = injectCurrentTimeMillis();
-            pw.print("Now: [");
-            pw.print(now);
-            pw.print("] ");
-            pw.print(formatTime(now));
+    private static DumpFilter parseDumpArgs(String[] args) {
+        final DumpFilter filter = new DumpFilter();
+        if (args == null) {
+            return filter;
+        }
 
-            pw.print("  Raw last reset: [");
-            pw.print(mRawLastResetTime);
-            pw.print("] ");
-            pw.print(formatTime(mRawLastResetTime));
+        int argIndex = 0;
+        while (argIndex < args.length) {
+            final String arg = args[argIndex++];
 
-            final long last = getLastResetTimeLocked();
-            pw.print("  Last reset: [");
-            pw.print(last);
-            pw.print("] ");
-            pw.print(formatTime(last));
+            if ("-c".equals(arg)) {
+                filter.setDumpCheckIn(true);
+                continue;
+            }
+            if ("--checkin".equals(arg)) {
+                filter.setDumpCheckIn(true);
+                filter.setCheckInClear(true);
+                continue;
+            }
+            if ("-a".equals(arg) || "--all".equals(arg)) {
+                filter.setDumpUid(true);
+                filter.setDumpFiles(true);
+                continue;
+            }
+            if ("-u".equals(arg) || "--uid".equals(arg)) {
+                filter.setDumpUid(true);
+                continue;
+            }
+            if ("-f".equals(arg) || "--files".equals(arg)) {
+                filter.setDumpFiles(true);
+                continue;
+            }
+            if ("-n".equals(arg) || "--no-main".equals(arg)) {
+                filter.setDumpMain(false);
+                continue;
+            }
+            if ("--user".equals(arg)) {
+                if (argIndex >= args.length) {
+                    throw new IllegalArgumentException("Missing user ID for --user");
+                }
+                try {
+                    filter.addUser(Integer.parseInt(args[argIndex++]));
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("Invalid user ID", e);
+                }
+                continue;
+            }
+            if ("-p".equals(arg) || "--package".equals(arg)) {
+                if (argIndex >= args.length) {
+                    throw new IllegalArgumentException("Missing package name for --package");
+                }
+                filter.addPackageRegex(args[argIndex++]);
+                filter.setDumpDetails(false);
+                continue;
+            }
+            if (arg.startsWith("-")) {
+                throw new IllegalArgumentException("Unknown option " + arg);
+            }
+            break;
+        }
+        while (argIndex < args.length) {
+            filter.addPackage(args[argIndex++]);
+        }
+        return filter;
+    }
 
-            final long next = getNextResetTimeLocked();
-            pw.print("  Next reset: [");
-            pw.print(next);
-            pw.print("] ");
-            pw.print(formatTime(next));
+    static class DumpFilter {
+        private boolean mDumpCheckIn = false;
+        private boolean mCheckInClear = false;
 
-            pw.print("  Config:");
-            pw.print("    Max icon dim: ");
-            pw.println(mMaxIconDimension);
-            pw.print("    Icon format: ");
-            pw.println(mIconPersistFormat);
-            pw.print("    Icon quality: ");
-            pw.println(mIconPersistQuality);
-            pw.print("    saveDelayMillis: ");
-            pw.println(mSaveDelayMillis);
-            pw.print("    resetInterval: ");
-            pw.println(mResetInterval);
-            pw.print("    maxUpdatesPerInterval: ");
-            pw.println(mMaxUpdatesPerInterval);
-            pw.print("    maxShortcutsPerActivity: ");
-            pw.println(mMaxShortcuts);
-            pw.println();
+        private boolean mDumpMain = true;
+        private boolean mDumpUid = false;
+        private boolean mDumpFiles = false;
 
-            pw.println("  Stats:");
-            synchronized (mStatLock) {
-                for (int i = 0; i < Stats.COUNT; i++) {
-                    dumpStatLS(pw, "    ", i);
+        private boolean mDumpDetails = true;
+        private List<Pattern> mPackagePatterns = new ArrayList<>();
+        private List<Integer> mUsers = new ArrayList<>();
+
+        void addPackageRegex(String regex) {
+            mPackagePatterns.add(Pattern.compile(regex));
+        }
+
+        public void addPackage(String packageName) {
+            addPackageRegex(Pattern.quote(packageName));
+        }
+
+        void addUser(int userId) {
+            mUsers.add(userId);
+        }
+
+        boolean isPackageMatch(String packageName) {
+            if (mPackagePatterns.size() == 0) {
+                return true;
+            }
+            for (int i = 0; i < mPackagePatterns.size(); i++) {
+                if (mPackagePatterns.get(i).matcher(packageName).find()) {
+                    return true;
                 }
             }
+            return false;
+        }
 
-            pw.println();
-            pw.print("  #Failures: ");
-            pw.println(mWtfCount);
+        boolean isUserMatch(int userId) {
+            if (mUsers.size() == 0) {
+                return true;
+            }
+            for (int i = 0; i < mUsers.size(); i++) {
+                if (mUsers.get(i) == userId) {
+                    return true;
+                }
+            }
+            return false;
+        }
 
-            if (mLastWtfStacktrace != null) {
-                pw.print("  Last failure stack trace: ");
-                pw.println(Log.getStackTraceString(mLastWtfStacktrace));
+        public boolean shouldDumpCheckIn() {
+            return mDumpCheckIn;
+        }
+
+        public void setDumpCheckIn(boolean dumpCheckIn) {
+            mDumpCheckIn = dumpCheckIn;
+        }
+
+        public boolean shouldCheckInClear() {
+            return mCheckInClear;
+        }
+
+        public void setCheckInClear(boolean checkInClear) {
+            mCheckInClear = checkInClear;
+        }
+
+        public boolean shouldDumpMain() {
+            return mDumpMain;
+        }
+
+        public void setDumpMain(boolean dumpMain) {
+            mDumpMain = dumpMain;
+        }
+
+        public boolean shouldDumpUid() {
+            return mDumpUid;
+        }
+
+        public void setDumpUid(boolean dumpUid) {
+            mDumpUid = dumpUid;
+        }
+
+        public boolean shouldDumpFiles() {
+            return mDumpFiles;
+        }
+
+        public void setDumpFiles(boolean dumpFiles) {
+            mDumpFiles = dumpFiles;
+        }
+
+        public boolean shouldDumpDetails() {
+            return mDumpDetails;
+        }
+
+        public void setDumpDetails(boolean dumpDetails) {
+            mDumpDetails = dumpDetails;
+        }
+    }
+
+    private void dumpInner(PrintWriter pw) {
+        dumpInner(pw, new DumpFilter());
+    }
+
+    private void dumpInner(PrintWriter pw, DumpFilter filter) {
+        synchronized (mLock) {
+            if (filter.shouldDumpDetails()) {
+                final long now = injectCurrentTimeMillis();
+                pw.print("Now: [");
+                pw.print(now);
+                pw.print("] ");
+                pw.print(formatTime(now));
+
+                pw.print("  Raw last reset: [");
+                pw.print(mRawLastResetTime);
+                pw.print("] ");
+                pw.print(formatTime(mRawLastResetTime));
+
+                final long last = getLastResetTimeLocked();
+                pw.print("  Last reset: [");
+                pw.print(last);
+                pw.print("] ");
+                pw.print(formatTime(last));
+
+                final long next = getNextResetTimeLocked();
+                pw.print("  Next reset: [");
+                pw.print(next);
+                pw.print("] ");
+                pw.print(formatTime(next));
+
+                pw.print("  Config:");
+                pw.print("    Max icon dim: ");
+                pw.println(mMaxIconDimension);
+                pw.print("    Icon format: ");
+                pw.println(mIconPersistFormat);
+                pw.print("    Icon quality: ");
+                pw.println(mIconPersistQuality);
+                pw.print("    saveDelayMillis: ");
+                pw.println(mSaveDelayMillis);
+                pw.print("    resetInterval: ");
+                pw.println(mResetInterval);
+                pw.print("    maxUpdatesPerInterval: ");
+                pw.println(mMaxUpdatesPerInterval);
+                pw.print("    maxShortcutsPerActivity: ");
+                pw.println(mMaxShortcuts);
+                pw.println();
+
+                pw.println("  Stats:");
+                synchronized (mStatLock) {
+                    for (int i = 0; i < Stats.COUNT; i++) {
+                        dumpStatLS(pw, "    ", i);
+                    }
+                }
+
+                pw.println();
+                pw.print("  #Failures: ");
+                pw.println(mWtfCount);
+
+                if (mLastWtfStacktrace != null) {
+                    pw.print("  Last failure stack trace: ");
+                    pw.println(Log.getStackTraceString(mLastWtfStacktrace));
+                }
+
+                pw.println();
+                mShortcutBitmapSaver.dumpLocked(pw, "  ");
+
+                pw.println();
             }
 
-            pw.println();
-            mShortcutBitmapSaver.dumpLocked(pw, "  ");
-
             for (int i = 0; i < mUsers.size(); i++) {
-                pw.println();
-                mUsers.valueAt(i).dump(pw, "  ");
+                final ShortcutUser user = mUsers.valueAt(i);
+                if (filter.isUserMatch(user.getUserId())) {
+                    user.dump(pw, "  ", filter);
+                    pw.println();
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 2c388c4..55e6d28 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -28,6 +28,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
+import com.android.server.pm.ShortcutService.DumpFilter;
 import com.android.server.pm.ShortcutService.InvalidFileFormatException;
 
 import libcore.util.Objects;
@@ -531,44 +532,54 @@
                 + " S=" + restoredShortcuts[0]);
     }
 
-    public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
-        pw.print(prefix);
-        pw.print("User: ");
-        pw.print(mUserId);
-        pw.print("  Known locales: ");
-        pw.print(mKnownLocales);
-        pw.print("  Last app scan: [");
-        pw.print(mLastAppScanTime);
-        pw.print("] ");
-        pw.print(ShortcutService.formatTime(mLastAppScanTime));
-        pw.print("  Last app scan FP: ");
-        pw.print(mLastAppScanOsFingerprint);
-        pw.println();
+    public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) {
+        if (filter.shouldDumpDetails()) {
+            pw.print(prefix);
+            pw.print("User: ");
+            pw.print(mUserId);
+            pw.print("  Known locales: ");
+            pw.print(mKnownLocales);
+            pw.print("  Last app scan: [");
+            pw.print(mLastAppScanTime);
+            pw.print("] ");
+            pw.print(ShortcutService.formatTime(mLastAppScanTime));
+            pw.print("  Last app scan FP: ");
+            pw.print(mLastAppScanOsFingerprint);
+            pw.println();
 
-        prefix += prefix + "  ";
+            prefix += prefix + "  ";
 
-        pw.print(prefix);
-        pw.print("Cached launcher: ");
-        pw.print(mCachedLauncher);
-        pw.println();
+            pw.print(prefix);
+            pw.print("Cached launcher: ");
+            pw.print(mCachedLauncher);
+            pw.println();
 
-        pw.print(prefix);
-        pw.print("Last known launcher: ");
-        pw.print(mLastKnownLauncher);
-        pw.println();
+            pw.print(prefix);
+            pw.print("Last known launcher: ");
+            pw.print(mLastKnownLauncher);
+            pw.println();
+        }
 
         for (int i = 0; i < mLaunchers.size(); i++) {
-            mLaunchers.valueAt(i).dump(pw, prefix);
+            ShortcutLauncher launcher = mLaunchers.valueAt(i);
+            if (filter.isPackageMatch(launcher.getPackageName())) {
+                launcher.dump(pw, prefix, filter);
+            }
         }
 
         for (int i = 0; i < mPackages.size(); i++) {
-            mPackages.valueAt(i).dump(pw, prefix);
+            ShortcutPackage pkg = mPackages.valueAt(i);
+            if (filter.isPackageMatch(pkg.getPackageName())) {
+                pkg.dump(pw, prefix, filter);
+            }
         }
 
-        pw.println();
-        pw.print(prefix);
-        pw.println("Bitmap directories: ");
-        dumpDirectorySize(pw, prefix + "  ", mService.getUserBitmapFilePath(mUserId));
+        if (filter.shouldDumpDetails()) {
+            pw.println();
+            pw.print(prefix);
+            pw.println("Bitmap directories: ");
+            dumpDirectorySize(pw, prefix + "  ", mService.getUserBitmapFilePath(mUserId));
+        }
     }
 
     private void dumpDirectorySize(@NonNull PrintWriter pw,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f2d527b..1e5245c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1055,7 +1055,7 @@
 
     /** Called by PackageManagerService */
     public boolean exists(int userId) {
-        return getUserInfoNoChecks(userId) != null;
+        return mLocalService.exists(userId);
     }
 
     @Override
@@ -3502,8 +3502,8 @@
      * @param userId
      * @return whether the user has been initialized yet
      */
-    boolean isInitialized(int userId) {
-        return (getUserInfo(userId).flags & UserInfo.FLAG_INITIALIZED) != 0;
+    boolean isUserInitialized(int userId) {
+        return mLocalService.isUserInitialized(userId);
     }
 
     private class LocalService extends UserManagerInternal {
@@ -3715,6 +3715,16 @@
             }
             return state == UserState.STATE_RUNNING_UNLOCKED;
         }
+
+        @Override
+        public boolean isUserInitialized(int userId) {
+            return (getUserInfo(userId).flags & UserInfo.FLAG_INITIALIZED) != 0;
+        }
+
+        @Override
+        public boolean exists(int userId) {
+            return getUserInfoNoChecks(userId) != null;
+        }
     }
 
     /* Remove all the users except of the system one. */
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
new file mode 100644
index 0000000..161efd3
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -0,0 +1,1296 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.DownloadManager;
+import android.app.admin.DevicePolicyManager;
+import android.companion.CompanionDeviceManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManagerInternal.PackagesProvider;
+import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.print.PrintManager;
+import android.provider.CalendarContract;
+import android.provider.ContactsContract;
+import android.provider.MediaStore;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.TelephonyManager;
+import android.security.Credentials;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+import com.android.internal.util.XmlUtils;
+import com.android.server.LocalServices;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.PackageSetting;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static android.os.Process.FIRST_APPLICATION_UID;
+
+/**
+ * This class is the policy for granting runtime permissions to
+ * platform components and default handlers in the system such
+ * that the device is usable out-of-the-box. For example, the
+ * shell UID is a part of the system and the Phone app should
+ * have phone related permission by default.
+ * <p>
+ * NOTE: This class is at the wrong abstraction level. It is a part of the package manager
+ * service but knows about lots of higher level subsystems. The correct way to do this is
+ * to have an interface defined in the package manager but have the impl next to other
+ * policy stuff like PhoneWindowManager
+ */
+public final class DefaultPermissionGrantPolicy {
+    private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
+    private static final boolean DEBUG = false;
+
+    private static final int DEFAULT_FLAGS =
+            PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                    | PackageManager.MATCH_UNINSTALLED_PACKAGES;
+
+    private static final String AUDIO_MIME_TYPE = "audio/mpeg";
+
+    private static final String TAG_EXCEPTIONS = "exceptions";
+    private static final String TAG_EXCEPTION = "exception";
+    private static final String TAG_PERMISSION = "permission";
+    private static final String ATTR_PACKAGE = "package";
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_FIXED = "fixed";
+
+    private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>();
+    static {
+        PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE);
+        PHONE_PERMISSIONS.add(Manifest.permission.CALL_PHONE);
+        PHONE_PERMISSIONS.add(Manifest.permission.READ_CALL_LOG);
+        PHONE_PERMISSIONS.add(Manifest.permission.WRITE_CALL_LOG);
+        PHONE_PERMISSIONS.add(Manifest.permission.ADD_VOICEMAIL);
+        PHONE_PERMISSIONS.add(Manifest.permission.USE_SIP);
+        PHONE_PERMISSIONS.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
+    }
+
+    private static final Set<String> CONTACTS_PERMISSIONS = new ArraySet<>();
+    static {
+        CONTACTS_PERMISSIONS.add(Manifest.permission.READ_CONTACTS);
+        CONTACTS_PERMISSIONS.add(Manifest.permission.WRITE_CONTACTS);
+        CONTACTS_PERMISSIONS.add(Manifest.permission.GET_ACCOUNTS);
+    }
+
+    private static final Set<String> LOCATION_PERMISSIONS = new ArraySet<>();
+    static {
+        LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION);
+        LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
+    }
+
+    private static final Set<String> CALENDAR_PERMISSIONS = new ArraySet<>();
+    static {
+        CALENDAR_PERMISSIONS.add(Manifest.permission.READ_CALENDAR);
+        CALENDAR_PERMISSIONS.add(Manifest.permission.WRITE_CALENDAR);
+    }
+
+    private static final Set<String> SMS_PERMISSIONS = new ArraySet<>();
+    static {
+        SMS_PERMISSIONS.add(Manifest.permission.SEND_SMS);
+        SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_SMS);
+        SMS_PERMISSIONS.add(Manifest.permission.READ_SMS);
+        SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_WAP_PUSH);
+        SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_MMS);
+        SMS_PERMISSIONS.add(Manifest.permission.READ_CELL_BROADCASTS);
+    }
+
+    private static final Set<String> MICROPHONE_PERMISSIONS = new ArraySet<>();
+    static {
+        MICROPHONE_PERMISSIONS.add(Manifest.permission.RECORD_AUDIO);
+    }
+
+    private static final Set<String> CAMERA_PERMISSIONS = new ArraySet<>();
+    static {
+        CAMERA_PERMISSIONS.add(Manifest.permission.CAMERA);
+    }
+
+    private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>();
+    static {
+        SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS);
+    }
+
+    private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
+    static {
+        STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+        STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+    }
+
+    private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
+
+    private static final String ACTION_TRACK = "com.android.fitness.TRACK";
+
+    private final Handler mHandler;
+
+    private PackagesProvider mLocationPackagesProvider;
+    private PackagesProvider mVoiceInteractionPackagesProvider;
+    private PackagesProvider mSmsAppPackagesProvider;
+    private PackagesProvider mDialerAppPackagesProvider;
+    private PackagesProvider mSimCallManagerPackagesProvider;
+    private SyncAdapterPackagesProvider mSyncAdapterPackagesProvider;
+
+    private ArrayMap<String, List<DefaultPermissionGrant>> mGrantExceptions;
+    private final Context mContext;
+    private final Object mLock = new Object();
+    private final PackageManagerInternal mServiceInternal;
+    private final PermissionManagerService mPermissionManager;
+    private final DefaultPermissionGrantedCallback mPermissionGrantedCallback;
+    public interface DefaultPermissionGrantedCallback {
+        /** Callback when permissions have been granted */
+        public void onDefaultRuntimePermissionsGranted(int userId);
+    }
+
+    public DefaultPermissionGrantPolicy(Context context, Looper looper,
+            @Nullable DefaultPermissionGrantedCallback callback,
+            @NonNull PermissionManagerService permissionManager) {
+        mContext = context;
+        mHandler = new Handler(looper) {
+            @Override
+            public void handleMessage(Message msg) {
+                if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) {
+                    synchronized (mLock) {
+                        if (mGrantExceptions == null) {
+                            mGrantExceptions = readDefaultPermissionExceptionsLocked();
+                        }
+                    }
+                }
+            }
+        };
+        mPermissionGrantedCallback = callback;
+        mPermissionManager = permissionManager;
+        mServiceInternal = LocalServices.getService(PackageManagerInternal.class);
+    }
+
+    public void setLocationPackagesProvider(PackagesProvider provider) {
+        synchronized (mLock) {
+            mLocationPackagesProvider = provider;
+        }
+    }
+
+    public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
+        synchronized (mLock) {
+            mVoiceInteractionPackagesProvider = provider;
+        }
+    }
+
+    public void setSmsAppPackagesProvider(PackagesProvider provider) {
+        synchronized (mLock) {
+            mSmsAppPackagesProvider = provider;
+        }
+    }
+
+    public void setDialerAppPackagesProvider(PackagesProvider provider) {
+        synchronized (mLock) {
+            mDialerAppPackagesProvider = provider;
+        }
+    }
+
+    public void setSimCallManagerPackagesProvider(PackagesProvider provider) {
+        synchronized (mLock) {
+            mSimCallManagerPackagesProvider = provider;
+        }
+    }
+
+    public void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider) {
+        synchronized (mLock) {
+            mSyncAdapterPackagesProvider = provider;
+        }
+    }
+
+    public void grantDefaultPermissions(Collection<PackageParser.Package> packages, int userId) {
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
+            grantAllRuntimePermissions(packages, userId);
+        } else {
+            grantPermissionsToSysComponentsAndPrivApps(packages, userId);
+            grantDefaultSystemHandlerPermissions(userId);
+            grantDefaultPermissionExceptions(userId);
+        }
+    }
+
+    private void grantRuntimePermissionsForPackage(int userId, PackageParser.Package pkg) {
+        Set<String> permissions = new ArraySet<>();
+        for (String permission :  pkg.requestedPermissions) {
+            final BasePermission bp = mPermissionManager.getPermission(permission);
+            if (bp == null) {
+                continue;
+            }
+            if (bp.isRuntime()) {
+                permissions.add(permission);
+            }
+        }
+        if (!permissions.isEmpty()) {
+            grantRuntimePermissions(pkg, permissions, true, userId);
+        }
+    }
+
+    private void grantAllRuntimePermissions(
+            Collection<PackageParser.Package> packages, int userId) {
+        Log.i(TAG, "Granting all runtime permissions for user " + userId);
+        for (PackageParser.Package pkg : packages) {
+            grantRuntimePermissionsForPackage(userId, pkg);
+        }
+    }
+
+    public void scheduleReadDefaultPermissionExceptions() {
+        mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
+    }
+
+    private void grantPermissionsToSysComponentsAndPrivApps(
+            Collection<PackageParser.Package> packages, int userId) {
+        Log.i(TAG, "Granting permissions to platform components for user " + userId);
+        for (PackageParser.Package pkg : packages) {
+            if (!isSysComponentOrPersistentPlatformSignedPrivApp(pkg)
+                    || !doesPackageSupportRuntimePermissions(pkg)
+                    || pkg.requestedPermissions.isEmpty()) {
+                continue;
+            }
+            grantRuntimePermissionsForPackage(userId, pkg);
+        }
+    }
+
+    private void grantDefaultSystemHandlerPermissions(int userId) {
+        Log.i(TAG, "Granting permissions to default platform handlers for user " + userId);
+
+        final PackagesProvider locationPackagesProvider;
+        final PackagesProvider voiceInteractionPackagesProvider;
+        final PackagesProvider smsAppPackagesProvider;
+        final PackagesProvider dialerAppPackagesProvider;
+        final PackagesProvider simCallManagerPackagesProvider;
+        final SyncAdapterPackagesProvider syncAdapterPackagesProvider;
+
+        synchronized (mLock) {
+            locationPackagesProvider = mLocationPackagesProvider;
+            voiceInteractionPackagesProvider = mVoiceInteractionPackagesProvider;
+            smsAppPackagesProvider = mSmsAppPackagesProvider;
+            dialerAppPackagesProvider = mDialerAppPackagesProvider;
+            simCallManagerPackagesProvider = mSimCallManagerPackagesProvider;
+            syncAdapterPackagesProvider = mSyncAdapterPackagesProvider;
+        }
+
+        String[] voiceInteractPackageNames = (voiceInteractionPackagesProvider != null)
+                ? voiceInteractionPackagesProvider.getPackages(userId) : null;
+        String[] locationPackageNames = (locationPackagesProvider != null)
+                ? locationPackagesProvider.getPackages(userId) : null;
+        String[] smsAppPackageNames = (smsAppPackagesProvider != null)
+                ? smsAppPackagesProvider.getPackages(userId) : null;
+        String[] dialerAppPackageNames = (dialerAppPackagesProvider != null)
+                ? dialerAppPackagesProvider.getPackages(userId) : null;
+        String[] simCallManagerPackageNames = (simCallManagerPackagesProvider != null)
+                ? simCallManagerPackagesProvider.getPackages(userId) : null;
+        String[] contactsSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
+                syncAdapterPackagesProvider.getPackages(ContactsContract.AUTHORITY, userId) : null;
+        String[] calendarSyncAdapterPackages = (syncAdapterPackagesProvider != null) ?
+                syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null;
+
+        // Installer
+        final String installerPackageName = mServiceInternal.getKnownPackageName(
+                PackageManagerInternal.PACKAGE_INSTALLER, userId);
+        PackageParser.Package installerPackage = getSystemPackage(installerPackageName);
+        if (installerPackage != null
+                && doesPackageSupportRuntimePermissions(installerPackage)) {
+            grantRuntimePermissions(installerPackage, STORAGE_PERMISSIONS, true, userId);
+        }
+
+        // Verifier
+        final String verifierPackageName = mServiceInternal.getKnownPackageName(
+                PackageManagerInternal.PACKAGE_VERIFIER, userId);
+        PackageParser.Package verifierPackage = getSystemPackage(verifierPackageName);
+        if (verifierPackage != null
+                && doesPackageSupportRuntimePermissions(verifierPackage)) {
+            grantRuntimePermissions(verifierPackage, STORAGE_PERMISSIONS, true, userId);
+            grantRuntimePermissions(verifierPackage, PHONE_PERMISSIONS, false, userId);
+            grantRuntimePermissions(verifierPackage, SMS_PERMISSIONS, false, userId);
+        }
+
+        // SetupWizard
+        final String setupWizardPackageName = mServiceInternal.getKnownPackageName(
+                PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId);
+        PackageParser.Package setupPackage = getSystemPackage(setupWizardPackageName);
+        if (setupPackage != null
+                && doesPackageSupportRuntimePermissions(setupPackage)) {
+            grantRuntimePermissions(setupPackage, PHONE_PERMISSIONS, userId);
+            grantRuntimePermissions(setupPackage, CONTACTS_PERMISSIONS, userId);
+            grantRuntimePermissions(setupPackage, LOCATION_PERMISSIONS, userId);
+            grantRuntimePermissions(setupPackage, CAMERA_PERMISSIONS, userId);
+        }
+
+        // Camera
+        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+        PackageParser.Package cameraPackage = getDefaultSystemHandlerActivityPackage(
+                cameraIntent, userId);
+        if (cameraPackage != null
+                && doesPackageSupportRuntimePermissions(cameraPackage)) {
+            grantRuntimePermissions(cameraPackage, CAMERA_PERMISSIONS, userId);
+            grantRuntimePermissions(cameraPackage, MICROPHONE_PERMISSIONS, userId);
+            grantRuntimePermissions(cameraPackage, STORAGE_PERMISSIONS, userId);
+        }
+
+        // Media provider
+        PackageParser.Package mediaStorePackage = getDefaultProviderAuthorityPackage(
+                MediaStore.AUTHORITY, userId);
+        if (mediaStorePackage != null) {
+            grantRuntimePermissions(mediaStorePackage, STORAGE_PERMISSIONS, true, userId);
+        }
+
+        // Downloads provider
+        PackageParser.Package downloadsPackage = getDefaultProviderAuthorityPackage(
+                "downloads", userId);
+        if (downloadsPackage != null) {
+            grantRuntimePermissions(downloadsPackage, STORAGE_PERMISSIONS, true, userId);
+        }
+
+        // Downloads UI
+        Intent downloadsUiIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
+        PackageParser.Package downloadsUiPackage = getDefaultSystemHandlerActivityPackage(
+                downloadsUiIntent, userId);
+        if (downloadsUiPackage != null
+                && doesPackageSupportRuntimePermissions(downloadsUiPackage)) {
+            grantRuntimePermissions(downloadsUiPackage, STORAGE_PERMISSIONS, true, userId);
+        }
+
+        // Storage provider
+        PackageParser.Package storagePackage = getDefaultProviderAuthorityPackage(
+                "com.android.externalstorage.documents", userId);
+        if (storagePackage != null) {
+            grantRuntimePermissions(storagePackage, STORAGE_PERMISSIONS, true, userId);
+        }
+
+        // CertInstaller
+        Intent certInstallerIntent = new Intent(Credentials.INSTALL_ACTION);
+        PackageParser.Package certInstallerPackage = getDefaultSystemHandlerActivityPackage(
+                certInstallerIntent, userId);
+        if (certInstallerPackage != null
+                && doesPackageSupportRuntimePermissions(certInstallerPackage)) {
+            grantRuntimePermissions(certInstallerPackage, STORAGE_PERMISSIONS, true, userId);
+        }
+
+        // Dialer
+        if (dialerAppPackageNames == null) {
+            Intent dialerIntent = new Intent(Intent.ACTION_DIAL);
+            PackageParser.Package dialerPackage = getDefaultSystemHandlerActivityPackage(
+                    dialerIntent, userId);
+            if (dialerPackage != null) {
+                grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
+            }
+        } else {
+            for (String dialerAppPackageName : dialerAppPackageNames) {
+                PackageParser.Package dialerPackage = getSystemPackage(dialerAppPackageName);
+                if (dialerPackage != null) {
+                    grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
+                }
+            }
+        }
+
+        // Sim call manager
+        if (simCallManagerPackageNames != null) {
+            for (String simCallManagerPackageName : simCallManagerPackageNames) {
+                PackageParser.Package simCallManagerPackage =
+                        getSystemPackage(simCallManagerPackageName);
+                if (simCallManagerPackage != null) {
+                    grantDefaultPermissionsToDefaultSimCallManager(simCallManagerPackage,
+                            userId);
+                }
+            }
+        }
+
+        // SMS
+        if (smsAppPackageNames == null) {
+            Intent smsIntent = new Intent(Intent.ACTION_MAIN);
+            smsIntent.addCategory(Intent.CATEGORY_APP_MESSAGING);
+            PackageParser.Package smsPackage = getDefaultSystemHandlerActivityPackage(
+                    smsIntent, userId);
+            if (smsPackage != null) {
+               grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
+            }
+        } else {
+            for (String smsPackageName : smsAppPackageNames) {
+                PackageParser.Package smsPackage = getSystemPackage(smsPackageName);
+                if (smsPackage != null) {
+                    grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
+                }
+            }
+        }
+
+        // Cell Broadcast Receiver
+        Intent cbrIntent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
+        PackageParser.Package cbrPackage =
+                getDefaultSystemHandlerActivityPackage(cbrIntent, userId);
+        if (cbrPackage != null && doesPackageSupportRuntimePermissions(cbrPackage)) {
+            grantRuntimePermissions(cbrPackage, SMS_PERMISSIONS, userId);
+        }
+
+        // Carrier Provisioning Service
+        Intent carrierProvIntent = new Intent(Intents.SMS_CARRIER_PROVISION_ACTION);
+        PackageParser.Package carrierProvPackage =
+                getDefaultSystemHandlerServicePackage(carrierProvIntent, userId);
+        if (carrierProvPackage != null
+                && doesPackageSupportRuntimePermissions(carrierProvPackage)) {
+            grantRuntimePermissions(carrierProvPackage, SMS_PERMISSIONS, false, userId);
+        }
+
+        // Calendar
+        Intent calendarIntent = new Intent(Intent.ACTION_MAIN);
+        calendarIntent.addCategory(Intent.CATEGORY_APP_CALENDAR);
+        PackageParser.Package calendarPackage = getDefaultSystemHandlerActivityPackage(
+                calendarIntent, userId);
+        if (calendarPackage != null
+                && doesPackageSupportRuntimePermissions(calendarPackage)) {
+            grantRuntimePermissions(calendarPackage, CALENDAR_PERMISSIONS, userId);
+            grantRuntimePermissions(calendarPackage, CONTACTS_PERMISSIONS, userId);
+        }
+
+        // Calendar provider
+        PackageParser.Package calendarProviderPackage = getDefaultProviderAuthorityPackage(
+                CalendarContract.AUTHORITY, userId);
+        if (calendarProviderPackage != null) {
+            grantRuntimePermissions(calendarProviderPackage, CONTACTS_PERMISSIONS, userId);
+            grantRuntimePermissions(calendarProviderPackage, CALENDAR_PERMISSIONS,
+                    true, userId);
+            grantRuntimePermissions(calendarProviderPackage, STORAGE_PERMISSIONS, userId);
+        }
+
+        // Calendar provider sync adapters
+        List<PackageParser.Package> calendarSyncAdapters = getHeadlessSyncAdapterPackages(
+                calendarSyncAdapterPackages, userId);
+        final int calendarSyncAdapterCount = calendarSyncAdapters.size();
+        for (int i = 0; i < calendarSyncAdapterCount; i++) {
+            PackageParser.Package calendarSyncAdapter = calendarSyncAdapters.get(i);
+            if (doesPackageSupportRuntimePermissions(calendarSyncAdapter)) {
+                grantRuntimePermissions(calendarSyncAdapter, CALENDAR_PERMISSIONS, userId);
+            }
+        }
+
+        // Contacts
+        Intent contactsIntent = new Intent(Intent.ACTION_MAIN);
+        contactsIntent.addCategory(Intent.CATEGORY_APP_CONTACTS);
+        PackageParser.Package contactsPackage = getDefaultSystemHandlerActivityPackage(
+                contactsIntent, userId);
+        if (contactsPackage != null
+                && doesPackageSupportRuntimePermissions(contactsPackage)) {
+            grantRuntimePermissions(contactsPackage, CONTACTS_PERMISSIONS, userId);
+            grantRuntimePermissions(contactsPackage, PHONE_PERMISSIONS, userId);
+        }
+
+        // Contacts provider sync adapters
+        List<PackageParser.Package> contactsSyncAdapters = getHeadlessSyncAdapterPackages(
+                contactsSyncAdapterPackages, userId);
+        final int contactsSyncAdapterCount = contactsSyncAdapters.size();
+        for (int i = 0; i < contactsSyncAdapterCount; i++) {
+            PackageParser.Package contactsSyncAdapter = contactsSyncAdapters.get(i);
+            if (doesPackageSupportRuntimePermissions(contactsSyncAdapter)) {
+                grantRuntimePermissions(contactsSyncAdapter, CONTACTS_PERMISSIONS, userId);
+            }
+        }
+
+        // Contacts provider
+        PackageParser.Package contactsProviderPackage = getDefaultProviderAuthorityPackage(
+                ContactsContract.AUTHORITY, userId);
+        if (contactsProviderPackage != null) {
+            grantRuntimePermissions(contactsProviderPackage, CONTACTS_PERMISSIONS,
+                    true, userId);
+            grantRuntimePermissions(contactsProviderPackage, PHONE_PERMISSIONS,
+                    true, userId);
+            grantRuntimePermissions(contactsProviderPackage, STORAGE_PERMISSIONS, userId);
+        }
+
+        // Device provisioning
+        Intent deviceProvisionIntent = new Intent(
+                DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE);
+        PackageParser.Package deviceProvisionPackage =
+                getDefaultSystemHandlerActivityPackage(deviceProvisionIntent, userId);
+        if (deviceProvisionPackage != null
+                && doesPackageSupportRuntimePermissions(deviceProvisionPackage)) {
+            grantRuntimePermissions(deviceProvisionPackage, CONTACTS_PERMISSIONS, userId);
+        }
+
+        // Maps
+        Intent mapsIntent = new Intent(Intent.ACTION_MAIN);
+        mapsIntent.addCategory(Intent.CATEGORY_APP_MAPS);
+        PackageParser.Package mapsPackage = getDefaultSystemHandlerActivityPackage(
+                mapsIntent, userId);
+        if (mapsPackage != null
+                && doesPackageSupportRuntimePermissions(mapsPackage)) {
+            grantRuntimePermissions(mapsPackage, LOCATION_PERMISSIONS, userId);
+        }
+
+        // Gallery
+        Intent galleryIntent = new Intent(Intent.ACTION_MAIN);
+        galleryIntent.addCategory(Intent.CATEGORY_APP_GALLERY);
+        PackageParser.Package galleryPackage = getDefaultSystemHandlerActivityPackage(
+                galleryIntent, userId);
+        if (galleryPackage != null
+                && doesPackageSupportRuntimePermissions(galleryPackage)) {
+            grantRuntimePermissions(galleryPackage, STORAGE_PERMISSIONS, userId);
+        }
+
+        // Email
+        Intent emailIntent = new Intent(Intent.ACTION_MAIN);
+        emailIntent.addCategory(Intent.CATEGORY_APP_EMAIL);
+        PackageParser.Package emailPackage = getDefaultSystemHandlerActivityPackage(
+                emailIntent, userId);
+        if (emailPackage != null
+                && doesPackageSupportRuntimePermissions(emailPackage)) {
+            grantRuntimePermissions(emailPackage, CONTACTS_PERMISSIONS, userId);
+            grantRuntimePermissions(emailPackage, CALENDAR_PERMISSIONS, userId);
+        }
+
+        // Browser
+        PackageParser.Package browserPackage = null;
+        String defaultBrowserPackage = mServiceInternal.getKnownPackageName(
+                PackageManagerInternal.PACKAGE_BROWSER, userId);
+        if (defaultBrowserPackage != null) {
+            browserPackage = getPackage(defaultBrowserPackage);
+        }
+        if (browserPackage == null) {
+            Intent browserIntent = new Intent(Intent.ACTION_MAIN);
+            browserIntent.addCategory(Intent.CATEGORY_APP_BROWSER);
+            browserPackage = getDefaultSystemHandlerActivityPackage(
+                    browserIntent, userId);
+        }
+        if (browserPackage != null
+                && doesPackageSupportRuntimePermissions(browserPackage)) {
+            grantRuntimePermissions(browserPackage, LOCATION_PERMISSIONS, userId);
+        }
+
+        // Voice interaction
+        if (voiceInteractPackageNames != null) {
+            for (String voiceInteractPackageName : voiceInteractPackageNames) {
+                PackageParser.Package voiceInteractPackage = getSystemPackage(
+                        voiceInteractPackageName);
+                if (voiceInteractPackage != null
+                        && doesPackageSupportRuntimePermissions(voiceInteractPackage)) {
+                    grantRuntimePermissions(voiceInteractPackage,
+                            CONTACTS_PERMISSIONS, userId);
+                    grantRuntimePermissions(voiceInteractPackage,
+                            CALENDAR_PERMISSIONS, userId);
+                    grantRuntimePermissions(voiceInteractPackage,
+                            MICROPHONE_PERMISSIONS, userId);
+                    grantRuntimePermissions(voiceInteractPackage,
+                            PHONE_PERMISSIONS, userId);
+                    grantRuntimePermissions(voiceInteractPackage,
+                            SMS_PERMISSIONS, userId);
+                    grantRuntimePermissions(voiceInteractPackage,
+                            LOCATION_PERMISSIONS, userId);
+                }
+            }
+        }
+
+        if (ActivityManager.isLowRamDeviceStatic()) {
+            // Allow voice search on low-ram devices
+            Intent globalSearchIntent = new Intent("android.search.action.GLOBAL_SEARCH");
+            PackageParser.Package globalSearchPickerPackage =
+                getDefaultSystemHandlerActivityPackage(globalSearchIntent, userId);
+
+            if (globalSearchPickerPackage != null
+                    && doesPackageSupportRuntimePermissions(globalSearchPickerPackage)) {
+                grantRuntimePermissions(globalSearchPickerPackage,
+                    MICROPHONE_PERMISSIONS, true, userId);
+                grantRuntimePermissions(globalSearchPickerPackage,
+                    LOCATION_PERMISSIONS, true, userId);
+            }
+        }
+
+        // Voice recognition
+        Intent voiceRecoIntent = new Intent("android.speech.RecognitionService");
+        voiceRecoIntent.addCategory(Intent.CATEGORY_DEFAULT);
+        PackageParser.Package voiceRecoPackage = getDefaultSystemHandlerServicePackage(
+                voiceRecoIntent, userId);
+        if (voiceRecoPackage != null
+                && doesPackageSupportRuntimePermissions(voiceRecoPackage)) {
+            grantRuntimePermissions(voiceRecoPackage, MICROPHONE_PERMISSIONS, userId);
+        }
+
+        // Location
+        if (locationPackageNames != null) {
+            for (String packageName : locationPackageNames) {
+                PackageParser.Package locationPackage = getSystemPackage(packageName);
+                if (locationPackage != null
+                        && doesPackageSupportRuntimePermissions(locationPackage)) {
+                    grantRuntimePermissions(locationPackage, CONTACTS_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, CALENDAR_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, MICROPHONE_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, PHONE_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, SMS_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, LOCATION_PERMISSIONS,
+                            true, userId);
+                    grantRuntimePermissions(locationPackage, CAMERA_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, SENSORS_PERMISSIONS, userId);
+                    grantRuntimePermissions(locationPackage, STORAGE_PERMISSIONS, userId);
+                }
+            }
+        }
+
+        // Music
+        Intent musicIntent = new Intent(Intent.ACTION_VIEW);
+        musicIntent.addCategory(Intent.CATEGORY_DEFAULT);
+        musicIntent.setDataAndType(Uri.fromFile(new File("foo.mp3")),
+                AUDIO_MIME_TYPE);
+        PackageParser.Package musicPackage = getDefaultSystemHandlerActivityPackage(
+                musicIntent, userId);
+        if (musicPackage != null
+                && doesPackageSupportRuntimePermissions(musicPackage)) {
+            grantRuntimePermissions(musicPackage, STORAGE_PERMISSIONS, userId);
+        }
+
+        // Home
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+        homeIntent.addCategory(Intent.CATEGORY_HOME);
+        homeIntent.addCategory(Intent.CATEGORY_LAUNCHER_APP);
+        PackageParser.Package homePackage = getDefaultSystemHandlerActivityPackage(
+                homeIntent, userId);
+        if (homePackage != null
+                && doesPackageSupportRuntimePermissions(homePackage)) {
+            grantRuntimePermissions(homePackage, LOCATION_PERMISSIONS, false, userId);
+        }
+
+        // Watches
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
+            // Home application on watches
+            Intent wearHomeIntent = new Intent(Intent.ACTION_MAIN);
+            wearHomeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
+
+            PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackage(
+                    wearHomeIntent, userId);
+
+            if (wearHomePackage != null
+                    && doesPackageSupportRuntimePermissions(wearHomePackage)) {
+                grantRuntimePermissions(wearHomePackage, CONTACTS_PERMISSIONS, false,
+                        userId);
+                grantRuntimePermissions(wearHomePackage, PHONE_PERMISSIONS, true, userId);
+                grantRuntimePermissions(wearHomePackage, MICROPHONE_PERMISSIONS, false,
+                        userId);
+                grantRuntimePermissions(wearHomePackage, LOCATION_PERMISSIONS, false,
+                        userId);
+            }
+
+            // Fitness tracking on watches
+            Intent trackIntent = new Intent(ACTION_TRACK);
+            PackageParser.Package trackPackage = getDefaultSystemHandlerActivityPackage(
+                    trackIntent, userId);
+            if (trackPackage != null
+                    && doesPackageSupportRuntimePermissions(trackPackage)) {
+                grantRuntimePermissions(trackPackage, SENSORS_PERMISSIONS, false, userId);
+                grantRuntimePermissions(trackPackage, LOCATION_PERMISSIONS, false, userId);
+            }
+        }
+
+        // Print Spooler
+        PackageParser.Package printSpoolerPackage = getSystemPackage(
+                PrintManager.PRINT_SPOOLER_PACKAGE_NAME);
+        if (printSpoolerPackage != null
+                && doesPackageSupportRuntimePermissions(printSpoolerPackage)) {
+            grantRuntimePermissions(printSpoolerPackage, LOCATION_PERMISSIONS, true, userId);
+        }
+
+        // EmergencyInfo
+        Intent emergencyInfoIntent = new Intent(TelephonyManager.ACTION_EMERGENCY_ASSISTANCE);
+        PackageParser.Package emergencyInfoPckg = getDefaultSystemHandlerActivityPackage(
+                emergencyInfoIntent, userId);
+        if (emergencyInfoPckg != null
+                && doesPackageSupportRuntimePermissions(emergencyInfoPckg)) {
+            grantRuntimePermissions(emergencyInfoPckg, CONTACTS_PERMISSIONS, true, userId);
+            grantRuntimePermissions(emergencyInfoPckg, PHONE_PERMISSIONS, true, userId);
+        }
+
+        // NFC Tag viewer
+        Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW);
+        nfcTagIntent.setType("vnd.android.cursor.item/ndef_msg");
+        PackageParser.Package nfcTagPkg = getDefaultSystemHandlerActivityPackage(
+                nfcTagIntent, userId);
+        if (nfcTagPkg != null
+                && doesPackageSupportRuntimePermissions(nfcTagPkg)) {
+            grantRuntimePermissions(nfcTagPkg, CONTACTS_PERMISSIONS, false, userId);
+            grantRuntimePermissions(nfcTagPkg, PHONE_PERMISSIONS, false, userId);
+        }
+
+        // Storage Manager
+        Intent storageManagerIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
+        PackageParser.Package storageManagerPckg = getDefaultSystemHandlerActivityPackage(
+                storageManagerIntent, userId);
+        if (storageManagerPckg != null
+                && doesPackageSupportRuntimePermissions(storageManagerPckg)) {
+            grantRuntimePermissions(storageManagerPckg, STORAGE_PERMISSIONS, true, userId);
+        }
+
+        // Companion devices
+        PackageParser.Package companionDeviceDiscoveryPackage = getSystemPackage(
+                CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME);
+        if (companionDeviceDiscoveryPackage != null
+                && doesPackageSupportRuntimePermissions(companionDeviceDiscoveryPackage)) {
+            grantRuntimePermissions(companionDeviceDiscoveryPackage,
+                    LOCATION_PERMISSIONS, true, userId);
+        }
+
+        // Ringtone Picker
+        Intent ringtonePickerIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+        PackageParser.Package ringtonePickerPackage =
+                getDefaultSystemHandlerActivityPackage(ringtonePickerIntent, userId);
+        if (ringtonePickerPackage != null
+                && doesPackageSupportRuntimePermissions(ringtonePickerPackage)) {
+            grantRuntimePermissions(ringtonePickerPackage,
+                    STORAGE_PERMISSIONS, true, userId);
+        }
+
+        if (mPermissionGrantedCallback != null) {
+            mPermissionGrantedCallback.onDefaultRuntimePermissionsGranted(userId);
+        }
+    }
+
+    private void grantDefaultPermissionsToDefaultSystemDialerApp(
+            PackageParser.Package dialerPackage, int userId) {
+        if (doesPackageSupportRuntimePermissions(dialerPackage)) {
+            boolean isPhonePermFixed =
+                    mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
+            grantRuntimePermissions(
+                    dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
+            grantRuntimePermissions(dialerPackage, CONTACTS_PERMISSIONS, userId);
+            grantRuntimePermissions(dialerPackage, SMS_PERMISSIONS, userId);
+            grantRuntimePermissions(dialerPackage, MICROPHONE_PERMISSIONS, userId);
+            grantRuntimePermissions(dialerPackage, CAMERA_PERMISSIONS, userId);
+        }
+    }
+
+    private void grantDefaultPermissionsToDefaultSystemSmsApp(
+            PackageParser.Package smsPackage, int userId) {
+        if (doesPackageSupportRuntimePermissions(smsPackage)) {
+            grantRuntimePermissions(smsPackage, PHONE_PERMISSIONS, userId);
+            grantRuntimePermissions(smsPackage, CONTACTS_PERMISSIONS, userId);
+            grantRuntimePermissions(smsPackage, SMS_PERMISSIONS, userId);
+            grantRuntimePermissions(smsPackage, STORAGE_PERMISSIONS, userId);
+            grantRuntimePermissions(smsPackage, MICROPHONE_PERMISSIONS, userId);
+            grantRuntimePermissions(smsPackage, CAMERA_PERMISSIONS, userId);
+        }
+    }
+
+    public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) {
+        Log.i(TAG, "Granting permissions to default sms app for user:" + userId);
+        if (packageName == null) {
+            return;
+        }
+        PackageParser.Package smsPackage = getPackage(packageName);
+        if (smsPackage != null && doesPackageSupportRuntimePermissions(smsPackage)) {
+            grantRuntimePermissions(smsPackage, PHONE_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(smsPackage, CONTACTS_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(smsPackage, SMS_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(smsPackage, STORAGE_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(smsPackage, MICROPHONE_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(smsPackage, CAMERA_PERMISSIONS, false, true, userId);
+        }
+    }
+
+    public void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId) {
+        Log.i(TAG, "Granting permissions to default dialer app for user:" + userId);
+        if (packageName == null) {
+            return;
+        }
+        PackageParser.Package dialerPackage = getPackage(packageName);
+        if (dialerPackage != null
+                && doesPackageSupportRuntimePermissions(dialerPackage)) {
+            grantRuntimePermissions(dialerPackage, PHONE_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(dialerPackage, CONTACTS_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(dialerPackage, SMS_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(dialerPackage, MICROPHONE_PERMISSIONS, false, true, userId);
+            grantRuntimePermissions(dialerPackage, CAMERA_PERMISSIONS, false, true, userId);
+        }
+    }
+
+    private void grantDefaultPermissionsToDefaultSimCallManager(
+            PackageParser.Package simCallManagerPackage, int userId) {
+        Log.i(TAG, "Granting permissions to sim call manager for user:" + userId);
+        if (doesPackageSupportRuntimePermissions(simCallManagerPackage)) {
+            grantRuntimePermissions(simCallManagerPackage, PHONE_PERMISSIONS, userId);
+            grantRuntimePermissions(simCallManagerPackage, MICROPHONE_PERMISSIONS, userId);
+        }
+    }
+
+    public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
+        if (packageName == null) {
+            return;
+        }
+        PackageParser.Package simCallManagerPackage = getPackage(packageName);
+        if (simCallManagerPackage != null) {
+            grantDefaultPermissionsToDefaultSimCallManager(simCallManagerPackage, userId);
+        }
+    }
+
+    public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) {
+        Log.i(TAG, "Granting permissions to enabled carrier apps for user:" + userId);
+        if (packageNames == null) {
+            return;
+        }
+        for (String packageName : packageNames) {
+            PackageParser.Package carrierPackage = getSystemPackage(packageName);
+            if (carrierPackage != null
+                    && doesPackageSupportRuntimePermissions(carrierPackage)) {
+                grantRuntimePermissions(carrierPackage, PHONE_PERMISSIONS, userId);
+                grantRuntimePermissions(carrierPackage, LOCATION_PERMISSIONS, userId);
+                grantRuntimePermissions(carrierPackage, SMS_PERMISSIONS, userId);
+            }
+        }
+    }
+
+    public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) {
+        Log.i(TAG, "Granting permissions to enabled ImsServices for user:" + userId);
+        if (packageNames == null) {
+            return;
+        }
+        for (String packageName : packageNames) {
+            PackageParser.Package imsServicePackage = getSystemPackage(packageName);
+            if (imsServicePackage != null
+                    && doesPackageSupportRuntimePermissions(imsServicePackage)) {
+                grantRuntimePermissions(imsServicePackage, PHONE_PERMISSIONS, userId);
+                grantRuntimePermissions(imsServicePackage, MICROPHONE_PERMISSIONS, userId);
+                grantRuntimePermissions(imsServicePackage, LOCATION_PERMISSIONS, userId);
+                grantRuntimePermissions(imsServicePackage, CAMERA_PERMISSIONS, userId);
+            }
+        }
+    }
+
+    public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) {
+        Log.i(TAG, "Granting permissions to default browser for user:" + userId);
+        if (packageName == null) {
+            return;
+        }
+        PackageParser.Package browserPackage = getSystemPackage(packageName);
+        if (browserPackage != null
+                && doesPackageSupportRuntimePermissions(browserPackage)) {
+            grantRuntimePermissions(browserPackage, LOCATION_PERMISSIONS, false, false, userId);
+        }
+    }
+
+    private PackageParser.Package getDefaultSystemHandlerActivityPackage(
+            Intent intent, int userId) {
+        ResolveInfo handler = mServiceInternal.resolveIntent(intent,
+                intent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS, userId, false);
+        if (handler == null || handler.activityInfo == null) {
+            return null;
+        }
+        if (mServiceInternal.isResolveActivityComponent(handler.activityInfo)) {
+            return null;
+        }
+        return getSystemPackage(handler.activityInfo.packageName);
+    }
+
+    private PackageParser.Package getDefaultSystemHandlerServicePackage(
+            Intent intent, int userId) {
+        List<ResolveInfo> handlers = mServiceInternal.queryIntentServices(
+                intent, DEFAULT_FLAGS, Binder.getCallingUid(), userId);
+        if (handlers == null) {
+            return null;
+        }
+        final int handlerCount = handlers.size();
+        for (int i = 0; i < handlerCount; i++) {
+            ResolveInfo handler = handlers.get(i);
+            PackageParser.Package handlerPackage = getSystemPackage(
+                    handler.serviceInfo.packageName);
+            if (handlerPackage != null) {
+                return handlerPackage;
+            }
+        }
+        return null;
+    }
+
+    private List<PackageParser.Package> getHeadlessSyncAdapterPackages(
+            String[] syncAdapterPackageNames, int userId) {
+        List<PackageParser.Package> syncAdapterPackages = new ArrayList<>();
+
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+        homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+        for (String syncAdapterPackageName : syncAdapterPackageNames) {
+            homeIntent.setPackage(syncAdapterPackageName);
+
+            ResolveInfo homeActivity = mServiceInternal.resolveIntent(homeIntent,
+                    homeIntent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS,
+                    userId, false);
+            if (homeActivity != null) {
+                continue;
+            }
+
+            PackageParser.Package syncAdapterPackage = getSystemPackage(syncAdapterPackageName);
+            if (syncAdapterPackage != null) {
+                syncAdapterPackages.add(syncAdapterPackage);
+            }
+        }
+
+        return syncAdapterPackages;
+    }
+
+    private PackageParser.Package getDefaultProviderAuthorityPackage(
+            String authority, int userId) {
+        ProviderInfo provider =
+                mServiceInternal.resolveContentProvider(authority, DEFAULT_FLAGS, userId);
+        if (provider != null) {
+            return getSystemPackage(provider.packageName);
+        }
+        return null;
+    }
+
+    private PackageParser.Package getPackage(String packageName) {
+        return mServiceInternal.getPackage(packageName);
+    }
+
+    private PackageParser.Package getSystemPackage(String packageName) {
+        PackageParser.Package pkg = getPackage(packageName);
+        if (pkg != null && pkg.isSystemApp()) {
+            return !isSysComponentOrPersistentPlatformSignedPrivApp(pkg) ? pkg : null;
+        }
+        return null;
+    }
+
+    private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
+            int userId) {
+        grantRuntimePermissions(pkg, permissions, false, false, userId);
+    }
+
+    private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
+            boolean systemFixed, int userId) {
+        grantRuntimePermissions(pkg, permissions, systemFixed, false, userId);
+    }
+
+    private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions,
+            boolean systemFixed, boolean isDefaultPhoneOrSms, int userId) {
+        if (pkg.requestedPermissions.isEmpty()) {
+            return;
+        }
+
+        List<String> requestedPermissions = pkg.requestedPermissions;
+        Set<String> grantablePermissions = null;
+
+        // If this is the default Phone or SMS app we grant permissions regardless
+        // whether the version on the system image declares the permission as used since
+        // selecting the app as the default Phone or SMS the user makes a deliberate
+        // choice to grant this app the permissions needed to function. For all other
+        // apps, (default grants on first boot and user creation) we don't grant default
+        // permissions if the version on the system image does not declare them.
+        if (!isDefaultPhoneOrSms && pkg.isUpdatedSystemApp()) {
+            final PackageParser.Package disabledPkg =
+                    mServiceInternal.getDisabledPackage(pkg.packageName);
+            if (disabledPkg != null) {
+                if (disabledPkg.requestedPermissions.isEmpty()) {
+                    return;
+                }
+                if (!requestedPermissions.equals(disabledPkg.requestedPermissions)) {
+                    grantablePermissions = new ArraySet<>(requestedPermissions);
+                    requestedPermissions = disabledPkg.requestedPermissions;
+                }
+            }
+        }
+
+        final int grantablePermissionCount = requestedPermissions.size();
+        for (int i = 0; i < grantablePermissionCount; i++) {
+            String permission = requestedPermissions.get(i);
+
+            // If there is a disabled system app it may request a permission the updated
+            // version ot the data partition doesn't, In this case skip the permission.
+            if (grantablePermissions != null && !grantablePermissions.contains(permission)) {
+                continue;
+            }
+
+            if (permissions.contains(permission)) {
+                final int flags = mServiceInternal.getPermissionFlagsTEMP(
+                        permission, pkg.packageName, userId);
+
+                // If any flags are set to the permission, then it is either set in
+                // its current state by the system or device/profile owner or the user.
+                // In all these cases we do not want to clobber the current state.
+                // Unless the caller wants to override user choices. The override is
+                // to make sure we can grant the needed permission to the default
+                // sms and phone apps after the user chooses this in the UI.
+                if (flags == 0 || isDefaultPhoneOrSms) {
+                    // Never clobber policy or system.
+                    final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
+                            | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+                    if ((flags & fixedFlags) != 0) {
+                        continue;
+                    }
+
+                    mServiceInternal.grantRuntimePermission(
+                            pkg.packageName, permission, userId, false);
+                    if (DEBUG) {
+                        Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")
+                                + permission + " to default handler " + pkg.packageName);
+                    }
+
+                    int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+                    if (systemFixed) {
+                        newFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+                    }
+
+                    mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
+                            newFlags, newFlags, userId);
+                }
+
+                // If a component gets a permission for being the default handler A
+                // and also default handler B, we grant the weaker grant form.
+                if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0
+                        && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
+                        && !systemFixed) {
+                    if (DEBUG) {
+                        Log.i(TAG, "Granted not fixed " + permission + " to default handler "
+                                + pkg.packageName);
+                    }
+                    mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
+                            PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, userId);
+                }
+            }
+        }
+    }
+
+    private boolean isSysComponentOrPersistentPlatformSignedPrivApp(PackageParser.Package pkg) {
+        if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
+            return true;
+        }
+        if (!pkg.isPrivilegedApp()) {
+            return false;
+        }
+        final PackageParser.Package disabledPkg =
+                mServiceInternal.getDisabledPackage(pkg.packageName);
+        if (disabledPkg != null) {
+            if ((disabledPkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
+                return false;
+            }
+        } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
+            return false;
+        }
+        final String systemPackageName = mServiceInternal.getKnownPackageName(
+                PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
+        final PackageParser.Package systemPackage = getPackage(systemPackageName);
+        return PackageManagerService.compareSignatures(systemPackage.mSignatures,
+                pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
+    }
+
+    private void grantDefaultPermissionExceptions(int userId) {
+        mHandler.removeMessages(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
+
+        synchronized (mLock) {
+            // mGrantExceptions is null only before the first read and then
+            // it serves as a cache of the default grants that should be
+            // performed for every user. If there is an entry then the app
+            // is on the system image and supports runtime permissions.
+            if (mGrantExceptions == null) {
+                mGrantExceptions = readDefaultPermissionExceptionsLocked();
+            }
+        }
+
+        Set<String> permissions = null;
+        final int exceptionCount = mGrantExceptions.size();
+        for (int i = 0; i < exceptionCount; i++) {
+            String packageName = mGrantExceptions.keyAt(i);
+            PackageParser.Package pkg = getSystemPackage(packageName);
+            List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i);
+            final int permissionGrantCount = permissionGrants.size();
+            for (int j = 0; j < permissionGrantCount; j++) {
+                DefaultPermissionGrant permissionGrant = permissionGrants.get(j);
+                if (permissions == null) {
+                    permissions = new ArraySet<>();
+                } else {
+                    permissions.clear();
+                }
+                permissions.add(permissionGrant.name);
+                grantRuntimePermissions(pkg, permissions,
+                        permissionGrant.fixed, userId);
+            }
+        }
+    }
+
+    private File[] getDefaultPermissionFiles() {
+        ArrayList<File> ret = new ArrayList<File>();
+        File dir = new File(Environment.getRootDirectory(), "etc/default-permissions");
+        if (dir.isDirectory() && dir.canRead()) {
+            Collections.addAll(ret, dir.listFiles());
+        }
+        dir = new File(Environment.getVendorDirectory(), "etc/default-permissions");
+        if (dir.isDirectory() && dir.canRead()) {
+            Collections.addAll(ret, dir.listFiles());
+        }
+        return ret.isEmpty() ? null : ret.toArray(new File[0]);
+    }
+
+    private @NonNull ArrayMap<String, List<DefaultPermissionGrant>>
+            readDefaultPermissionExceptionsLocked() {
+        File[] files = getDefaultPermissionFiles();
+        if (files == null) {
+            return new ArrayMap<>(0);
+        }
+
+        ArrayMap<String, List<DefaultPermissionGrant>> grantExceptions = new ArrayMap<>();
+
+        // Iterate over the files in the directory and scan .xml files
+        for (File file : files) {
+            if (!file.getPath().endsWith(".xml")) {
+                Slog.i(TAG, "Non-xml file " + file
+                        + " in " + file.getParent() + " directory, ignoring");
+                continue;
+            }
+            if (!file.canRead()) {
+                Slog.w(TAG, "Default permissions file " + file + " cannot be read");
+                continue;
+            }
+            try (
+                InputStream str = new BufferedInputStream(new FileInputStream(file))
+            ) {
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(str, null);
+                parse(parser, grantExceptions);
+            } catch (XmlPullParserException | IOException e) {
+                Slog.w(TAG, "Error reading default permissions file " + file, e);
+            }
+        }
+
+        return grantExceptions;
+    }
+
+    private void parse(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
+            outGrantExceptions) throws IOException, XmlPullParserException {
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            if (TAG_EXCEPTIONS.equals(parser.getName())) {
+                parseExceptions(parser, outGrantExceptions);
+            } else {
+                Log.e(TAG, "Unknown tag " + parser.getName());
+            }
+        }
+    }
+
+    private void parseExceptions(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
+            outGrantExceptions) throws IOException, XmlPullParserException {
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            if (TAG_EXCEPTION.equals(parser.getName())) {
+                String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+
+                List<DefaultPermissionGrant> packageExceptions =
+                        outGrantExceptions.get(packageName);
+                if (packageExceptions == null) {
+                    // The package must be on the system image
+                    PackageParser.Package pkg = getSystemPackage(packageName);
+                    if (pkg == null) {
+                        Log.w(TAG, "Unknown package:" + packageName);
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+
+                    // The package must support runtime permissions
+                    if (!doesPackageSupportRuntimePermissions(pkg)) {
+                        Log.w(TAG, "Skipping non supporting runtime permissions package:"
+                                + packageName);
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    packageExceptions = new ArrayList<>();
+                    outGrantExceptions.put(packageName, packageExceptions);
+                }
+
+                parsePermission(parser, packageExceptions);
+            } else {
+                Log.e(TAG, "Unknown tag " + parser.getName() + "under <exceptions>");
+            }
+        }
+    }
+
+    private void parsePermission(XmlPullParser parser, List<DefaultPermissionGrant>
+            outPackageExceptions) throws IOException, XmlPullParserException {
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (TAG_PERMISSION.contains(parser.getName())) {
+                String name = parser.getAttributeValue(null, ATTR_NAME);
+                if (name == null) {
+                    Log.w(TAG, "Mandatory name attribute missing for permission tag");
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                }
+
+                final boolean fixed = XmlUtils.readBooleanAttribute(parser, ATTR_FIXED);
+
+                DefaultPermissionGrant exception = new DefaultPermissionGrant(name, fixed);
+                outPackageExceptions.add(exception);
+            } else {
+                Log.e(TAG, "Unknown tag " + parser.getName() + "under <exception>");
+            }
+        }
+    }
+
+    private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) {
+        return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
+    }
+
+    private static final class DefaultPermissionGrant {
+        final String name;
+        final boolean fixed;
+
+        public DefaultPermissionGrant(String name, boolean fixed) {
+            this.name = name;
+            this.fixed = fixed;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
new file mode 100644
index 0000000..3b20b42
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
+import android.content.pm.PermissionInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManager.PermissionInfoFlags;
+import android.content.pm.PackageParser.Permission;
+
+import com.android.server.pm.SharedUserSetting;
+import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Internal interfaces to be used by other components within the system server.
+ */
+public abstract class PermissionManagerInternal {
+    /**
+     * Callbacks invoked when interesting actions have been taken on a permission.
+     * <p>
+     * NOTE: The current arguments are merely to support the existing use cases. This
+     * needs to be properly thought out with appropriate arguments for each of the
+     * callback methods.
+     */
+    public static class PermissionCallback {
+        public void onGidsChanged(int appId, int userId) {
+        }
+        public void onPermissionChanged() {
+        }
+        public void onPermissionGranted(int uid, int userId) {
+        }
+        public void onInstallPermissionGranted() {
+        }
+        public void onPermissionRevoked(int uid, int userId) {
+        }
+        public void onInstallPermissionRevoked() {
+        }
+        public void onPermissionUpdated(int userId) {
+        }
+        public void onPermissionRemoved() {
+        }
+        public void onInstallPermissionUpdated() {
+        }
+    }
+
+    public abstract void grantRuntimePermission(
+            @NonNull String permName, @NonNull String packageName, boolean overridePolicy,
+            int callingUid, int userId, @Nullable PermissionCallback callback);
+    public abstract void grantRuntimePermissionsGrantedToDisabledPackage(
+            @NonNull PackageParser.Package pkg, int callingUid,
+            @Nullable PermissionCallback callback);
+    public abstract void grantRequestedRuntimePermissions(
+            @NonNull PackageParser.Package pkg, @NonNull int[] userIds,
+            @NonNull String[] grantedPermissions, int callingUid,
+            @Nullable PermissionCallback callback);
+    public abstract void revokeRuntimePermission(@NonNull String permName,
+            @NonNull String packageName, boolean overridePolicy, int callingUid, int userId,
+            @Nullable PermissionCallback callback);
+    public abstract int[] revokeUnusedSharedUserPermissions(@NonNull SharedUserSetting suSetting,
+            @NonNull int[] allUserIds);
+
+
+    public abstract boolean addPermission(@NonNull PermissionInfo info, boolean async,
+            int callingUid, @Nullable PermissionCallback callback);
+    public abstract void removePermission(@NonNull String permName, int callingUid,
+            @Nullable PermissionCallback callback);
+
+    public abstract int getPermissionFlags(@NonNull String permName,
+            @NonNull String packageName, int callingUid, int userId);
+    /**
+     * Retrieve all of the information we know about a particular permission.
+     */
+    public abstract @Nullable PermissionInfo getPermissionInfo(@NonNull String permName,
+            @NonNull String packageName, @PermissionInfoFlags int flags, int callingUid);
+    /**
+     * Retrieve all of the permissions associated with a particular group.
+     */
+    public abstract @Nullable List<PermissionInfo> getPermissionInfoByGroup(@NonNull String group,
+            @PermissionInfoFlags int flags, int callingUid);
+    public abstract boolean isPermissionAppOp(@NonNull String permName);
+    public abstract boolean isPermissionInstant(@NonNull String permName);
+
+    /**
+     * Updates the flags associated with a permission by replacing the flags in
+     * the specified mask with the provided flag values.
+     */
+    public abstract void updatePermissionFlags(@NonNull String permName,
+            @NonNull String packageName, int flagMask, int flagValues, int callingUid, int userId,
+            @Nullable PermissionCallback callback);
+    /**
+     * Updates the flags for all applications by replacing the flags in the specified mask
+     * with the provided flag values.
+     */
+    public abstract boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues,
+            int callingUid, int userId, @NonNull Collection<PackageParser.Package> packages,
+            @Nullable PermissionCallback callback);
+
+    public abstract int checkPermission(@NonNull String permName, @NonNull String packageName,
+            int callingUid, int userId);
+
+    /**
+     * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
+     * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userid} is not for the caller.
+     * @param checkShell whether to prevent shell from access if there's a debugging restriction
+     * @param message the message to log on security exception
+     */
+    public abstract void enforceCrossUserPermission(int callingUid, int userId,
+            boolean requireFullPermission, boolean checkShell, @NonNull String message);
+    public abstract void enforceGrantRevokeRuntimePermissionPermissions(@NonNull String message);
+
+    public abstract @NonNull PermissionSettings getPermissionSettings();
+    public abstract @NonNull DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy();
+
+    /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
+    public abstract Iterator<BasePermission> getPermissionIteratorTEMP();
+    public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
+    public abstract void putPermissionTEMP(@NonNull String permName,
+            @NonNull BasePermission permission);
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
new file mode 100644
index 0000000..6c031a6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionInfo;
+import android.content.pm.PackageParser.Package;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.UserManagerInternal;
+import android.os.storage.StorageManagerInternal;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
+import com.android.server.SystemConfig;
+import com.android.server.Watchdog;
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.PackageManagerServiceUtils;
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.ProcessLoggingHandler;
+import com.android.server.pm.SharedUserSetting;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
+import com.android.server.pm.permission.PermissionsState.PermissionState;
+
+import libcore.util.EmptyArray;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Manages all permissions and handles permissions related tasks.
+ */
+public class PermissionManagerService {
+    private static final String TAG = "PackageManager";
+
+    /** All dangerous permission names in the same order as the events in MetricsEvent */
+    private static final List<String> ALL_DANGEROUS_PERMISSIONS = Arrays.asList(
+            Manifest.permission.READ_CALENDAR,
+            Manifest.permission.WRITE_CALENDAR,
+            Manifest.permission.CAMERA,
+            Manifest.permission.READ_CONTACTS,
+            Manifest.permission.WRITE_CONTACTS,
+            Manifest.permission.GET_ACCOUNTS,
+            Manifest.permission.ACCESS_FINE_LOCATION,
+            Manifest.permission.ACCESS_COARSE_LOCATION,
+            Manifest.permission.RECORD_AUDIO,
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.CALL_PHONE,
+            Manifest.permission.READ_CALL_LOG,
+            Manifest.permission.WRITE_CALL_LOG,
+            Manifest.permission.ADD_VOICEMAIL,
+            Manifest.permission.USE_SIP,
+            Manifest.permission.PROCESS_OUTGOING_CALLS,
+            Manifest.permission.READ_CELL_BROADCASTS,
+            Manifest.permission.BODY_SENSORS,
+            Manifest.permission.SEND_SMS,
+            Manifest.permission.RECEIVE_SMS,
+            Manifest.permission.READ_SMS,
+            Manifest.permission.RECEIVE_WAP_PUSH,
+            Manifest.permission.RECEIVE_MMS,
+            Manifest.permission.READ_EXTERNAL_STORAGE,
+            Manifest.permission.WRITE_EXTERNAL_STORAGE,
+            Manifest.permission.READ_PHONE_NUMBERS,
+            Manifest.permission.ANSWER_PHONE_CALLS);
+
+    /** Cap the size of permission trees that 3rd party apps can define */
+    private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768;     // characters of text
+
+    /** Lock to protect internal data access */
+    private final Object mLock;
+
+    /** Internal connection to the package manager */
+    private final PackageManagerInternal mPackageManagerInt;
+
+    /** Internal connection to the user manager */
+    private final UserManagerInternal mUserManagerInt;
+
+    /** Default permission policy to provide proper behaviour out-of-the-box */
+    private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
+
+    /** Internal storage for permissions and related settings */
+    private final PermissionSettings mSettings;
+
+    private final HandlerThread mHandlerThread;
+    private final Handler mHandler;
+    private final Context mContext;
+
+    PermissionManagerService(Context context,
+            @Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
+            @NonNull Object externalLock) {
+        mContext = context;
+        mLock = externalLock;
+        mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
+        mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
+        mSettings = new PermissionSettings(context, mLock);
+
+        mHandlerThread = new ServiceThread(TAG,
+                Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        Watchdog.getInstance().addThread(mHandler);
+
+        mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy(
+                context, mHandlerThread.getLooper(), defaultGrantCallback, this);
+
+        // propagate permission configuration
+        final ArrayMap<String, SystemConfig.PermissionEntry> permConfig =
+                SystemConfig.getInstance().getPermissions();
+        synchronized (mLock) {
+            for (int i=0; i<permConfig.size(); i++) {
+                final SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
+                BasePermission bp = mSettings.getPermissionLocked(perm.name);
+                if (bp == null) {
+                    bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
+                    mSettings.putPermissionLocked(perm.name, bp);
+                }
+                if (perm.gids != null) {
+                    bp.setGids(perm.gids, perm.perUser);
+                }
+            }
+        }
+
+        LocalServices.addService(
+                PermissionManagerInternal.class, new PermissionManagerInternalImpl());
+    }
+
+    /**
+     * Creates and returns an initialized, internal service for use by other components.
+     * <p>
+     * The object returned is identical to the one returned by the LocalServices class using:
+     * {@code LocalServices.getService(PermissionManagerInternal.class);}
+     * <p>
+     * NOTE: The external lock is temporary and should be removed. This needs to be a
+     * lock created by the permission manager itself.
+     */
+    public static PermissionManagerInternal create(Context context,
+            @Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
+            @NonNull Object externalLock) {
+        final PermissionManagerInternal permMgrInt =
+                LocalServices.getService(PermissionManagerInternal.class);
+        if (permMgrInt != null) {
+            return permMgrInt;
+        }
+        new PermissionManagerService(context, defaultGrantCallback, externalLock);
+        return LocalServices.getService(PermissionManagerInternal.class);
+    }
+
+    @Nullable BasePermission getPermission(String permName) {
+        synchronized (mLock) {
+            return mSettings.getPermissionLocked(permName);
+        }
+    }
+
+    private int checkPermission(String permName, String pkgName, int callingUid, int userId) {
+        if (!mUserManagerInt.exists(userId)) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+
+        final PackageParser.Package pkg = mPackageManagerInt.getPackage(pkgName);
+        if (pkg != null && pkg.mExtras != null) {
+            if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+                return PackageManager.PERMISSION_DENIED;
+            }
+            final PackageSetting ps = (PackageSetting) pkg.mExtras;
+            final boolean instantApp = ps.getInstantApp(userId);
+            final PermissionsState permissionsState = ps.getPermissionsState();
+            if (permissionsState.hasPermission(permName, userId)) {
+                if (instantApp) {
+                    synchronized (mLock) {
+                        BasePermission bp = mSettings.getPermissionLocked(permName);
+                        if (bp != null && bp.isInstant()) {
+                            return PackageManager.PERMISSION_GRANTED;
+                        }
+                    }
+                } else {
+                    return PackageManager.PERMISSION_GRANTED;
+                }
+            }
+            // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
+            if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
+                    .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
+                return PackageManager.PERMISSION_GRANTED;
+            }
+        }
+
+        return PackageManager.PERMISSION_DENIED;
+    }
+
+    private PermissionInfo getPermissionInfo(String name, String packageName, int flags,
+            int callingUid) {
+        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+            return null;
+        }
+        // reader
+        synchronized (mLock) {
+            final BasePermission bp = mSettings.getPermissionLocked(name);
+            if (bp == null) {
+                return null;
+            }
+            final int adjustedProtectionLevel = adjustPermissionProtectionFlagsLocked(
+                    bp.getProtectionLevel(), packageName, callingUid);
+            return bp.generatePermissionInfo(adjustedProtectionLevel, flags);
+        }
+    }
+
+    private List<PermissionInfo> getPermissionInfoByGroup(
+            String groupName, int flags, int callingUid) {
+        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+            return null;
+        }
+        // reader
+        synchronized (mLock) {
+            // TODO Uncomment when mPermissionGroups moves to this class
+//            if (groupName != null && !mPermissionGroups.containsKey(groupName)) {
+//                // This is thrown as NameNotFoundException
+//                return null;
+//            }
+
+            final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
+            for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
+                final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags);
+                if (pi != null) {
+                    out.add(pi);
+                }
+            }
+            return out;
+        }
+    }
+
+    private int adjustPermissionProtectionFlagsLocked(
+            int protectionLevel, String packageName, int uid) {
+        // Signature permission flags area always reported
+        final int protectionLevelMasked = protectionLevel
+                & (PermissionInfo.PROTECTION_NORMAL
+                | PermissionInfo.PROTECTION_DANGEROUS
+                | PermissionInfo.PROTECTION_SIGNATURE);
+        if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) {
+            return protectionLevel;
+        }
+        // System sees all flags.
+        final int appId = UserHandle.getAppId(uid);
+        if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID
+                || appId == Process.SHELL_UID) {
+            return protectionLevel;
+        }
+        // Normalize package name to handle renamed packages and static libs
+        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+        if (pkg == null) {
+            return protectionLevel;
+        }
+        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+            return protectionLevelMasked;
+        }
+        // Apps that target O see flags for all protection levels.
+        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+        if (ps == null) {
+            return protectionLevel;
+        }
+        if (ps.getAppId() != appId) {
+            return protectionLevel;
+        }
+        return protectionLevel;
+    }
+
+    private boolean addPermission(
+            PermissionInfo info, int callingUid, PermissionCallback callback) {
+        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+            throw new SecurityException("Instant apps can't add permissions");
+        }
+        if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
+            throw new SecurityException("Label must be specified in permission");
+        }
+        final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
+                info.name, callingUid);
+        final boolean added;
+        final boolean changed;
+        synchronized (mLock) {
+            BasePermission bp = mSettings.getPermissionLocked(info.name);
+            added = bp == null;
+            int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
+            if (added) {
+                enforcePermissionCapLocked(info, tree);
+                bp = new BasePermission(info.name, tree.getSourcePackageName(),
+                        BasePermission.TYPE_DYNAMIC);
+            } else if (bp.isDynamic()) {
+                throw new SecurityException(
+                        "Not allowed to modify non-dynamic permission "
+                        + info.name);
+            }
+            changed = bp.addToTree(fixedLevel, info, tree);
+            if (added) {
+                mSettings.putPermissionLocked(info.name, bp);
+            }
+        }
+        if (changed && callback != null) {
+            callback.onPermissionChanged();
+        }
+        return added;
+    }
+
+    private void removePermission(
+            String permName, int callingUid, PermissionCallback callback) {
+        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+            throw new SecurityException("Instant applications don't have access to this method");
+        }
+        final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
+                permName, callingUid);
+        synchronized (mLock) {
+            final BasePermission bp = mSettings.getPermissionLocked(permName);
+            if (bp == null) {
+                return;
+            }
+            if (bp.isDynamic()) {
+                throw new SecurityException(
+                        "Not allowed to modify non-dynamic permission "
+                        + permName);
+            }
+            mSettings.removePermissionLocked(permName);
+            if (callback != null) {
+                callback.onPermissionRemoved();
+            }
+        }
+    }
+
+    private void grantRuntimePermissionsGrantedToDisabledPackageLocked(
+            PackageParser.Package pkg, int callingUid, PermissionCallback callback) {
+        if (pkg.parentPackage == null) {
+            return;
+        }
+        if (pkg.requestedPermissions == null) {
+            return;
+        }
+        final PackageParser.Package disabledPkg =
+                mPackageManagerInt.getDisabledPackage(pkg.parentPackage.packageName);
+        if (disabledPkg == null || disabledPkg.mExtras == null) {
+            return;
+        }
+        final PackageSetting disabledPs = (PackageSetting) disabledPkg.mExtras;
+        if (!disabledPs.isPrivileged() || disabledPs.hasChildPackages()) {
+            return;
+        }
+        final int permCount = pkg.requestedPermissions.size();
+        for (int i = 0; i < permCount; i++) {
+            String permission = pkg.requestedPermissions.get(i);
+            BasePermission bp = mSettings.getPermissionLocked(permission);
+            if (bp == null || !(bp.isRuntime() || bp.isDevelopment())) {
+                continue;
+            }
+            for (int userId : mUserManagerInt.getUserIds()) {
+                if (disabledPs.getPermissionsState().hasRuntimePermission(permission, userId)) {
+                    grantRuntimePermission(
+                            permission, pkg.packageName, false, callingUid, userId, callback);
+                }
+            }
+        }
+    }
+
+    private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
+            String[] grantedPermissions, int callingUid, PermissionCallback callback) {
+        for (int userId : userIds) {
+            grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions, callingUid,
+                    callback);
+        }
+    }
+
+    private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId,
+            String[] grantedPermissions, int callingUid, PermissionCallback callback) {
+        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        if (ps == null) {
+            return;
+        }
+
+        PermissionsState permissionsState = ps.getPermissionsState();
+
+        final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
+                | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+
+        final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
+                >= Build.VERSION_CODES.M;
+
+        final boolean instantApp = mPackageManagerInt.isInstantApp(pkg.packageName, userId);
+
+        for (String permission : pkg.requestedPermissions) {
+            final BasePermission bp;
+            synchronized (mLock) {
+                bp = mSettings.getPermissionLocked(permission);
+            }
+            if (bp != null && (bp.isRuntime() || bp.isDevelopment())
+                    && (!instantApp || bp.isInstant())
+                    && (supportsRuntimePermissions || !bp.isRuntimeOnly())
+                    && (grantedPermissions == null
+                           || ArrayUtils.contains(grantedPermissions, permission))) {
+                final int flags = permissionsState.getPermissionFlags(permission, userId);
+                if (supportsRuntimePermissions) {
+                    // Installer cannot change immutable permissions.
+                    if ((flags & immutableFlags) == 0) {
+                        grantRuntimePermission(permission, pkg.packageName, false, callingUid,
+                                userId, callback);
+                    }
+                } else if (mSettings.mPermissionReviewRequired) {
+                    // In permission review mode we clear the review flag when we
+                    // are asked to install the app with all permissions granted.
+                    if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                        updatePermissionFlags(permission, pkg.packageName,
+                                PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, callingUid,
+                                userId, callback);
+                    }
+                }
+            }
+        }
+    }
+
+    private void grantRuntimePermission(String permName, String packageName, boolean overridePolicy,
+            int callingUid, final int userId, PermissionCallback callback) {
+        if (!mUserManagerInt.exists(userId)) {
+            Log.e(TAG, "No such user:" + userId);
+            return;
+        }
+
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+                "grantRuntimePermission");
+
+        enforceCrossUserPermission(callingUid, userId,
+                true /* requireFullPermission */, true /* checkShell */,
+                "grantRuntimePermission");
+
+        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+        if (pkg == null || pkg.mExtras == null) {
+            throw new IllegalArgumentException("Unknown package: " + packageName);
+        }
+        final BasePermission bp;
+        synchronized(mLock) {
+            bp = mSettings.getPermissionLocked(permName);
+        }
+        if (bp == null) {
+            throw new IllegalArgumentException("Unknown permission: " + permName);
+        }
+        if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+            throw new IllegalArgumentException("Unknown package: " + packageName);
+        }
+
+        bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);
+
+        // If a permission review is required for legacy apps we represent
+        // their permissions as always granted runtime ones since we need
+        // to keep the review required permission flag per user while an
+        // install permission's state is shared across all users.
+        if (mSettings.mPermissionReviewRequired
+                && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+                && bp.isRuntime()) {
+            return;
+        }
+
+        final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
+
+        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+        final PermissionsState permissionsState = ps.getPermissionsState();
+
+        final int flags = permissionsState.getPermissionFlags(permName, userId);
+        if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
+            throw new SecurityException("Cannot grant system fixed permission "
+                    + permName + " for package " + packageName);
+        }
+        if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+            throw new SecurityException("Cannot grant policy fixed permission "
+                    + permName + " for package " + packageName);
+        }
+
+        if (bp.isDevelopment()) {
+            // Development permissions must be handled specially, since they are not
+            // normal runtime permissions.  For now they apply to all users.
+            if (permissionsState.grantInstallPermission(bp) !=
+                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                if (callback != null) {
+                    callback.onInstallPermissionGranted();
+                }
+            }
+            return;
+        }
+
+        if (ps.getInstantApp(userId) && !bp.isInstant()) {
+            throw new SecurityException("Cannot grant non-ephemeral permission"
+                    + permName + " for package " + packageName);
+        }
+
+        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+            Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
+            return;
+        }
+
+        final int result = permissionsState.grantRuntimePermission(bp, userId);
+        switch (result) {
+            case PermissionsState.PERMISSION_OPERATION_FAILURE: {
+                return;
+            }
+
+            case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
+                if (callback != null) {
+                    callback.onGidsChanged(UserHandle.getAppId(pkg.applicationInfo.uid), userId);
+                }
+            }
+            break;
+        }
+
+        if (bp.isRuntime()) {
+            logPermissionGranted(mContext, permName, packageName);
+        }
+
+        if (callback != null) {
+            callback.onPermissionGranted(uid, userId);
+        }
+
+        // Only need to do this if user is initialized. Otherwise it's a new user
+        // and there are no processes running as the user yet and there's no need
+        // to make an expensive call to remount processes for the changed permissions.
+        if (READ_EXTERNAL_STORAGE.equals(permName)
+                || WRITE_EXTERNAL_STORAGE.equals(permName)) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (mUserManagerInt.isUserInitialized(userId)) {
+                    StorageManagerInternal storageManagerInternal = LocalServices.getService(
+                            StorageManagerInternal.class);
+                    storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+    }
+
+    private void revokeRuntimePermission(String permName, String packageName,
+            boolean overridePolicy, int callingUid, int userId, PermissionCallback callback) {
+        if (!mUserManagerInt.exists(userId)) {
+            Log.e(TAG, "No such user:" + userId);
+            return;
+        }
+
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+                "revokeRuntimePermission");
+
+        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+                true /* requireFullPermission */, true /* checkShell */,
+                "revokeRuntimePermission");
+
+        final int appId;
+
+        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+        if (pkg == null || pkg.mExtras == null) {
+            throw new IllegalArgumentException("Unknown package: " + packageName);
+        }
+        if (mPackageManagerInt.filterAppAccess(pkg, Binder.getCallingUid(), userId)) {
+            throw new IllegalArgumentException("Unknown package: " + packageName);
+        }
+        final BasePermission bp = mSettings.getPermissionLocked(permName);
+        if (bp == null) {
+            throw new IllegalArgumentException("Unknown permission: " + permName);
+        }
+
+        bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);
+
+        // If a permission review is required for legacy apps we represent
+        // their permissions as always granted runtime ones since we need
+        // to keep the review required permission flag per user while an
+        // install permission's state is shared across all users.
+        if (mSettings.mPermissionReviewRequired
+                && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+                && bp.isRuntime()) {
+            return;
+        }
+
+        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+        final PermissionsState permissionsState = ps.getPermissionsState();
+
+        final int flags = permissionsState.getPermissionFlags(permName, userId);
+        if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
+            throw new SecurityException("Cannot revoke system fixed permission "
+                    + permName + " for package " + packageName);
+        }
+        if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+            throw new SecurityException("Cannot revoke policy fixed permission "
+                    + permName + " for package " + packageName);
+        }
+
+        if (bp.isDevelopment()) {
+            // Development permissions must be handled specially, since they are not
+            // normal runtime permissions.  For now they apply to all users.
+            if (permissionsState.revokeInstallPermission(bp) !=
+                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                if (callback != null) {
+                    callback.onInstallPermissionRevoked();
+                }
+            }
+            return;
+        }
+
+        if (permissionsState.revokeRuntimePermission(bp, userId) ==
+                PermissionsState.PERMISSION_OPERATION_FAILURE) {
+            return;
+        }
+
+        if (bp.isRuntime()) {
+            logPermissionRevoked(mContext, permName, packageName);
+        }
+
+        if (callback != null) {
+            final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
+            callback.onPermissionRevoked(pkg.applicationInfo.uid, userId);
+        }
+    }
+
+    private int[] revokeUnusedSharedUserPermissions(SharedUserSetting suSetting, int[] allUserIds) {
+        // Collect all used permissions in the UID
+        final ArraySet<String> usedPermissions = new ArraySet<>();
+        final List<PackageParser.Package> pkgList = suSetting.getPackages();
+        if (pkgList == null || pkgList.size() == 0) {
+            return EmptyArray.INT;
+        }
+        for (PackageParser.Package pkg : pkgList) {
+            final int requestedPermCount = pkg.requestedPermissions.size();
+            for (int j = 0; j < requestedPermCount; j++) {
+                String permission = pkg.requestedPermissions.get(j);
+                BasePermission bp = mSettings.getPermissionLocked(permission);
+                if (bp != null) {
+                    usedPermissions.add(permission);
+                }
+            }
+        }
+
+        PermissionsState permissionsState = suSetting.getPermissionsState();
+        // Prune install permissions
+        List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates();
+        final int installPermCount = installPermStates.size();
+        for (int i = installPermCount - 1; i >= 0;  i--) {
+            PermissionState permissionState = installPermStates.get(i);
+            if (!usedPermissions.contains(permissionState.getName())) {
+                BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
+                if (bp != null) {
+                    permissionsState.revokeInstallPermission(bp);
+                    permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+                            PackageManager.MASK_PERMISSION_FLAGS, 0);
+                }
+            }
+        }
+
+        int[] runtimePermissionChangedUserIds = EmptyArray.INT;
+
+        // Prune runtime permissions
+        for (int userId : allUserIds) {
+            List<PermissionState> runtimePermStates = permissionsState
+                    .getRuntimePermissionStates(userId);
+            final int runtimePermCount = runtimePermStates.size();
+            for (int i = runtimePermCount - 1; i >= 0; i--) {
+                PermissionState permissionState = runtimePermStates.get(i);
+                if (!usedPermissions.contains(permissionState.getName())) {
+                    BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
+                    if (bp != null) {
+                        permissionsState.revokeRuntimePermission(bp, userId);
+                        permissionsState.updatePermissionFlags(bp, userId,
+                                PackageManager.MASK_PERMISSION_FLAGS, 0);
+                        runtimePermissionChangedUserIds = ArrayUtils.appendInt(
+                                runtimePermissionChangedUserIds, userId);
+                    }
+                }
+            }
+        }
+
+        return runtimePermissionChangedUserIds;
+    }
+
+    private int getPermissionFlags(String permName, String packageName, int callingUid, int userId) {
+        if (!mUserManagerInt.exists(userId)) {
+            return 0;
+        }
+
+        enforceGrantRevokeRuntimePermissionPermissions("getPermissionFlags");
+
+        enforceCrossUserPermission(callingUid, userId,
+                true /* requireFullPermission */, false /* checkShell */,
+                "getPermissionFlags");
+
+        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+        if (pkg == null || pkg.mExtras == null) {
+            return 0;
+        }
+        synchronized (mLock) {
+            if (mSettings.getPermissionLocked(permName) == null) {
+                return 0;
+            }
+        }
+        if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+            return 0;
+        }
+        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+        PermissionsState permissionsState = ps.getPermissionsState();
+        return permissionsState.getPermissionFlags(permName, userId);
+    }
+
+    private void updatePermissionFlags(String permName, String packageName, int flagMask,
+            int flagValues, int callingUid, int userId, PermissionCallback callback) {
+        if (!mUserManagerInt.exists(userId)) {
+            return;
+        }
+
+        enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags");
+
+        enforceCrossUserPermission(callingUid, userId,
+                true /* requireFullPermission */, true /* checkShell */,
+                "updatePermissionFlags");
+
+        // Only the system can change these flags and nothing else.
+        if (callingUid != Process.SYSTEM_UID) {
+            flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+            flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+            flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+            flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+            flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+        }
+
+        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+        if (pkg == null || pkg.mExtras == null) {
+            throw new IllegalArgumentException("Unknown package: " + packageName);
+        }
+        if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+            throw new IllegalArgumentException("Unknown package: " + packageName);
+        }
+
+        final BasePermission bp;
+        synchronized (mLock) {
+            bp = mSettings.getPermissionLocked(permName);
+        }
+        if (bp == null) {
+            throw new IllegalArgumentException("Unknown permission: " + permName);
+        }
+
+        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+        final PermissionsState permissionsState = ps.getPermissionsState();
+        final boolean hadState =
+                permissionsState.getRuntimePermissionState(permName, userId) != null;
+        final boolean permissionUpdated =
+                permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues);
+        if (permissionUpdated && callback != null) {
+            // Install and runtime permissions are stored in different places,
+            // so figure out what permission changed and persist the change.
+            if (permissionsState.getInstallPermissionState(permName) != null) {
+                callback.onInstallPermissionUpdated();
+            } else if (permissionsState.getRuntimePermissionState(permName, userId) != null
+                    || hadState) {
+                callback.onPermissionUpdated(userId);
+            }
+        }
+    }
+
+    private boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid,
+            int userId, Collection<Package> packages, PermissionCallback callback) {
+        if (!mUserManagerInt.exists(userId)) {
+            return false;
+        }
+
+        enforceGrantRevokeRuntimePermissionPermissions(
+                "updatePermissionFlagsForAllApps");
+        enforceCrossUserPermission(callingUid, userId,
+                true /* requireFullPermission */, true /* checkShell */,
+                "updatePermissionFlagsForAllApps");
+
+        // Only the system can change system fixed flags.
+        if (callingUid != Process.SYSTEM_UID) {
+            flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+            flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+        }
+
+        boolean changed = false;
+        for (PackageParser.Package pkg : packages) {
+            final PackageSetting ps = (PackageSetting) pkg.mExtras;
+            if (ps == null) {
+                continue;
+            }
+            PermissionsState permissionsState = ps.getPermissionsState();
+            changed |= permissionsState.updatePermissionFlagsForAllPermissions(
+                    userId, flagMask, flagValues);
+        }
+        return changed;
+    }
+
+    private void enforceGrantRevokeRuntimePermissionPermissions(String message) {
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
+                != PackageManager.PERMISSION_GRANTED
+            && mContext.checkCallingOrSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(message + " requires "
+                    + Manifest.permission.GRANT_RUNTIME_PERMISSIONS + " or "
+                    + Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
+        }
+    }
+
+    /**
+     * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS
+     * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller.
+     * @param checkShell whether to prevent shell from access if there's a debugging restriction
+     * @param message the message to log on security exception
+     */
+    private void enforceCrossUserPermission(int callingUid, int userId,
+            boolean requireFullPermission, boolean checkShell, String message) {
+        if (userId < 0) {
+            throw new IllegalArgumentException("Invalid userId " + userId);
+        }
+        if (checkShell) {
+            PackageManagerServiceUtils.enforceShellRestriction(
+                    UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+        }
+        if (userId == UserHandle.getUserId(callingUid)) return;
+        if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
+            if (requireFullPermission) {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+            } else {
+                try {
+                    mContext.enforceCallingOrSelfPermission(
+                            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+                } catch (SecurityException se) {
+                    mContext.enforceCallingOrSelfPermission(
+                            android.Manifest.permission.INTERACT_ACROSS_USERS, message);
+                }
+            }
+        }
+    }
+
+    private int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
+        int size = 0;
+        for (BasePermission perm : mSettings.getAllPermissionsLocked()) {
+            size += tree.calculateFootprint(perm);
+        }
+        return size;
+    }
+
+    private void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
+        // We calculate the max size of permissions defined by this uid and throw
+        // if that plus the size of 'info' would exceed our stated maximum.
+        if (tree.getUid() != Process.SYSTEM_UID) {
+            final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree);
+            if (curTreeSize + info.calculateFootprint() > MAX_PERMISSION_TREE_FOOTPRINT) {
+                throw new SecurityException("Permission tree size cap exceeded");
+            }
+        }
+    }
+
+    /**
+     * Get the first event id for the permission.
+     *
+     * <p>There are four events for each permission: <ul>
+     *     <li>Request permission: first id + 0</li>
+     *     <li>Grant permission: first id + 1</li>
+     *     <li>Request for permission denied: first id + 2</li>
+     *     <li>Revoke permission: first id + 3</li>
+     * </ul></p>
+     *
+     * @param name name of the permission
+     *
+     * @return The first event id for the permission
+     */
+    private static int getBaseEventId(@NonNull String name) {
+        int eventIdIndex = ALL_DANGEROUS_PERMISSIONS.indexOf(name);
+
+        if (eventIdIndex == -1) {
+            if (AppOpsManager.permissionToOpCode(name) == AppOpsManager.OP_NONE
+                    || Build.IS_USER) {
+                Log.i(TAG, "Unknown permission " + name);
+
+                return MetricsEvent.ACTION_PERMISSION_REQUEST_UNKNOWN;
+            } else {
+                // Most likely #ALL_DANGEROUS_PERMISSIONS needs to be updated.
+                //
+                // Also update
+                // - EventLogger#ALL_DANGEROUS_PERMISSIONS
+                // - metrics_constants.proto
+                throw new IllegalStateException("Unknown permission " + name);
+            }
+        }
+
+        return MetricsEvent.ACTION_PERMISSION_REQUEST_READ_CALENDAR + eventIdIndex * 4;
+    }
+
+    /**
+     * Log that a permission was revoked.
+     *
+     * @param context Context of the caller
+     * @param name name of the permission
+     * @param packageName package permission if for
+     */
+    private static void logPermissionRevoked(@NonNull Context context, @NonNull String name,
+            @NonNull String packageName) {
+        MetricsLogger.action(context, getBaseEventId(name) + 3, packageName);
+    }
+
+    /**
+     * Log that a permission request was granted.
+     *
+     * @param context Context of the caller
+     * @param name name of the permission
+     * @param packageName package permission if for
+     */
+    private static void logPermissionGranted(@NonNull Context context, @NonNull String name,
+            @NonNull String packageName) {
+        MetricsLogger.action(context, getBaseEventId(name) + 1, packageName);
+    }
+
+    private class PermissionManagerInternalImpl extends PermissionManagerInternal {
+        @Override
+        public boolean addPermission(PermissionInfo info, boolean async, int callingUid,
+                PermissionCallback callback) {
+            return PermissionManagerService.this.addPermission(info, callingUid, callback);
+        }
+        @Override
+        public void removePermission(String permName, int callingUid,
+                PermissionCallback callback) {
+            PermissionManagerService.this.removePermission(permName, callingUid, callback);
+        }
+        @Override
+        public void grantRuntimePermission(String permName, String packageName,
+                boolean overridePolicy, int callingUid, int userId,
+                PermissionCallback callback) {
+            PermissionManagerService.this.grantRuntimePermission(
+                    permName, packageName, overridePolicy, callingUid, userId, callback);
+        }
+        @Override
+        public void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
+                String[] grantedPermissions, int callingUid, PermissionCallback callback) {
+            PermissionManagerService.this.grantRequestedRuntimePermissions(
+                    pkg, userIds, grantedPermissions, callingUid, callback);
+        }
+        @Override
+        public void grantRuntimePermissionsGrantedToDisabledPackage(PackageParser.Package pkg,
+                int callingUid, PermissionCallback callback) {
+            PermissionManagerService.this.grantRuntimePermissionsGrantedToDisabledPackageLocked(
+                    pkg, callingUid, callback);
+        }
+        @Override
+        public void revokeRuntimePermission(String permName, String packageName,
+                boolean overridePolicy, int callingUid, int userId,
+                PermissionCallback callback) {
+            PermissionManagerService.this.revokeRuntimePermission(permName, packageName,
+                    overridePolicy, callingUid, userId, callback);
+        }
+        @Override
+        public int[] revokeUnusedSharedUserPermissions(SharedUserSetting suSetting,
+                int[] allUserIds) {
+            return PermissionManagerService.this.revokeUnusedSharedUserPermissions(
+                    (SharedUserSetting) suSetting, allUserIds);
+        }
+        @Override
+        public int getPermissionFlags(String permName, String packageName, int callingUid,
+                int userId) {
+            return PermissionManagerService.this.getPermissionFlags(permName, packageName,
+                    callingUid, userId);
+        }
+        @Override
+        public void updatePermissionFlags(String permName, String packageName, int flagMask,
+                int flagValues, int callingUid, int userId, PermissionCallback callback) {
+            PermissionManagerService.this.updatePermissionFlags(
+                    permName, packageName, flagMask, flagValues, callingUid, userId, callback);
+        }
+        @Override
+        public boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid,
+                int userId, Collection<Package> packages, PermissionCallback callback) {
+            return PermissionManagerService.this.updatePermissionFlagsForAllApps(
+                    flagMask, flagValues, callingUid, userId, packages, callback);
+        }
+        @Override
+        public void enforceCrossUserPermission(int callingUid, int userId,
+                boolean requireFullPermission, boolean checkShell, String message) {
+            PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId,
+                    requireFullPermission, checkShell, message);
+        }
+        @Override
+        public void enforceGrantRevokeRuntimePermissionPermissions(String message) {
+            PermissionManagerService.this.enforceGrantRevokeRuntimePermissionPermissions(message);
+        }
+        @Override
+        public int checkPermission(String permName, String packageName, int callingUid,
+                int userId) {
+            return PermissionManagerService.this.checkPermission(
+                    permName, packageName, callingUid, userId);
+        }
+        @Override
+        public PermissionInfo getPermissionInfo(String permName, String packageName, int flags,
+                int callingUid) {
+            return PermissionManagerService.this.getPermissionInfo(
+                    permName, packageName, flags, callingUid);
+        }
+        @Override
+        public List<PermissionInfo> getPermissionInfoByGroup(String group, int flags,
+                int callingUid) {
+            return PermissionManagerService.this.getPermissionInfoByGroup(group, flags, callingUid);
+        }
+        @Override
+        public boolean isPermissionInstant(String permName) {
+            synchronized (PermissionManagerService.this.mLock) {
+                final BasePermission bp = mSettings.getPermissionLocked(permName);
+                return (bp != null && bp.isInstant());
+            }
+        }
+        @Override
+        public boolean isPermissionAppOp(String permName) {
+            synchronized (PermissionManagerService.this.mLock) {
+                final BasePermission bp = mSettings.getPermissionLocked(permName);
+                return (bp != null && bp.isAppOp());
+            }
+        }
+        @Override
+        public PermissionSettings getPermissionSettings() {
+            return mSettings;
+        }
+        @Override
+        public DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() {
+            return mDefaultPermissionGrantPolicy;
+        }
+        @Override
+        public BasePermission getPermissionTEMP(String permName) {
+            synchronized (PermissionManagerService.this.mLock) {
+                return mSettings.getPermissionLocked(permName);
+            }
+        }
+        @Override
+        public void putPermissionTEMP(String permName, BasePermission permission) {
+            synchronized (PermissionManagerService.this.mLock) {
+                mSettings.putPermissionLocked(permName, (BasePermission) permission);
+            }
+        }
+        @Override
+        public Iterator<BasePermission> getPermissionIteratorTEMP() {
+            synchronized (PermissionManagerService.this.mLock) {
+                return mSettings.getAllPermissionsLocked().iterator();
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
new file mode 100644
index 0000000..7a2e5ecc
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.util.XmlUtils;
+import com.android.server.pm.DumpState;
+import com.android.server.pm.PackageManagerService;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Collection;
+
+/**
+ * Permissions and other related data. This class is not meant for
+ * direct access outside of the permission package with the sole exception
+ * of package settings. Instead, it should be reference either from the
+ * permission manager or package settings.
+ */
+public class PermissionSettings {
+
+    final boolean mPermissionReviewRequired;
+    /**
+     * All of the permissions known to the system. The mapping is from permission
+     * name to permission object.
+     */
+    private final ArrayMap<String, BasePermission> mPermissions =
+            new ArrayMap<String, BasePermission>();
+    private final Object mLock;
+
+    PermissionSettings(@NonNull Context context, @NonNull Object lock) {
+        mPermissionReviewRequired =
+                context.getResources().getBoolean(R.bool.config_permissionReviewRequired);
+        mLock = lock;
+    }
+
+    public @Nullable BasePermission getPermission(@NonNull String permName) {
+        synchronized (mLock) {
+            return getPermissionLocked(permName);
+        }
+    }
+
+    /**
+     * Transfers ownership of permissions from one package to another.
+     */
+    public void transferPermissions(String origPackageName, String newPackageName,
+            ArrayMap<String, BasePermission> permissionTrees) {
+        synchronized (mLock) {
+            for (int i=0; i<2; i++) {
+                ArrayMap<String, BasePermission> permissions =
+                        i == 0 ? permissionTrees : mPermissions;
+                for (BasePermission bp : permissions.values()) {
+                    bp.transfer(origPackageName, newPackageName);
+                }
+            }
+        }
+    }
+
+    public boolean canPropagatePermissionToInstantApp(String permName) {
+        synchronized (mLock) {
+            final BasePermission bp = mPermissions.get(permName);
+            return (bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant());
+        }
+    }
+
+    public void readPermissions(XmlPullParser parser) throws IOException, XmlPullParserException {
+        synchronized (mLock) {
+            readPermissions(mPermissions, parser);
+        }
+    }
+
+    public void writePermissions(XmlSerializer serializer) throws IOException {
+        for (BasePermission bp : mPermissions.values()) {
+            bp.writeLPr(serializer);
+        }
+    }
+
+    public static void readPermissions(ArrayMap<String, BasePermission> out, XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (!BasePermission.readLPw(out, parser)) {
+                PackageManagerService.reportSettingsProblem(Log.WARN,
+                        "Unknown element reading permissions: " + parser.getName() + " at "
+                                + parser.getPositionDescription());
+            }
+            XmlUtils.skipCurrentTag(parser);
+        }
+    }
+
+    public void dumpPermissions(PrintWriter pw, String packageName,
+            ArraySet<String> permissionNames, boolean externalStorageEnforced,
+            DumpState dumpState) {
+        synchronized (mLock) {
+            boolean printedSomething = false;
+            for (BasePermission bp : mPermissions.values()) {
+                printedSomething = bp.dumpPermissionsLPr(pw, packageName, permissionNames,
+                        externalStorageEnforced, printedSomething, dumpState);
+            }
+        }
+    }
+
+    @Nullable BasePermission getPermissionLocked(@NonNull String permName) {
+        return mPermissions.get(permName);
+    }
+
+    void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) {
+        mPermissions.put(permName, permission);
+    }
+
+    void removePermissionLocked(@NonNull String permName) {
+        mPermissions.remove(permName);
+    }
+
+    Collection<BasePermission> getAllPermissionsLocked() {
+        return mPermissions.values();
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java
similarity index 99%
rename from services/core/java/com/android/server/pm/PermissionsState.java
rename to services/core/java/com/android/server/pm/permission/PermissionsState.java
index 8b42a99..11df380 100644
--- a/services/core/java/com/android/server/pm/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.pm;
+package com.android.server.pm.permission;
 
 import android.content.pm.PackageManager;
 import android.os.UserHandle;
@@ -24,7 +24,6 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.permission.BasePermission;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index f10bf1a..83fd549 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -529,7 +529,7 @@
      */
     @Override
     public void disable2ForUser(int what, IBinder token, String pkg, int userId) {
-        enforceStatusBar();
+        enforceStatusBarService();
 
         synchronized (mLock) {
             disableLocked(userId, what, token, pkg, 2);
diff --git a/services/core/java/com/android/server/utils/ManagedApplicationService.java b/services/core/java/com/android/server/utils/ManagedApplicationService.java
index 1ea7058..c555388 100644
--- a/services/core/java/com/android/server/utils/ManagedApplicationService.java
+++ b/services/core/java/com/android/server/utils/ManagedApplicationService.java
@@ -16,19 +16,24 @@
 package com.android.server.utils;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
 import android.os.IInterface;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Slog;
 
+import java.text.SimpleDateFormat;
 import java.util.Objects;
+import java.util.Date;
 
 /**
  * Manages the lifecycle of an application-provided service bound from system server.
@@ -38,6 +43,40 @@
 public class ManagedApplicationService {
     private final String TAG = getClass().getSimpleName();
 
+    /**
+     * Attempt to reconnect service forever if an onBindingDied or onServiceDisconnected event
+     * is received.
+     */
+    public static final int RETRY_FOREVER = 1;
+
+    /**
+     * Never attempt to reconnect the service - a single onBindingDied or onServiceDisconnected
+     * event will cause this to fully unbind the service and never attempt to reconnect.
+     */
+    public static final int RETRY_NEVER = 2;
+
+    /**
+     * Attempt to reconnect the service until the maximum number of retries is reached, then stop.
+     *
+     * The first retry will occur MIN_RETRY_DURATION_MS after the disconnection, and each
+     * subsequent retry will occur after 2x the duration used for the previous retry up to the
+     * MAX_RETRY_DURATION_MS duration.
+     *
+     * In this case, retries mean a full unbindService/bindService pair to handle cases when the
+     * usual service re-connection logic in ActiveServices has very high backoff times or when the
+     * serviceconnection has fully died due to a package update or similar.
+     */
+    public static final int RETRY_BEST_EFFORT = 3;
+
+    // Maximum number of retries before giving up (for RETRY_BEST_EFFORT).
+    private static final int MAX_RETRY_COUNT = 4;
+    // Max time between retry attempts.
+    private static final long MAX_RETRY_DURATION_MS = 16000;
+    // Min time between retry attempts.
+    private static final long MIN_RETRY_DURATION_MS = 2000;
+    // Time since the last retry attempt after which to clear the retry attempt counter.
+    private static final long RETRY_RESET_TIME_MS = MAX_RETRY_DURATION_MS * 4;
+
     private final Context mContext;
     private final int mUserId;
     private final ComponentName mComponent;
@@ -45,27 +84,75 @@
     private final String mSettingsAction;
     private final BinderChecker mChecker;
     private final boolean mIsImportant;
-
-    private final DeathRecipient mDeathRecipient = new DeathRecipient() {
-        @Override
-        public void binderDied() {
-            synchronized (mLock) {
-                mBoundInterface = null;
-            }
-        }
-    };
+    private final int mRetryType;
+    private final Handler mHandler;
+    private final Runnable mRetryRunnable = this::doRetry;
+    private final EventCallback mEventCb;
 
     private final Object mLock = new Object();
 
     // State protected by mLock
-    private ServiceConnection mPendingConnection;
     private ServiceConnection mConnection;
     private IInterface mBoundInterface;
     private PendingEvent mPendingEvent;
+    private int mRetryCount;
+    private long mLastRetryTimeMs;
+    private long mNextRetryDurationMs = MIN_RETRY_DURATION_MS;
+    private boolean mRetrying;
+
+    public static interface LogFormattable {
+       String toLogString(SimpleDateFormat dateFormat);
+    }
+
+    /**
+     * Lifecycle event of this managed service.
+     */
+    public static class LogEvent implements LogFormattable {
+        public static final int EVENT_CONNECTED = 1;
+        public static final int EVENT_DISCONNECTED = 2;
+        public static final int EVENT_BINDING_DIED = 3;
+        public static final int EVENT_STOPPED_PERMANENTLY = 4;
+
+        // Time of the events in "current time ms" timebase.
+        public final long timestamp;
+        // Name of the component for this system service.
+        public final ComponentName component;
+        // ID of the event that occurred.
+        public final int event;
+
+        public LogEvent(long timestamp, ComponentName component, int event) {
+            this.timestamp = timestamp;
+            this.component = component;
+            this.event = event;
+        }
+
+        @Override
+        public String toLogString(SimpleDateFormat dateFormat) {
+            return dateFormat.format(new Date(timestamp)) + "   " + eventToString(event)
+                    + " Managed Service: "
+                    + ((component == null) ? "None" : component.flattenToString());
+        }
+
+        public static String eventToString(int event) {
+            switch (event) {
+                case EVENT_CONNECTED:
+                    return "Connected";
+                case EVENT_DISCONNECTED:
+                    return "Disconnected";
+                case EVENT_BINDING_DIED:
+                    return "Binding Died For";
+                case EVENT_STOPPED_PERMANENTLY:
+                    return "Permanently Stopped";
+                default:
+                    return "Unknown Event Occurred";
+            }
+        }
+    }
 
     private ManagedApplicationService(final Context context, final ComponentName component,
             final int userId, int clientLabel, String settingsAction,
-            BinderChecker binderChecker, boolean isImportant) {
+            BinderChecker binderChecker, boolean isImportant, int retryType, Handler handler,
+            EventCallback eventCallback) {
         mContext = context;
         mComponent = component;
         mUserId = userId;
@@ -73,6 +160,9 @@
         mSettingsAction = settingsAction;
         mChecker = binderChecker;
         mIsImportant = isImportant;
+        mRetryType = retryType;
+        mHandler = handler;
+        mEventCb = eventCallback;
     }
 
     /**
@@ -87,7 +177,17 @@
      * Implement to call IInterface methods after service is connected.
      */
     public interface PendingEvent {
-         void runEvent(IInterface service) throws RemoteException;
+        void runEvent(IInterface service) throws RemoteException;
+    }
+
+    /**
+     * Implement to be notified about any problems with remote service.
+     */
+    public interface EventCallback {
+        /**
+         * Called when an sevice lifecycle event occurs.
+         */
+        void onServiceEvent(LogEvent event);
     }
 
     /**
@@ -97,21 +197,28 @@
      * @param component the {@link ComponentName} of the application service to bind.
      * @param userId the user ID of user to bind the application service as.
      * @param clientLabel the resource ID of a label displayed to the user indicating the
-     *      binding service.
+     *      binding service, or 0 if none is desired.
      * @param settingsAction an action that can be used to open the Settings UI to enable/disable
-     *      binding to these services.
-     * @param binderChecker an interface used to validate the returned binder object.
+     *      binding to these services, or null if none is desired.
+     * @param binderChecker an interface used to validate the returned binder object, or null if
+     *      this interface is unchecked.
      * @param isImportant bind the user service with BIND_IMPORTANT.
+     * @param retryType reconnect behavior to have when bound service is disconnected.
+     * @param handler the Handler to use for retries and delivering EventCallbacks.
+     * @param eventCallback a callback used to deliver disconnection events, or null if you
+     *      don't care.
      * @return a ManagedApplicationService instance.
      */
     public static ManagedApplicationService build(@NonNull final Context context,
-        @NonNull final ComponentName component, final int userId, @NonNull int clientLabel,
-        @NonNull String settingsAction, @NonNull BinderChecker binderChecker,
-        boolean isImportant) {
+            @NonNull final ComponentName component, final int userId, int clientLabel,
+            @Nullable String settingsAction, @Nullable BinderChecker binderChecker,
+            boolean isImportant, int retryType, @NonNull Handler handler,
+            @Nullable EventCallback eventCallback) {
         return new ManagedApplicationService(context, component, userId, clientLabel,
-            settingsAction, binderChecker, isImportant);
+            settingsAction, binderChecker, isImportant, retryType, handler, eventCallback);
     }
 
+
     /**
      * @return the user ID of the user that owns the bound service.
      */
@@ -142,13 +249,12 @@
         return true;
     }
 
-
-  /**
-   * Send an event to run as soon as the binder interface is available.
-   *
-   * @param event a {@link PendingEvent} to send.
-   */
-  public void sendEvent(@NonNull PendingEvent event) {
+    /**
+     * Send an event to run as soon as the binder interface is available.
+     *
+     * @param event a {@link PendingEvent} to send.
+     */
+    public void sendEvent(@NonNull PendingEvent event) {
         IInterface iface;
         synchronized (mLock) {
             iface = mBoundInterface;
@@ -171,15 +277,13 @@
      */
     public void disconnect() {
         synchronized (mLock) {
-            // Wipe out pending connections
-            mPendingConnection = null;
-
             // Unbind existing connection, if it exists
-            if (mConnection != null) {
-                mContext.unbindService(mConnection);
-                mConnection = null;
+            if (mConnection == null) {
+                return;
             }
 
+            mContext.unbindService(mConnection);
+            mConnection = null;
             mBoundInterface = null;
         }
     }
@@ -189,48 +293,70 @@
      */
     public void connect() {
         synchronized (mLock) {
-            if (mConnection != null || mPendingConnection != null) {
+            if (mConnection != null) {
                 // We're already connected or are trying to connect
                 return;
             }
 
-            final PendingIntent pendingIntent = PendingIntent.getActivity(
-                    mContext, 0, new Intent(mSettingsAction), 0);
-            final Intent intent = new Intent().setComponent(mComponent).
-                    putExtra(Intent.EXTRA_CLIENT_LABEL, mClientLabel).
-                    putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
+            Intent intent  = new Intent().setComponent(mComponent);
+            if (mClientLabel != 0) {
+                intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mClientLabel);
+            }
+            if (mSettingsAction != null) {
+                intent.putExtra(Intent.EXTRA_CLIENT_INTENT,
+                        PendingIntent.getActivity(mContext, 0, new Intent(mSettingsAction), 0));
+            }
 
-            final ServiceConnection serviceConnection = new ServiceConnection() {
+            mConnection = new ServiceConnection() {
+                @Override
+                public void onBindingDied(ComponentName componentName) {
+                    final long timestamp = System.currentTimeMillis();
+                    Slog.w(TAG, "Service binding died: " + componentName);
+                    synchronized (mLock) {
+                        if (mConnection != this) {
+                            return;
+                        }
+                        mHandler.post(() -> {
+                            mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
+                                  LogEvent.EVENT_BINDING_DIED));
+                        });
+
+                        mBoundInterface = null;
+                        startRetriesLocked();
+                    }
+                }
+
                 @Override
                 public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+                    final long timestamp = System.currentTimeMillis();
+                    Slog.i(TAG, "Service connected: " + componentName);
                     IInterface iface = null;
                     PendingEvent pendingEvent = null;
                     synchronized (mLock) {
-                        if (mPendingConnection == this) {
-                            // No longer pending, remove from pending connection
-                            mPendingConnection = null;
-                            mConnection = this;
-                        } else {
-                            // Service connection wasn't pending, must have been disconnected
-                            mContext.unbindService(this);
+                        if (mConnection != this) {
+                            // Must've been unbound.
                             return;
                         }
+                        mHandler.post(() -> {
+                            mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
+                                  LogEvent.EVENT_CONNECTED));
+                        });
 
-                        try {
-                            iBinder.linkToDeath(mDeathRecipient, 0);
+                        stopRetriesLocked();
+
+                        mBoundInterface = null;
+                        if (mChecker != null) {
                             mBoundInterface = mChecker.asInterface(iBinder);
                             if (!mChecker.checkType(mBoundInterface)) {
-                                // Received an invalid binder, disconnect
-                                mContext.unbindService(this);
+                                // Received an invalid binder, disconnect.
                                 mBoundInterface = null;
+                                Slog.w(TAG, "Invalid binder from " + componentName);
+                                startRetriesLocked();
+                                return;
                             }
                             iface = mBoundInterface;
                             pendingEvent = mPendingEvent;
                             mPendingEvent = null;
-                        } catch (RemoteException e) {
-                            // DOA
-                            Slog.w(TAG, "Unable to bind service: " + intent, e);
-                            mBoundInterface = null;
                         }
                     }
                     if (iface != null && pendingEvent != null) {
@@ -238,31 +364,44 @@
                             pendingEvent.runEvent(iface);
                         } catch (RuntimeException | RemoteException ex) {
                             Slog.e(TAG, "Received exception from user service: ", ex);
+                            startRetriesLocked();
                         }
                     }
                 }
 
                 @Override
                 public void onServiceDisconnected(ComponentName componentName) {
-                    Slog.w(TAG, "Service disconnected: " + intent);
-                    mConnection = null;
-                    mBoundInterface = null;
+                    final long timestamp = System.currentTimeMillis();
+                    Slog.w(TAG, "Service disconnected: " + componentName);
+                    synchronized (mLock) {
+                        if (mConnection != this) {
+                            return;
+                        }
+
+                        mHandler.post(() -> {
+                            mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
+                                  LogEvent.EVENT_DISCONNECTED));
+                        });
+
+                        mBoundInterface = null;
+                        startRetriesLocked();
+                    }
                 }
             };
 
-            mPendingConnection = serviceConnection;
-
             int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
             if (mIsImportant) {
                 flags |= Context.BIND_IMPORTANT;
             }
             try {
-                if (!mContext.bindServiceAsUser(intent, serviceConnection, flags,
+                if (!mContext.bindServiceAsUser(intent, mConnection, flags,
                         new UserHandle(mUserId))) {
                     Slog.w(TAG, "Unable to bind service: " + intent);
+                    startRetriesLocked();
                 }
             } catch (SecurityException e) {
                 Slog.w(TAG, "Unable to bind service: " + intent, e);
+                startRetriesLocked();
             }
         }
     }
@@ -270,4 +409,81 @@
     private boolean matches(final ComponentName component, final int userId) {
         return Objects.equals(mComponent, component) && mUserId == userId;
     }
+
+    private void startRetriesLocked() {
+        if (checkAndDeliverServiceDiedCbLocked()) {
+            // If we delivered the service callback, disconnect and stop retrying.
+            disconnect();
+            return;
+        }
+
+        if (mRetrying) {
+            // Retry already queued, don't queue a new one.
+            return;
+        }
+        mRetrying = true;
+        queueRetryLocked();
+    }
+
+    private void stopRetriesLocked() {
+        mRetrying = false;
+        mHandler.removeCallbacks(mRetryRunnable);
+    }
+
+    private void queueRetryLocked() {
+        long now = SystemClock.uptimeMillis();
+        if ((now - mLastRetryTimeMs) > RETRY_RESET_TIME_MS) {
+            // It's been longer than the reset time since we last had to retry.  Re-initialize.
+            mNextRetryDurationMs = MIN_RETRY_DURATION_MS;
+            mRetryCount = 0;
+        }
+        mLastRetryTimeMs = now;
+        mHandler.postDelayed(mRetryRunnable, mNextRetryDurationMs);
+        mNextRetryDurationMs = Math.min(2 * mNextRetryDurationMs, MAX_RETRY_DURATION_MS);
+        mRetryCount++;
+    }
+
+    private boolean checkAndDeliverServiceDiedCbLocked() {
+
+       if (mRetryType == RETRY_NEVER || (mRetryType == RETRY_BEST_EFFORT
+                && mRetryCount >= MAX_RETRY_COUNT)) {
+            // If we never retry, or we've exhausted our retries, post the onServiceDied callback.
+            Slog.e(TAG, "Service " + mComponent + " has died too much, not retrying.");
+            if (mEventCb != null) {
+                final long timestamp = System.currentTimeMillis();
+                mHandler.post(() -> {
+                  mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent,
+                        LogEvent.EVENT_STOPPED_PERMANENTLY));
+                });
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private void doRetry() {
+        synchronized (mLock) {
+            if (mConnection == null) {
+                // We disconnected for good.  Don't attempt to retry.
+                return;
+            }
+            if (!mRetrying) {
+                // We successfully connected.  Don't attempt to retry.
+                return;
+            }
+            Slog.i(TAG, "Attempting to reconnect " + mComponent + "...");
+            // While frameworks may restart the remote Service if we stay bound, we have little
+            // control of the backoff timing for reconnecting the service.  In the event of a
+            // process crash, the backoff time can be very large (1-30 min), which is not
+            // acceptable for the types of services this is used for.  Instead force an unbind/bind
+            // sequence to cause a more immediate retry.
+            disconnect();
+            if (checkAndDeliverServiceDiedCbLocked()) {
+                // No more retries.
+                return;
+            }
+            queueRetryLocked();
+            connect();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index 5721415..95d03d4 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -296,6 +296,7 @@
             flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
             flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
             flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+            flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
             mVirtualDisplay = mDisplayManager.createVirtualDisplay(null /* projection */,
                     DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi,
                     null /* surface */, flags, null /* callback */, null /* handler */,
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 830ebda..1511aee3 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -66,6 +66,8 @@
 import com.android.server.SystemConfig;
 import com.android.server.SystemService;
 import com.android.server.utils.ManagedApplicationService.PendingEvent;
+import com.android.server.utils.ManagedApplicationService.LogEvent;
+import com.android.server.utils.ManagedApplicationService.LogFormattable;
 import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
 import com.android.server.utils.ManagedApplicationService;
 import com.android.server.utils.ManagedApplicationService.BinderChecker;
@@ -108,7 +110,7 @@
     static final boolean DBG = false;
 
     private static final int PENDING_STATE_DELAY_MS = 300;
-    private static final int EVENT_LOG_SIZE = 32;
+    private static final int EVENT_LOG_SIZE = 64;
     private static final int INVALID_APPOPS_MODE = -1;
     /** Null set of sleep sleep flags. */
     private static final int FLAG_NONE = 0;
@@ -134,6 +136,7 @@
     private int mVrAppProcessId;
     private EnabledComponentsObserver mComponentObserver;
     private ManagedApplicationService mCurrentVrService;
+    private ManagedApplicationService mCurrentVrCompositorService;
     private ComponentName mDefaultVrService;
     private Context mContext;
     private ComponentName mCurrentVrModeComponent;
@@ -147,7 +150,8 @@
     private int mPreviousCoarseLocationMode = INVALID_APPOPS_MODE;
     private int mPreviousManageOverlayMode = INVALID_APPOPS_MODE;
     private VrState mPendingState;
-    private final ArrayDeque<VrState> mLoggingDeque = new ArrayDeque<>(EVENT_LOG_SIZE);
+    private boolean mLogLimitHit;
+    private final ArrayDeque<LogFormattable> mLoggingDeque = new ArrayDeque<>(EVENT_LOG_SIZE);
     private final NotificationAccessManager mNotifAccessManager = new NotificationAccessManager();
     private INotificationManager mNotificationManager;
     /** Tracks the state of the screen and keyguard UI.*/
@@ -160,6 +164,30 @@
     private Vr2dDisplay mVr2dDisplay;
     private boolean mBootsToVr;
 
+    // Handles events from the managed services (e.g. VrListenerService and any bound VR compositor
+    // service).
+    private final ManagedApplicationService.EventCallback mEventCallback
+                = new ManagedApplicationService.EventCallback() {
+        @Override
+        public void onServiceEvent(LogEvent event) {
+            logEvent(event);
+
+            ComponentName component = null;
+            synchronized (mLock) {
+                component = ((mCurrentVrService == null) ? null : mCurrentVrService.getComponent());
+            }
+
+            // If not on an AIO device and we permanently stopped trying to connect to the
+            // VrListenerService (or don't have one bound), leave persistent VR mode and VR mode.
+            if (!mBootsToVr && event.event == LogEvent.EVENT_STOPPED_PERMANENTLY &&
+                    (component == null || component.equals(event.component))) {
+                Slog.e(TAG, "VrListenerSevice has died permanently, leaving system VR mode.");
+                // We're not a native VR device.  Leave VR + persistent mode.
+                setPersistentVrModeEnabled(false);
+            }
+        }
+    };
+
     private static final int MSG_VR_STATE_CHANGE = 0;
     private static final int MSG_PENDING_VR_STATE_CHANGE = 1;
     private static final int MSG_PERSISTENT_VR_MODE_STATE_CHANGE = 2;
@@ -276,7 +304,24 @@
         }
     };
 
-    private static class VrState {
+    // Event used to log when settings are changed for dumpsys logs.
+    private static class SettingEvent implements LogFormattable {
+        public final long timestamp;
+        public final String what;
+
+        SettingEvent(String what) {
+            this.timestamp = System.currentTimeMillis();
+            this.what = what;
+        }
+
+        @Override
+        public String toLogString(SimpleDateFormat dateFormat) {
+            return dateFormat.format(new Date(timestamp)) + "   " + what;
+        }
+    }
+
+    // Event used to track changes of the primary on-screen VR activity.
+    private static class VrState implements LogFormattable {
         final boolean enabled;
         final boolean running2dInVr;
         final int userId;
@@ -286,7 +331,6 @@
         final long timestamp;
         final boolean defaultPermissionsGranted;
 
-
         VrState(boolean enabled, boolean running2dInVr, ComponentName targetPackageName, int userId,
                 int processId, ComponentName callingPackage) {
             this.enabled = enabled;
@@ -310,6 +354,39 @@
             this.defaultPermissionsGranted = defaultPermissionsGranted;
             this.timestamp = System.currentTimeMillis();
         }
+
+        @Override
+        public String toLogString(SimpleDateFormat dateFormat) {
+            String tab = "  ";
+            String newLine = "\n";
+            StringBuilder sb = new StringBuilder(dateFormat.format(new Date(timestamp)));
+            sb.append(tab);
+            sb.append("State changed to:");
+            sb.append(tab);
+            sb.append((enabled) ? "ENABLED" : "DISABLED");
+            sb.append(newLine);
+            if (enabled) {
+                sb.append(tab);
+                sb.append("User=");
+                sb.append(userId);
+                sb.append(newLine);
+                sb.append(tab);
+                sb.append("Current VR Activity=");
+                sb.append((callingPackage == null) ? "None" : callingPackage.flattenToString());
+                sb.append(newLine);
+                sb.append(tab);
+                sb.append("Bound VrListenerService=");
+                sb.append((targetPackageName == null) ? "None"
+                        : targetPackageName.flattenToString());
+                sb.append(newLine);
+                if (defaultPermissionsGranted) {
+                    sb.append(tab);
+                    sb.append("Default permissions granted to the bound VrListenerService.");
+                    sb.append(newLine);
+                }
+            }
+            return sb.toString();
+        }
     }
 
     private static final BinderChecker sBinderChecker = new BinderChecker() {
@@ -490,6 +567,13 @@
         }
 
         @Override
+        public void setAndBindCompositor(String componentName) {
+            enforceCallerPermissionAnyOf(Manifest.permission.RESTRICTED_VR_ACCESS);
+            VrManagerService.this.setAndBindCompositor(
+                (componentName == null) ? null : ComponentName.unflattenFromString(componentName));
+        }
+
+        @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
@@ -497,6 +581,12 @@
             pw.println("VR mode is currently: " + ((mVrModeAllowed) ? "allowed" : "disallowed"));
             pw.println("Persistent VR mode is currently: " +
                     ((mPersistentVrModeEnabled) ? "enabled" : "disabled"));
+            pw.println("Currently bound VR listener service: "
+                    + ((mCurrentVrService == null)
+                    ? "None" : mCurrentVrService.getComponent().flattenToString()));
+            pw.println("Currently bound VR compositor service: "
+                    + ((mCurrentVrCompositorService == null)
+                    ? "None" : mCurrentVrCompositorService.getComponent().flattenToString()));
             pw.println("Previous state transitions:\n");
             String tab = "  ";
             dumpStateTransitions(pw);
@@ -785,6 +875,7 @@
                         + mCurrentVrService.getComponent() + " for user "
                         + mCurrentVrService.getUserId());
                     mCurrentVrService.disconnect();
+                    updateCompositorServiceLocked(UserHandle.USER_NULL, null);
                     mCurrentVrService = null;
                 } else {
                     nothingChanged = true;
@@ -798,6 +889,7 @@
                         Slog.i(TAG, "VR mode component changed to " + component
                             + ", disconnecting " + mCurrentVrService.getComponent()
                             + " for user " + mCurrentVrService.getUserId());
+                        updateCompositorServiceLocked(UserHandle.USER_NULL, null);
                         createAndConnectService(component, userId);
                         sendUpdatedCaller = true;
                     } else {
@@ -985,7 +1077,7 @@
 
 
     private void createAndConnectService(@NonNull ComponentName component, int userId) {
-        mCurrentVrService = VrManagerService.create(mContext, component, userId);
+        mCurrentVrService = createVrListenerService(component, userId);
         mCurrentVrService.connect();
         Slog.i(TAG, "Connecting " + component + " for user " + userId);
     }
@@ -1020,13 +1112,27 @@
     }
 
     /**
-     * Helper function for making ManagedApplicationService instances.
+     * Helper function for making ManagedApplicationService for VrListenerService instances.
      */
-    private static ManagedApplicationService create(@NonNull Context context,
-            @NonNull ComponentName component, int userId) {
-        return ManagedApplicationService.build(context, component, userId,
+    private ManagedApplicationService createVrListenerService(@NonNull ComponentName component,
+            int userId) {
+        int retryType = (mBootsToVr) ? ManagedApplicationService.RETRY_FOREVER
+                : ManagedApplicationService.RETRY_NEVER;
+        return ManagedApplicationService.build(mContext, component, userId,
                 R.string.vr_listener_binding_label, Settings.ACTION_VR_LISTENER_SETTINGS,
-                sBinderChecker, /*isImportant*/true);
+                sBinderChecker, /*isImportant*/true, retryType, mHandler, mEventCallback);
+    }
+
+    /**
+     * Helper function for making ManagedApplicationService for VR Compositor instances.
+     */
+    private ManagedApplicationService createVrCompositorService(@NonNull ComponentName component,
+            int userId) {
+        int retryType = (mBootsToVr) ? ManagedApplicationService.RETRY_FOREVER
+                : ManagedApplicationService.RETRY_BEST_EFFORT;
+        return ManagedApplicationService.build(mContext, component, userId, /*clientLabel*/0,
+                /*settingsAction*/null, /*binderChecker*/null, /*isImportant*/true, retryType,
+                mHandler, /*disconnectCallback*/mEventCallback);
     }
 
     /**
@@ -1057,44 +1163,35 @@
 
     private void logStateLocked() {
         ComponentName currentBoundService = (mCurrentVrService == null) ? null :
-            mCurrentVrService.getComponent();
-        VrState current = new VrState(mVrModeEnabled, mRunning2dInVr, currentBoundService,
-            mCurrentVrModeUser, mVrAppProcessId, mCurrentVrModeComponent, mWasDefaultGranted);
-        if (mLoggingDeque.size() == EVENT_LOG_SIZE) {
-            mLoggingDeque.removeFirst();
+                mCurrentVrService.getComponent();
+        logEvent(new VrState(mVrModeEnabled, mRunning2dInVr, currentBoundService,
+                mCurrentVrModeUser, mVrAppProcessId, mCurrentVrModeComponent, mWasDefaultGranted));
+    }
+
+    private void logEvent(LogFormattable event) {
+        synchronized (mLoggingDeque) {
+            if (mLoggingDeque.size() == EVENT_LOG_SIZE) {
+                mLoggingDeque.removeFirst();
+                mLogLimitHit = true;
+            }
+            mLoggingDeque.add(event);
         }
-        mLoggingDeque.add(current);
     }
 
     private void dumpStateTransitions(PrintWriter pw) {
         SimpleDateFormat d = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
-        String tab = "  ";
-        if (mLoggingDeque.size() == 0) {
-            pw.print(tab);
-            pw.println("None");
-        }
-        for (VrState state : mLoggingDeque) {
-            pw.print(d.format(new Date(state.timestamp)));
-            pw.print(tab);
-            pw.print("State changed to:");
-            pw.print(tab);
-            pw.println((state.enabled) ? "ENABLED" : "DISABLED");
-            if (state.enabled) {
-                pw.print(tab);
-                pw.print("User=");
-                pw.println(state.userId);
-                pw.print(tab);
-                pw.print("Current VR Activity=");
-                pw.println((state.callingPackage == null) ?
-                    "None" : state.callingPackage.flattenToString());
-                pw.print(tab);
-                pw.print("Bound VrListenerService=");
-                pw.println((state.targetPackageName == null) ?
-                    "None" : state.targetPackageName.flattenToString());
-                if (state.defaultPermissionsGranted) {
-                    pw.print(tab);
-                    pw.println("Default permissions granted to the bound VrListenerService.");
-                }
+        synchronized (mLoggingDeque) {
+            if (mLoggingDeque.size() == 0) {
+                pw.print("  ");
+                pw.println("None");
+            }
+
+            if (mLogLimitHit) {
+                pw.println("..."); // Indicates log overflow
+            }
+
+            for (LogFormattable event : mLoggingDeque) {
+                pw.println(event.toLogString(d));
             }
         }
     }
@@ -1177,10 +1274,41 @@
         return INVALID_DISPLAY;
     }
 
+    private void setAndBindCompositor(ComponentName componentName) {
+        final int userId = UserHandle.getCallingUserId();
+        final long token = Binder.clearCallingIdentity();
+        synchronized (mLock) {
+            updateCompositorServiceLocked(userId, componentName);
+        }
+        Binder.restoreCallingIdentity(token);
+    }
+
+    private void updateCompositorServiceLocked(int userId, ComponentName componentName) {
+        if (mCurrentVrCompositorService != null
+                && mCurrentVrCompositorService.disconnectIfNotMatching(componentName, userId)) {
+            Slog.i(TAG, "Disconnecting compositor service: "
+                    + mCurrentVrCompositorService.getComponent());
+            // Check if existing service matches the requested one, if not (or if the requested
+            // component is null) disconnect it.
+            mCurrentVrCompositorService = null;
+        }
+
+        if (componentName != null && mCurrentVrCompositorService == null) {
+            // We don't have an existing service matching the requested component, so attempt to
+            // connect one.
+            Slog.i(TAG, "Connecting compositor service: " + componentName);
+            mCurrentVrCompositorService = createVrCompositorService(componentName, userId);
+            mCurrentVrCompositorService.connect();
+        }
+    }
+
     private void setPersistentModeAndNotifyListenersLocked(boolean enabled) {
         if (mPersistentVrModeEnabled == enabled) {
             return;
         }
+        String eventName = "Persistent VR mode " + ((enabled) ? "enabled" : "disabled");
+        Slog.i(TAG, eventName);
+        logEvent(new SettingEvent(eventName));
         mPersistentVrModeEnabled = enabled;
 
         mHandler.sendMessage(mHandler.obtainMessage(MSG_PERSISTENT_VR_MODE_STATE_CHANGE,
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index c76b905..5365e27 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -16,10 +16,10 @@
 
 package com.android.server.wm;
 
-import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -78,11 +78,8 @@
     // requires that the duration of the two animations are the same.
     SurfaceControl thumbnail;
     int thumbnailTransactionSeq;
-    // TODO(b/62029108): combine both members into a private one. Create a member function to set
-    // the thumbnail layer to +1 to the highest layer position and replace all setter instances
-    // with this function. Remove all unnecessary calls to both variables in other classes.
-    int thumbnailLayer;
-    int thumbnailForceAboveLayer;
+    private int mThumbnailLayer;
+
     Animation thumbnailAnimation;
     final Transformation thumbnailTransformation = new Transformation();
     // This flag indicates that the destruction of the thumbnail surface is synchronized with
@@ -256,7 +253,7 @@
 
     private void updateLayers() {
         mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
-        thumbnailLayer = mAppToken.getHighestAnimLayer();
+        updateThumbnailLayer();
     }
 
     private void stepThumbnailAnimation(long currentTime) {
@@ -280,26 +277,35 @@
         thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
         if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
                 "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
-                + " layer=" + thumbnailLayer
+                + " layer=" + mThumbnailLayer
                 + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
                 + "," + tmpFloats[Matrix.MSKEW_Y]
                 + "][" + tmpFloats[Matrix.MSKEW_X]
                 + "," + tmpFloats[Matrix.MSCALE_Y] + "]");
         thumbnail.setAlpha(thumbnailTransformation.getAlpha());
-        if (thumbnailForceAboveLayer > 0) {
-            thumbnail.setLayer(thumbnailForceAboveLayer + 1);
-        } else {
-            // The thumbnail is layered below the window immediately above this
-            // token's anim layer.
-            thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
-                    - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
-        }
+        updateThumbnailLayer();
         thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
                 tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
         thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
     }
 
     /**
+     * Updates the thumbnail layer z order to just above the highest animation layer if changed
+     */
+    void updateThumbnailLayer() {
+        if (thumbnail != null) {
+            final int layer = mAppToken.getHighestAnimLayer();
+            if (layer != mThumbnailLayer) {
+                if (DEBUG_LAYERS) Slog.v(TAG,
+                        "Setting thumbnail layer " + mAppToken + ": layer=" + layer);
+                thumbnail.setLayer(layer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
+                        - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
+                mThumbnailLayer = layer;
+            }
+        }
+    }
+
+    /**
      * Sometimes we need to synchronize the first frame of animation with some external event, e.g.
      * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
      * and keep producing the first frame of the animation.
@@ -473,7 +479,7 @@
         }
         if (thumbnail != null) {
             pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
-                    pw.print(" layer="); pw.println(thumbnailLayer);
+                    pw.print(" layer="); pw.println(mThumbnailLayer);
             pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
             pw.print(prefix); pw.print("thumbnailTransformation=");
                     pw.println(thumbnailTransformation.toShortString());
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 78f8157..a1eeff8 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1170,7 +1170,6 @@
                 wAppAnimator.thumbnail.destroy();
             }
             wAppAnimator.thumbnail = tAppAnimator.thumbnail;
-            wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
             wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
             tAppAnimator.thumbnail = null;
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index bdf46c7..0e68a8f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -392,10 +392,6 @@
                     != mTmpWindowAnimator.mAnimTransactionSequence) {
                 appAnimator.thumbnailTransactionSeq =
                         mTmpWindowAnimator.mAnimTransactionSequence;
-                appAnimator.thumbnailLayer = 0;
-            }
-            if (appAnimator.thumbnailLayer < winAnimator.mAnimLayer) {
-                appAnimator.thumbnailLayer = winAnimator.mAnimLayer;
             }
         }
     };
@@ -969,10 +965,10 @@
         final int lastOrientation = mLastOrientation;
         final boolean oldAltOrientation = mAltOrientation;
         int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation);
-        final boolean rotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
+        boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
                 rotation);
 
-        if (rotateSeamlessly) {
+        if (mayRotateSeamlessly) {
             final WindowState seamlessRotated = getWindow((w) -> w.mSeamlesslyRotated);
             if (seamlessRotated != null) {
                 // We can't rotate (seamlessly or not) while waiting for the last seamless rotation
@@ -981,7 +977,20 @@
                 // window-removal.
                 return false;
             }
+
+            // In the presence of the PINNED stack or System Alert
+            // windows we unforuntately can not seamlessly rotate.
+            if (getStackById(PINNED_STACK_ID) != null) {
+                mayRotateSeamlessly = false;
+            }
+            for (int i = 0; i < mService.mSessions.size(); i++) {
+                if (mService.mSessions.valueAt(i).hasAlertWindowSurfaces()) {
+                    mayRotateSeamlessly = false;
+                    break;
+                }
+            }
         }
+        final boolean rotateSeamlessly = mayRotateSeamlessly;
 
         // TODO: Implement forced rotation changes.
         //       Set mAltOrientation to indicate that the application is receiving
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 2215f20..41f076d 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -37,8 +37,8 @@
     private Rect mTmpToBounds = new Rect();
 
     public PinnedStackWindowController(int stackId, PinnedStackWindowListener listener,
-            int displayId, boolean onTop, Rect outBounds) {
-        super(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
+            int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
+        super(stackId, listener, displayId, onTop, outBounds, service);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1781247..4dd147e 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -719,4 +719,8 @@
     public String toString() {
         return mStringName;
     }
+
+    boolean hasAlertWindowSurfaces() {
+        return !mAlertWindowSurfaces.isEmpty();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index fdd2ade..7caf2fe 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -266,20 +266,8 @@
         w.mLayer = layer;
         w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
                 + w.getSpecialWindowAnimLayerAdjustment();
-        if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0) {
-            if (w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
-                w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
-            }
-            // TODO(b/62029108): the entire contents of the if statement should call the refactored
-            // function to set the thumbnail layer for w.AppToken
-            int highestLayer = w.mAppToken.getHighestAnimLayer();
-            if (highestLayer > 0) {
-                if (w.mAppToken.mAppAnimator.thumbnail != null
-                        && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer != highestLayer) {
-                    w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = highestLayer;
-                    w.mAppToken.mAppAnimator.thumbnail.setLayer(highestLayer + 1);
-                }
-            }
+        if (w.mAppToken != null) {
+            w.mAppToken.mAppAnimator.updateThumbnailLayer();
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 88625d3..af1fa2f 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -342,10 +342,7 @@
         mTmpLayerAndToken.token = null;
         handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken);
         final AppWindowToken topClosingApp = mTmpLayerAndToken.token;
-        final int topClosingLayer = mTmpLayerAndToken.layer;
-
-        final AppWindowToken topOpeningApp = handleOpeningApps(transit,
-                animLp, voiceInteraction, topClosingLayer);
+        final AppWindowToken topOpeningApp = handleOpeningApps(transit, animLp, voiceInteraction);
 
         mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp);
 
@@ -387,8 +384,9 @@
     }
 
     private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp,
-            boolean voiceInteraction, int topClosingLayer) {
+            boolean voiceInteraction) {
         AppWindowToken topOpeningApp = null;
+        int topOpeningLayer = Integer.MIN_VALUE;
         final int appsCount = mService.mOpeningApps.size();
         for (int i = 0; i < appsCount; i++) {
             AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
@@ -422,7 +420,6 @@
             }
             mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
 
-            int topOpeningLayer = 0;
             if (animLp != null) {
                 final int layer = wtoken.getHighestAnimLayer();
                 if (topOpeningApp == null || layer > topOpeningLayer) {
@@ -431,7 +428,7 @@
                 }
             }
             if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) {
-                createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
+                createThumbnailAppAnimator(transit, wtoken);
             }
         }
         return topOpeningApp;
@@ -473,7 +470,7 @@
                 }
             }
             if (mService.mAppTransition.isNextAppTransitionThumbnailDown()) {
-                createThumbnailAppAnimator(transit, wtoken, 0, layerAndToken.layer);
+                createThumbnailAppAnimator(transit, wtoken);
             }
         }
     }
@@ -666,8 +663,7 @@
         }
     }
 
-    private void createThumbnailAppAnimator(int transit, AppWindowToken appToken,
-            int openingLayer, int closingLayer) {
+    private void createThumbnailAppAnimator(int transit, AppWindowToken appToken) {
         AppWindowAnimator openingAppAnimator = (appToken == null) ? null : appToken.mAppAnimator;
         if (openingAppAnimator == null || openingAppAnimator.animation == null) {
             return;
@@ -724,7 +720,6 @@
                 anim = mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect,
                         insets, thumbnailHeader, taskId, displayConfig.uiMode,
                         displayConfig.orientation);
-                openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
                 openingAppAnimator.deferThumbnailDestruction =
                         !mService.mAppTransition.isNextThumbnailTransitionScaleUp();
             } else {
@@ -734,8 +729,8 @@
             anim.restrictDuration(MAX_ANIMATION_DURATION);
             anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
 
+            openingAppAnimator.updateThumbnailLayer();
             openingAppAnimator.thumbnail = surfaceControl;
-            openingAppAnimator.thumbnailLayer = openingLayer;
             openingAppAnimator.thumbnailAnimation = anim;
             mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
         } catch (Surface.OutOfResourcesException e) {
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 9655637..36e9e7f 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -92,6 +92,7 @@
     android.hardware.audio.common@2.0 \
     android.hardware.broadcastradio@1.0 \
     android.hardware.broadcastradio@1.1 \
+    android.hardware.broadcastradio@1.2 \
     android.hardware.contexthub@1.0 \
     android.hardware.gnss@1.0 \
     android.hardware.ir@1.0 \
@@ -109,5 +110,5 @@
     android.frameworks.sensorservice@1.0 \
 
 LOCAL_STATIC_LIBRARIES += \
-    android.hardware.broadcastradio@1.1-utils-lib \
+    android.hardware.broadcastradio@common-utils-lib \
     libscrypt_static \
diff --git a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
index b3817db..e1dbdeb 100644
--- a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
+++ b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
@@ -23,8 +23,9 @@
 #include "convert.h"
 
 #include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
-#include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
+#include <android/hardware/broadcastradio/1.2/IBroadcastRadioFactory.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
+#include <broadcastradio-utils/Utils.h>
 #include <core_jni_helpers.h>
 #include <hidl/ServiceManagement.h>
 #include <nativehelper/JNIHelp.h>
@@ -44,14 +45,16 @@
 
 namespace V1_0 = hardware::broadcastradio::V1_0;
 namespace V1_1 = hardware::broadcastradio::V1_1;
-
-using V1_0::Class;
-using V1_0::Result;
+namespace V1_2 = hardware::broadcastradio::V1_2;
+namespace utils = hardware::broadcastradio::utils;
 
 using V1_0::BandConfig;
-using V1_0::ProgramInfo;
-using V1_0::MetaData;
+using V1_0::Class;
 using V1_0::ITuner;
+using V1_0::MetaData;
+using V1_0::ProgramInfo;
+using V1_0::Result;
+using utils::HalRevision;
 
 static mutex gContextMutex;
 
@@ -67,10 +70,15 @@
     } Tuner;
 } gjni;
 
+struct Module {
+    sp<V1_0::IBroadcastRadio> radioModule;
+    HalRevision halRev;
+};
+
 struct ServiceContext {
     ServiceContext() {}
 
-    std::vector<sp<V1_0::IBroadcastRadio>> mModules;
+    std::vector<Module> mModules;
 
 private:
     DISALLOW_COPY_AND_ASSIGN(ServiceContext);
@@ -139,6 +147,17 @@
             continue;
         }
 
+        auto halRev = HalRevision::V1_0;
+        auto halMinor = 0;
+        if (V1_2::IBroadcastRadioFactory::castFrom(factory).withDefault(nullptr) != nullptr) {
+            halRev = HalRevision::V1_2;
+            halMinor = 2;
+        } else if (V1_1::IBroadcastRadioFactory::castFrom(factory).withDefault(nullptr)
+                != nullptr) {
+            halRev = HalRevision::V1_1;
+            halMinor = 1;
+        }
+
         // Second level of scanning - that's unfortunate.
         for (auto&& clazz : gAllClasses) {
             sp<V1_0::IBroadcastRadio> module10 = nullptr;
@@ -155,9 +174,9 @@
             if (module10 == nullptr) continue;
 
             auto idx = ctx.mModules.size();
-            ctx.mModules.push_back(module10);
-            ALOGI("loaded broadcast radio module %zu: %s:%s",
-                    idx, serviceName.c_str(), V1_0::toString(clazz).c_str());
+            ctx.mModules.push_back({module10, halRev});
+            ALOGI("loaded broadcast radio module %zu: %s:%s (HAL 1.%d)",
+                    idx, serviceName.c_str(), V1_0::toString(clazz).c_str(), halMinor);
 
             JavaRef<jobject> jModule = nullptr;
             Result halResult = Result::OK;
@@ -198,22 +217,15 @@
         ALOGE("Invalid module ID: %d", moduleId);
         return nullptr;
     }
-    auto module = ctx.mModules[moduleId];
 
-    HalRevision halRev;
-    if (V1_1::IBroadcastRadio::castFrom(module).withDefault(nullptr) != nullptr) {
-        ALOGI("Opening tuner %d with broadcast radio HAL 1.1", moduleId);
-        halRev = HalRevision::V1_1;
-    } else {
-        ALOGI("Opening tuner %d with broadcast radio HAL 1.0", moduleId);
-        halRev = HalRevision::V1_0;
-    }
+    ALOGI("Opening tuner %d", moduleId);
+    auto module = ctx.mModules[moduleId];
 
     Region region;
     BandConfig bandConfigHal = convert::BandConfigToHal(env, bandConfig, region);
 
     auto tuner = make_javaref(env, env->NewObject(gjni.Tuner.clazz, gjni.Tuner.cstor,
-            callback, halRev, region, withAudio, bandConfigHal.type));
+            callback, module.halRev, region, withAudio, bandConfigHal.type));
     if (tuner == nullptr) {
         ALOGE("Unable to create new tuner object.");
         return nullptr;
@@ -223,7 +235,7 @@
     Result halResult;
     sp<ITuner> halTuner = nullptr;
 
-    auto hidlResult = module->openTuner(bandConfigHal, withAudio, tunerCb,
+    auto hidlResult = module.radioModule->openTuner(bandConfigHal, withAudio, tunerCb,
             [&](Result result, const sp<ITuner>& tuner) {
                 halResult = result;
                 halTuner = tuner;
@@ -235,7 +247,7 @@
         return nullptr;
     }
 
-    Tuner::assignHalInterfaces(env, tuner, module, halTuner);
+    Tuner::assignHalInterfaces(env, tuner, module.radioModule, halTuner);
     ALOGD("Opened tuner %p", halTuner.get());
     return tuner.release();
 }
diff --git a/services/core/jni/BroadcastRadio/Tuner.cpp b/services/core/jni/BroadcastRadio/Tuner.cpp
index e1ade4d..6403a5a 100644
--- a/services/core/jni/BroadcastRadio/Tuner.cpp
+++ b/services/core/jni/BroadcastRadio/Tuner.cpp
@@ -22,7 +22,7 @@
 #include "convert.h"
 #include "TunerCallback.h"
 
-#include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
+#include <android/hardware/broadcastradio/1.2/IBroadcastRadioFactory.h>
 #include <binder/IPCThreadState.h>
 #include <broadcastradio-utils/Utils.h>
 #include <core_jni_helpers.h>
@@ -44,13 +44,16 @@
 
 namespace V1_0 = hardware::broadcastradio::V1_0;
 namespace V1_1 = hardware::broadcastradio::V1_1;
+namespace V1_2 = hardware::broadcastradio::V1_2;
+namespace utils = hardware::broadcastradio::utils;
 
 using V1_0::Band;
 using V1_0::BandConfig;
 using V1_0::MetaData;
 using V1_0::Result;
-using V1_1::ITunerCallback;
+using V1_2::ITunerCallback;
 using V1_1::ProgramListResult;
+using utils::HalRevision;
 
 static mutex gContextMutex;
 
@@ -310,7 +313,7 @@
         convert::ThrowIfFailed(env, halTuner11->tuneByProgramSelector(selector));
     } else {
         uint32_t channel, subChannel;
-        if (!V1_1::utils::getLegacyChannel(selector, &channel, &subChannel)) {
+        if (!utils::getLegacyChannel(selector, &channel, &subChannel)) {
             jniThrowException(env, "java/lang/IllegalArgumentException",
                     "Can't tune to non-AM/FM channel with HAL<1.1");
             return;
diff --git a/services/core/jni/BroadcastRadio/Tuner.h b/services/core/jni/BroadcastRadio/Tuner.h
index 818597b..48c3bc7 100644
--- a/services/core/jni/BroadcastRadio/Tuner.h
+++ b/services/core/jni/BroadcastRadio/Tuner.h
@@ -22,8 +22,8 @@
 #include "JavaRef.h"
 
 #include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
-#include <android/hardware/broadcastradio/1.1/ITuner.h>
-#include <android/hardware/broadcastradio/1.1/ITunerCallback.h>
+#include <android/hardware/broadcastradio/1.2/ITuner.h>
+#include <android/hardware/broadcastradio/1.2/ITunerCallback.h>
 #include <jni.h>
 #include <utils/StrongPointer.h>
 
@@ -39,7 +39,7 @@
         sp<hardware::broadcastradio::V1_0::IBroadcastRadio> halModule,
         sp<hardware::broadcastradio::V1_0::ITuner> halTuner);
 
-sp<hardware::broadcastradio::V1_1::ITunerCallback>
+sp<hardware::broadcastradio::V1_2::ITunerCallback>
 getNativeCallback(JNIEnv *env, JavaRef<jobject> const &tuner);
 
 Region getRegion(JNIEnv *env, jobject obj);
diff --git a/services/core/jni/BroadcastRadio/TunerCallback.cpp b/services/core/jni/BroadcastRadio/TunerCallback.cpp
index d53721f..ed7c9c4 100644
--- a/services/core/jni/BroadcastRadio/TunerCallback.cpp
+++ b/services/core/jni/BroadcastRadio/TunerCallback.cpp
@@ -40,15 +40,19 @@
 
 namespace V1_0 = hardware::broadcastradio::V1_0;
 namespace V1_1 = hardware::broadcastradio::V1_1;
+namespace V1_2 = hardware::broadcastradio::V1_2;
+namespace utils = hardware::broadcastradio::utils;
 
 using V1_0::Band;
 using V1_0::BandConfig;
 using V1_0::MetaData;
 using V1_0::Result;
-using V1_1::ITunerCallback;
 using V1_1::ProgramInfo;
 using V1_1::ProgramListResult;
 using V1_1::ProgramSelector;
+using V1_1::VendorKeyValue;
+using V1_2::ITunerCallback;
+using utils::HalRevision;
 
 static JavaVM *gvm = nullptr;
 
@@ -117,6 +121,7 @@
     virtual Return<void> backgroundScanComplete(ProgramListResult result);
     virtual Return<void> programListChanged();
     virtual Return<void> currentProgramInfoChanged(const ProgramInfo& info);
+    virtual Return<void> parametersUpdated(const hidl_vec<VendorKeyValue>& parameters);
 };
 
 struct TunerCallbackContext {
@@ -203,7 +208,7 @@
         return {};
     }
 
-    auto selector = V1_1::utils::make_selector(mBand, info.channel, info.subChannel);
+    auto selector = utils::make_selector(mBand, info.channel, info.subChannel);
     return tuneComplete_1_1(result, selector);
 }
 
@@ -338,6 +343,14 @@
     return Return<void>();
 }
 
+Return<void> NativeCallback::parametersUpdated(const hidl_vec<VendorKeyValue>& parameters) {
+    ALOGV("%s", __func__);
+
+    // TODO(b/65862441): pass this callback to the front-end
+
+    return {};
+}
+
 static TunerCallbackContext& getNativeContext(jlong nativeContextHandle) {
     auto nativeContext = reinterpret_cast<TunerCallbackContext*>(nativeContextHandle);
     LOG_ALWAYS_FATAL_IF(nativeContext == nullptr, "Native context not initialized");
diff --git a/services/core/jni/BroadcastRadio/TunerCallback.h b/services/core/jni/BroadcastRadio/TunerCallback.h
index af12d21..7e776c2 100644
--- a/services/core/jni/BroadcastRadio/TunerCallback.h
+++ b/services/core/jni/BroadcastRadio/TunerCallback.h
@@ -21,7 +21,7 @@
 #include "NativeCallbackThread.h"
 #include "types.h"
 
-#include <android/hardware/broadcastradio/1.1/ITunerCallback.h>
+#include <android/hardware/broadcastradio/1.2/ITunerCallback.h>
 #include <jni.h>
 
 namespace android {
@@ -32,7 +32,7 @@
 namespace BroadcastRadio {
 namespace TunerCallback {
 
-sp<hardware::broadcastradio::V1_1::ITunerCallback>
+sp<hardware::broadcastradio::V1_2::ITunerCallback>
 getNativeCallback(JNIEnv *env, jobject jTunerCallback);
 
 } // namespace TunerCallback
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index ae278de..8dfa14f 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -31,7 +31,7 @@
 namespace BroadcastRadio {
 namespace convert {
 
-namespace utils = V1_1::utils;
+namespace utils = hardware::broadcastradio::utils;
 
 using hardware::Return;
 using hardware::hidl_vec;
diff --git a/services/core/jni/BroadcastRadio/regions.cpp b/services/core/jni/BroadcastRadio/regions.cpp
index b856419..b7fd0f3 100644
--- a/services/core/jni/BroadcastRadio/regions.cpp
+++ b/services/core/jni/BroadcastRadio/regions.cpp
@@ -27,7 +27,7 @@
 namespace BroadcastRadio {
 namespace regions {
 
-namespace utils = hardware::broadcastradio::V1_1::utils;
+namespace utils = hardware::broadcastradio::utils;
 
 using hardware::hidl_vec;
 
diff --git a/services/core/jni/BroadcastRadio/types.h b/services/core/jni/BroadcastRadio/types.h
index f726af3..64a4f63 100644
--- a/services/core/jni/BroadcastRadio/types.h
+++ b/services/core/jni/BroadcastRadio/types.h
@@ -27,11 +27,6 @@
  * frameworks/base/core/java/android/hardware/radio/RadioManager.java.
  */
 
-enum class HalRevision : jint {
-    V1_0,
-    V1_1,
-};
-
 // Keep in sync with STATUS_* constants from RadioManager.java.
 enum class Status : jint {
     OK = 0,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7830623..c59f44e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -97,8 +97,8 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ResolveInfo;
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index 6065268..5a3a8be 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -107,6 +107,20 @@
     public static final int RFC6177_MIN_PREFIX_LENGTH = 48;
 
     /**
+     * ICMP common (v4/v6) constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc792
+     *     - https://tools.ietf.org/html/rfc4443
+     */
+    public static final int ICMP_HEADER_TYPE_OFFSET = 0;
+    public static final int ICMP_HEADER_CODE_OFFSET = 1;
+    public static final int ICMP_HEADER_CHECKSUM_OFFSET = 2;
+    public static final int ICMP_ECHO_IDENTIFIER_OFFSET = 4;
+    public static final int ICMP_ECHO_SEQUENCE_NUMBER_OFFSET = 6;
+    public static final int ICMP_ECHO_DATA_OFFSET = 8;
+
+    /**
      * ICMPv6 constants.
      *
      * See also:
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 2249ff0..df989f7 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -25,25 +25,13 @@
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.fail;
 
-import org.json.JSONArray;
-import org.json.JSONObject;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import com.android.internal.util.FastXmlSerializer;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
-
 import android.app.Notification;
-import android.app.NotificationChannelGroup;
-import android.content.Context;
 import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.IContentProvider;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
@@ -52,14 +40,28 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContentResolver;
 import android.util.ArrayMap;
 import android.util.Xml;
 
+import com.android.internal.util.FastXmlSerializer;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
@@ -76,6 +78,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
@@ -95,10 +98,17 @@
     private static final int UID2 = 1111;
     private static final UserHandle USER2 = UserHandle.of(10);
     private static final String TEST_CHANNEL_ID = "test_channel_id";
+    private static final String TEST_AUTHORITY = "test";
+    private static final Uri SOUND_URI =
+            Uri.parse("content://" + TEST_AUTHORITY + "/internal/audio/media/10");
+    private static final Uri CANONICAL_SOUND_URI =
+            Uri.parse("content://" + TEST_AUTHORITY
+                    + "/internal/audio/media/10?title=Test&canonical=1");
 
     @Mock NotificationUsageStats mUsageStats;
     @Mock RankingHandler mHandler;
     @Mock PackageManager mPm;
+    @Mock IContentProvider mTestIContentProvider;
     @Mock Context mContext;
 
     private Notification mNotiGroupGSortA;
@@ -134,9 +144,22 @@
         when(mContext.getPackageManager()).thenReturn(mPm);
         when(mContext.getApplicationInfo()).thenReturn(legacy);
         // most tests assume badging is enabled
-        Secure.putIntForUser(getContext().getContentResolver(),
+        TestableContentResolver contentResolver = getContext().getContentResolver();
+        contentResolver.setFallbackToExisting(false);
+        Secure.putIntForUser(contentResolver,
                 Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID));
 
+        ContentProvider testContentProvider = mock(ContentProvider.class);
+        when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
+        contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
+
+        when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI)))
+                .thenReturn(CANONICAL_SOUND_URI);
+        when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+                .thenReturn(CANONICAL_SOUND_URI);
+        when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+                .thenReturn(SOUND_URI);
+
         mHelper = new RankingHelper(getContext(), mPm, mHandler, mUsageStats,
                 new String[] {ImportanceExtractor.class.getName()});
 
@@ -214,9 +237,12 @@
     }
 
     private void loadStreamXml(ByteArrayOutputStream stream, boolean forRestore) throws Exception {
+        loadByteArrayXml(stream.toByteArray(), forRestore);
+    }
+
+    private void loadByteArrayXml(byte[] byteArray, boolean forRestore) throws Exception {
         XmlPullParser parser = Xml.newPullParser();
-        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(stream.toByteArray())),
-                null);
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
         parser.nextTag();
         mHelper.readXml(parser, forRestore);
     }
@@ -377,7 +403,7 @@
         NotificationChannel channel2 =
                 new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
         channel2.setDescription("descriptions for all");
-        channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+        channel2.setSound(SOUND_URI, mAudioAttributes);
         channel2.enableLights(true);
         channel2.setBypassDnd(true);
         channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
@@ -439,6 +465,109 @@
     }
 
     @Test
+    public void testBackupXml_backupCanonicalizedSoundUri() throws Exception {
+        NotificationChannel channel =
+                new NotificationChannel("id", "name", IMPORTANCE_LOW);
+        channel.setSound(SOUND_URI, mAudioAttributes);
+        mHelper.createNotificationChannel(PKG, UID, channel, true);
+
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+        // Testing that in restore we are given the canonical version
+        loadStreamXml(baos, true);
+        verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
+    }
+
+    @Test
+    public void testRestoreXml_withExistentCanonicalizedSoundUri() throws Exception {
+        Uri localUri = Uri.parse("content://" + TEST_AUTHORITY + "/local/url");
+        Uri canonicalBasedOnLocal = localUri.buildUpon()
+                .appendQueryParameter("title", "Test")
+                .appendQueryParameter("canonical", "1")
+                .build();
+        when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+                .thenReturn(canonicalBasedOnLocal);
+        when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+                .thenReturn(localUri);
+        when(mTestIContentProvider.uncanonicalize(any(), eq(canonicalBasedOnLocal)))
+                .thenReturn(localUri);
+
+        NotificationChannel channel =
+                new NotificationChannel("id", "name", IMPORTANCE_LOW);
+        channel.setSound(SOUND_URI, mAudioAttributes);
+        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+        loadStreamXml(baos, true);
+
+        NotificationChannel actualChannel = mHelper.getNotificationChannel(
+                PKG, UID, channel.getId(), false);
+        assertEquals(localUri, actualChannel.getSound());
+    }
+
+    @Test
+    public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
+        Thread.sleep(3000);
+        when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+                .thenReturn(null);
+        when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+                .thenReturn(null);
+
+        NotificationChannel channel =
+                new NotificationChannel("id", "name", IMPORTANCE_LOW);
+        channel.setSound(SOUND_URI, mAudioAttributes);
+        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+        loadStreamXml(baos, true);
+
+        NotificationChannel actualChannel = mHelper.getNotificationChannel(
+                PKG, UID, channel.getId(), false);
+        assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
+    }
+
+
+    /**
+     * Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to
+     * handle its restore properly.
+     */
+    @Test
+    public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
+        // Not a local uncanonicalized uri, simulating that it fails to exist locally
+        when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null);
+        String id = "id";
+        String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
+                + "<package name=\"com.android.server.notification\" show_badge=\"true\">\n"
+                + "<channel id=\"" + id + "\" name=\"name\" importance=\"2\" "
+                + "sound=\"" + SOUND_URI + "\" "
+                + "usage=\"6\" content_type=\"0\" flags=\"1\" show_badge=\"true\" />\n"
+                + "<channel id=\"miscellaneous\" name=\"Uncategorized\" usage=\"5\" "
+                + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+                + "</package>\n"
+                + "</ranking>\n";
+
+        loadByteArrayXml(backupWithUncanonicalizedSoundUri.getBytes(), true);
+
+        NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG, UID, id, false);
+        assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
+    }
+
+    @Test
+    public void testBackupRestoreXml_withNullSoundUri() throws Exception {
+        NotificationChannel channel =
+                new NotificationChannel("id", "name", IMPORTANCE_LOW);
+        channel.setSound(null, mAudioAttributes);
+        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+        loadStreamXml(baos, true);
+
+        NotificationChannel actualChannel = mHelper.getNotificationChannel(
+                PKG, UID, channel.getId(), false);
+        assertEquals(null, actualChannel.getSound());
+    }
+
+    @Test
     public void testChannelXml_backup() throws Exception {
         NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
         NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
new file mode 100644
index 0000000..855b82c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -0,0 +1,97 @@
+/*
+ * 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.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import static com.android.server.am.ActivityManagerService.ANIMATE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
+
+/**
+ * Tests for the {@link ActivityStack} class.
+ *
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.am.ActivityStarterTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActivityStarterTests extends ActivityTestsBase {
+    private static final ComponentName testActivityComponent =
+            ComponentName.unflattenFromString("com.foo/.BarActivity");
+
+    private ActivityManagerService mService;
+    private ActivityStarter mStarter;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mService = createActivityManagerService();
+        mStarter = new ActivityStarter(mService, mService.mStackSupervisor);
+    }
+
+    @Test
+    public void testUpdateLaunchBounds() throws Exception {
+        // When in a non-resizeable stack, the task bounds should be updated.
+        final TaskRecord task = createTask(mService.mStackSupervisor, testActivityComponent,
+                mService.mStackSupervisor.getDefaultDisplay().createStack(
+                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */));
+        final Rect bounds = new Rect(10, 10, 100, 100);
+
+        mStarter.updateBounds(task, bounds);
+        assertEquals(task.mBounds, bounds);
+        assertEquals(task.getStack().mBounds, null);
+
+        // When in a resizeable stack, the stack bounds should be updated as well.
+        final TaskRecord task2 = createTask(mService.mStackSupervisor, testActivityComponent,
+                mService.mStackSupervisor.getDefaultDisplay().createStack(
+                        WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */));
+        assertTrue(task2.getStack() instanceof PinnedActivityStack);
+        mStarter.updateBounds(task2, bounds);
+
+        verify(mService, times(1)).resizeStack(eq(ActivityManager.StackId.PINNED_STACK_ID),
+                eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
+
+        // In the case of no animation, the stack and task bounds should be set immediately.
+        if (!ANIMATE) {
+            assertEquals(task2.getStack().mBounds, bounds);
+            assertEquals(task2.mBounds, bounds);
+        } else {
+            assertEquals(task2.mBounds, null);
+        }
+    }
+}
\ No newline at end of file
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 b4bfa62..1f6dda1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -22,6 +22,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
 
 import org.mockito.invocation.InvocationOnMock;
 
@@ -38,8 +39,8 @@
 import android.support.test.InstrumentationRegistry;
 import com.android.server.AttributeCache;
 import com.android.server.wm.AppWindowContainerController;
+import com.android.server.wm.PinnedStackWindowController;
 import com.android.server.wm.StackWindowController;
-
 import com.android.server.wm.TaskWindowContainerController;
 import com.android.server.wm.WindowManagerService;
 import com.android.server.wm.WindowTestUtils;
@@ -71,8 +72,8 @@
     }
 
     protected ActivityManagerService createActivityManagerService() {
-        final ActivityManagerService service = new TestActivityManagerService(mContext);
-        service.mWindowManager = WindowTestUtils.getMockWindowManagerService();
+        final ActivityManagerService service = spy(new TestActivityManagerService(mContext));
+        service.mWindowManager = prepareMockWindowManager();
         return service;
     }
 
@@ -237,6 +238,12 @@
                     Rect getDefaultPictureInPictureBounds(float aspectRatio) {
                         return new Rect(50, 50, 100, 100);
                     }
+
+                    @Override
+                    PinnedStackWindowController createStackWindowController(int displayId,
+                            boolean onTop, Rect outBounds) {
+                        return mock(PinnedStackWindowController.class);
+                    }
                 };
             } else {
                 return (T) new TestActivityStack(
@@ -246,7 +253,7 @@
     }
 
     private static WindowManagerService prepareMockWindowManager() {
-        final WindowManagerService service = mock(WindowManagerService.class);
+        final WindowManagerService service = WindowTestUtils.getMockWindowManagerService();
 
         doAnswer((InvocationOnMock invocationOnMock) -> {
             final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
@@ -264,7 +271,7 @@
     }
 
     /**
-     * Override of {@link ActivityStack} that tracks test metrics, such as the number of times a
+     * Overrided of {@link ActivityStack} that tracks test metrics, such as the number of times a
      * method is called. Note that its functionality depends on the implementations of the
      * construction arguments.
      */
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 3cd24b8..fd105bc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -34,6 +34,7 @@
 import static org.junit.Assert.fail;
 
 import android.annotation.NonNull;
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
@@ -51,6 +52,9 @@
 
 import com.android.internal.os.AtomicFile;
 import com.android.server.LocalServices;
+import com.android.server.pm.permission.PermissionManagerInternal;
+import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -79,8 +83,11 @@
             throws ReflectiveOperationException, IllegalAccessException {
         /* write out files and read */
         writeOldFiles();
+        final Context context = InstrumentationRegistry.getContext();
+        final Object lock = new Object();
+        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
         Settings settings =
-                new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
         verifyKeySetMetaData(settings);
     }
@@ -91,8 +98,11 @@
             throws ReflectiveOperationException, IllegalAccessException {
         // write out files and read
         writeOldFiles();
+        final Context context = InstrumentationRegistry.getContext();
+        final Object lock = new Object();
+        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
         Settings settings =
-                new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
 
         // write out, read back in and verify the same
@@ -105,8 +115,11 @@
     public void testSettingsReadOld() {
         // Write the package files and make sure they're parsed properly the first time
         writeOldFiles();
+        final Context context = InstrumentationRegistry.getContext();
+        final Object lock = new Object();
+        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
         Settings settings =
-                new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
         assertThat(settings.getPackageLPr(PACKAGE_NAME_3), is(notNullValue()));
         assertThat(settings.getPackageLPr(PACKAGE_NAME_1), is(notNullValue()));
@@ -125,13 +138,17 @@
     public void testNewPackageRestrictionsFile() throws ReflectiveOperationException {
         // Write the package files and make sure they're parsed properly the first time
         writeOldFiles();
+        final Context context = InstrumentationRegistry.getContext();
+        final Object lock = new Object();
+        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
         Settings settings =
-                new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
         settings.writeLPr();
 
         // Create Settings again to make it read from the new files
-        settings = new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+        settings =
+                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
 
         PackageSetting ps = settings.getPackageLPr(PACKAGE_NAME_2);
@@ -143,8 +160,11 @@
     public void testEnableDisable() {
         // Write the package files and make sure they're parsed properly the first time
         writeOldFiles();
+        final Context context = InstrumentationRegistry.getContext();
+        final Object lock = new Object();
+        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
         Settings settings =
-                new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
 
         // Enable/Disable a package
@@ -334,7 +354,11 @@
     /** Update package; changing shared user throws exception */
     @Test
     public void testUpdatePackageSetting03() {
-        final Settings testSettings01 = new Settings(new Object() /*lock*/);
+        final Context context = InstrumentationRegistry.getContext();
+        final Object lock = new Object();
+        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        final Settings testSettings01 =
+                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         final SharedUserSetting testUserSetting01 = createSharedUserSetting(
                 testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
         final PackageSetting testPkgSetting01 =
@@ -449,7 +473,11 @@
     /** Create PackageSetting for a shared user */
     @Test
     public void testCreateNewSetting03() {
-        final Settings testSettings01 = new Settings(new Object() /*lock*/);
+        final Context context = InstrumentationRegistry.getContext();
+        final Object lock = new Object();
+        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        final Settings testSettings01 =
+                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         final SharedUserSetting testUserSetting01 = createSharedUserSetting(
                 testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
         final PackageSetting testPkgSetting01 = Settings.createNewSetting(
@@ -542,8 +570,11 @@
         final PackageParser.Package pkg = new PackageParser.Package(PACKAGE_NAME);
         pkg.applicationInfo.setCodePath(ps.codePathString);
         pkg.applicationInfo.setResourcePath(ps.resourcePathString);
+        final Context context = InstrumentationRegistry.getContext();
+        final Object lock = new Object();
+        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
         final Settings settings =
-                new Settings(InstrumentationRegistry.getContext().getFilesDir(), new Object());
+                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         pkg.usesStaticLibraries = new ArrayList<>(
                 Arrays.asList("foo.bar1", "foo.bar2", "foo.bar3"));
         pkg.usesStaticLibrariesVersions = new int[] {2, 4, 6};
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
new file mode 100644
index 0000000..b2446ba
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -0,0 +1,987 @@
+/**
+ * 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.usage;
+
+import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
+import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
+import static com.android.server.usage.UsageStatsService.MSG_REPORT_EVENT;
+
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.admin.DevicePolicyManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.appwidget.AppWidgetManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
+import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
+import android.net.NetworkScoreManager;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.IDeviceIdleController;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages the standby state of an app, listening to various events.
+ */
+public class AppStandbyController {
+
+    private static final String TAG = "AppStandbyController";
+    private static final boolean DEBUG = false;
+
+    static final boolean COMPRESS_TIME = false;
+    private static final long ONE_MINUTE = 60 * 1000;
+
+    // To name the lock for stack traces
+    static class Lock {}
+
+    /** Lock to protect the app's standby state. Required for calls into AppIdleHistory */
+    private final Object mAppIdleLock = new Lock();
+
+    /** Keeps the history and state for each app. */
+    @GuardedBy("mAppIdleLock")
+    private AppIdleHistory mAppIdleHistory;
+
+    @GuardedBy("mAppIdleLock")
+    private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
+            mPackageAccessListeners = new ArrayList<>();
+
+    /** Whether we've queried the list of carrier privileged apps. */
+    @GuardedBy("mAppIdleLock")
+    private boolean mHaveCarrierPrivilegedApps;
+
+    /** List of carrier-privileged apps that should be excluded from standby */
+    @GuardedBy("mAppIdleLock")
+    private List<String> mCarrierPrivilegedApps;
+
+    // Messages for the handler
+    static final int MSG_INFORM_LISTENERS = 3;
+    static final int MSG_FORCE_IDLE_STATE = 4;
+    static final int MSG_CHECK_IDLE_STATES = 5;
+    static final int MSG_CHECK_PAROLE_TIMEOUT = 6;
+    static final int MSG_PAROLE_END_TIMEOUT = 7;
+    static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
+    static final int MSG_PAROLE_STATE_CHANGED = 9;
+    static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
+
+    long mAppIdleScreenThresholdMillis;
+    long mCheckIdleIntervalMillis;
+    long mAppIdleWallclockThresholdMillis;
+    long mAppIdleParoleIntervalMillis;
+    long mAppIdleParoleDurationMillis;
+    boolean mAppIdleEnabled;
+    boolean mAppIdleTempParoled;
+    boolean mCharging;
+    private long mLastAppIdleParoledTime;
+    private boolean mSystemServicesReady = false;
+
+    private volatile boolean mPendingOneTimeCheckIdleStates;
+
+    private final Handler mHandler;
+    private final Context mContext;
+
+    private DisplayManager mDisplayManager;
+    private IDeviceIdleController mDeviceIdleController;
+    private AppWidgetManager mAppWidgetManager;
+    private IBatteryStats mBatteryStats;
+    private PowerManager mPowerManager;
+    private PackageManager mPackageManager;
+    private PackageManagerInternal mPackageManagerInternal;
+
+    AppStandbyController(Context context, Looper looper) {
+        mContext = context;
+        mHandler = new AppStandbyHandler(looper);
+        mPackageManager = mContext.getPackageManager();
+        mAppIdleEnabled = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_enableAutoPowerModes);
+        if (mAppIdleEnabled) {
+            IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+            deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
+            deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+            mContext.registerReceiver(new DeviceStateReceiver(), deviceStates);
+        }
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
+        }
+
+        IntentFilter packageFilter = new IntentFilter();
+        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        packageFilter.addDataScheme("package");
+
+        mContext.registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
+                null, mHandler);
+    }
+
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_SYSTEM_SERVICES_READY) {
+            // Observe changes to the threshold
+            SettingsObserver settingsObserver = new SettingsObserver(mHandler);
+            settingsObserver.registerObserver();
+            settingsObserver.updateSettings();
+
+            mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
+            mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+                    ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+            mBatteryStats = IBatteryStats.Stub.asInterface(
+                    ServiceManager.getService(BatteryStats.SERVICE_NAME));
+            mDisplayManager = (DisplayManager) mContext.getSystemService(
+                    Context.DISPLAY_SERVICE);
+            mPowerManager = mContext.getSystemService(PowerManager.class);
+            mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+
+            mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+            synchronized (mAppIdleLock) {
+                mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime());
+            }
+
+            if (mPendingOneTimeCheckIdleStates) {
+                postOneTimeCheckIdleStates();
+            }
+
+            mSystemServicesReady = true;
+        } else if (phase == PHASE_BOOT_COMPLETED) {
+            setChargingState(mContext.getSystemService(BatteryManager.class).isCharging());
+        }
+    }
+
+    void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
+        // Get sync adapters for the authority
+        String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
+                authority, userId);
+        for (String packageName: packages) {
+            // Only force the sync adapters to active if the provider is not in the same package and
+            // the sync adapter is a system package.
+            try {
+                PackageInfo pi = mPackageManager.getPackageInfoAsUser(
+                        packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
+                if (pi == null || pi.applicationInfo == null) {
+                    continue;
+                }
+                if (!packageName.equals(providerPkgName)) {
+                    setAppIdleAsync(packageName, false, userId);
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                // Shouldn't happen
+            }
+        }
+    }
+
+    void setChargingState(boolean charging) {
+        synchronized (mAppIdleLock) {
+            if (mCharging != charging) {
+                mCharging = charging;
+                postParoleStateChanged();
+            }
+        }
+    }
+
+    /** Paroled here means temporary pardon from being inactive */
+    void setAppIdleParoled(boolean paroled) {
+        synchronized (mAppIdleLock) {
+            final long now = System.currentTimeMillis();
+            if (mAppIdleTempParoled != paroled) {
+                mAppIdleTempParoled = paroled;
+                if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
+                if (paroled) {
+                    postParoleEndTimeout();
+                } else {
+                    mLastAppIdleParoledTime = now;
+                    postNextParoleTimeout(now);
+                }
+                postParoleStateChanged();
+            }
+        }
+    }
+
+    boolean isParoledOrCharging() {
+        synchronized (mAppIdleLock) {
+            return mAppIdleTempParoled || mCharging;
+        }
+    }
+
+    private void postNextParoleTimeout(long now) {
+        if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
+        mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
+        // Compute when the next parole needs to happen. We check more frequently than necessary
+        // since the message handler delays are based on elapsedRealTime and not wallclock time.
+        // The comparison is done in wallclock time.
+        long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now;
+        if (timeLeft < 0) {
+            timeLeft = 0;
+        }
+        mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
+    }
+
+    private void postParoleEndTimeout() {
+        if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT");
+        mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT);
+        mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis);
+    }
+
+    private void postParoleStateChanged() {
+        if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
+        mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
+        mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
+    }
+
+    void postCheckIdleStates(int userId) {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
+    }
+
+    /**
+     * We send a different message to check idle states once, otherwise we would end up
+     * scheduling a series of repeating checkIdleStates each time we fired off one.
+     */
+    void postOneTimeCheckIdleStates() {
+        if (mDeviceIdleController == null) {
+            // Not booted yet; wait for it!
+            mPendingOneTimeCheckIdleStates = true;
+        } else {
+            mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
+            mPendingOneTimeCheckIdleStates = false;
+        }
+    }
+
+    /**
+     * Check all running users' or specified user's apps to see if they enter an idle state.
+     * @return Returns whether checking should continue periodically.
+     */
+    boolean checkIdleStates(int checkUserId) {
+        if (!mAppIdleEnabled) {
+            return false;
+        }
+
+        final int[] runningUserIds;
+        try {
+            runningUserIds = ActivityManager.getService().getRunningUserIds();
+            if (checkUserId != UserHandle.USER_ALL
+                    && !ArrayUtils.contains(runningUserIds, checkUserId)) {
+                return false;
+            }
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        for (int i = 0; i < runningUserIds.length; i++) {
+            final int userId = runningUserIds[i];
+            if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
+                continue;
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "Checking idle state for user " + userId);
+            }
+            List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+                    PackageManager.MATCH_DISABLED_COMPONENTS,
+                    userId);
+            final int packageCount = packages.size();
+            for (int p = 0; p < packageCount; p++) {
+                final PackageInfo pi = packages.get(p);
+                final String packageName = pi.packageName;
+                final boolean isIdle = isAppIdleFiltered(packageName,
+                        UserHandle.getAppId(pi.applicationInfo.uid),
+                        userId, elapsedRealtime);
+                mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
+                        userId, isIdle ? 1 : 0, packageName));
+                if (isIdle) {
+                    synchronized (mAppIdleLock) {
+                        mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
+                    }
+                }
+            }
+        }
+        if (DEBUG) {
+            Slog.d(TAG, "checkIdleStates took "
+                    + (SystemClock.elapsedRealtime() - elapsedRealtime));
+        }
+        return true;
+    }
+
+    /** Check if it's been a while since last parole and let idle apps do some work */
+    void checkParoleTimeout() {
+        boolean setParoled = false;
+        synchronized (mAppIdleLock) {
+            final long now = System.currentTimeMillis();
+            if (!mAppIdleTempParoled) {
+                final long timeSinceLastParole = now - mLastAppIdleParoledTime;
+                if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
+                    if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
+                    setParoled = true;
+                } else {
+                    if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
+                    postNextParoleTimeout(now);
+                }
+            }
+        }
+        if (setParoled) {
+            setAppIdleParoled(true);
+        }
+    }
+
+    private void notifyBatteryStats(String packageName, int userId, boolean idle) {
+        try {
+            final int uid = mPackageManager.getPackageUidAsUser(packageName,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+            if (idle) {
+                mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
+                        packageName, uid);
+            } else {
+                mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
+                        packageName, uid);
+            }
+        } catch (PackageManager.NameNotFoundException | RemoteException e) {
+        }
+    }
+
+    void onDeviceIdleModeChanged() {
+        final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
+        if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
+        boolean paroled = false;
+        synchronized (mAppIdleLock) {
+            final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime;
+            if (!deviceIdle
+                    && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
+                }
+                paroled = true;
+            } else if (deviceIdle) {
+                if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
+                paroled = false;
+            } else {
+                return;
+            }
+        }
+        setAppIdleParoled(paroled);
+    }
+
+    void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
+        synchronized (mAppIdleLock) {
+            // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
+            // about apps that are on some kind of whitelist anyway.
+            final boolean previouslyIdle = mAppIdleHistory.isIdle(
+                    event.mPackage, userId, elapsedRealtime);
+            // Inform listeners if necessary
+            if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
+                    || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
+                    || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
+                    || event.mEventType == UsageEvents.Event.USER_INTERACTION)) {
+                mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
+                if (previouslyIdle) {
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+                                /* idle = */ 0, event.mPackage));
+                    notifyBatteryStats(event.mPackage, userId, false);
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
+     * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
+     * the threshold for idle.
+     *
+     * This method is always called from the handler thread, so not much synchronization is
+     * required.
+     */
+    void forceIdleState(String packageName, int userId, boolean idle) {
+        final int appId = getAppId(packageName);
+        if (appId < 0) return;
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+
+        final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
+                userId, elapsedRealtime);
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
+        }
+        final boolean stillIdle = isAppIdleFiltered(packageName, appId,
+                userId, elapsedRealtime);
+        // Inform listeners if necessary
+        if (previouslyIdle != stillIdle) {
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+                    /* idle = */ stillIdle ? 1 : 0, packageName));
+            if (!stillIdle) {
+                notifyBatteryStats(packageName, userId, idle);
+            }
+        }
+    }
+
+    public void onUserRemoved(int userId) {
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.onUserRemoved(userId);
+        }
+    }
+
+    private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
+        synchronized (mAppIdleLock) {
+            return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
+        }
+    }
+
+    void addListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) {
+        synchronized (mAppIdleLock) {
+            if (!mPackageAccessListeners.contains(listener)) {
+                mPackageAccessListeners.add(listener);
+            }
+        }
+    }
+
+    void removeListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) {
+        synchronized (mAppIdleLock) {
+            mPackageAccessListeners.remove(listener);
+        }
+    }
+
+    int getAppId(String packageName) {
+        try {
+            ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
+                    PackageManager.MATCH_ANY_USER
+                            | PackageManager.MATCH_DISABLED_COMPONENTS);
+            return ai.uid;
+        } catch (PackageManager.NameNotFoundException re) {
+            return -1;
+        }
+    }
+
+    boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
+            boolean shouldObfuscateInstantApps) {
+        if (isParoledOrCharging()) {
+            return false;
+        }
+        if (shouldObfuscateInstantApps &&
+                mPackageManagerInternal.isPackageEphemeral(userId, packageName)) {
+            return false;
+        }
+        return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
+    }
+
+    /**
+     * Checks if an app has been idle for a while and filters out apps that are excluded.
+     * It returns false if the current system state allows all apps to be considered active.
+     * This happens if the device is plugged in or temporarily allowed to make exceptions.
+     * Called by interface impls.
+     */
+    boolean isAppIdleFiltered(String packageName, int appId, int userId,
+            long elapsedRealtime) {
+        if (packageName == null) return false;
+        // If not enabled at all, of course nobody is ever idle.
+        if (!mAppIdleEnabled) {
+            return false;
+        }
+        if (appId < Process.FIRST_APPLICATION_UID) {
+            // System uids never go idle.
+            return false;
+        }
+        if (packageName.equals("android")) {
+            // Nor does the framework (which should be redundant with the above, but for MR1 we will
+            // retain this for safety).
+            return false;
+        }
+        if (mSystemServicesReady) {
+            try {
+                // We allow all whitelisted apps, including those that don't want to be whitelisted
+                // for idle mode, because app idle (aka app standby) is really not as big an issue
+                // for controlling who participates vs. doze mode.
+                if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
+                    return false;
+                }
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+
+            if (isActiveDeviceAdmin(packageName, userId)) {
+                return false;
+            }
+
+            if (isActiveNetworkScorer(packageName)) {
+                return false;
+            }
+
+            if (mAppWidgetManager != null
+                    && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
+                return false;
+            }
+
+            if (isDeviceProvisioningPackage(packageName)) {
+                return false;
+            }
+        }
+
+        if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
+            return false;
+        }
+
+        // Check this last, as it is the most expensive check
+        // TODO: Optimize this by fetching the carrier privileged apps ahead of time
+        if (isCarrierApp(packageName)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    int[] getIdleUidsForUser(int userId) {
+        if (!mAppIdleEnabled) {
+            return new int[0];
+        }
+
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+
+        List<ApplicationInfo> apps;
+        try {
+            ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
+                    .getInstalledApplications(/* flags= */ 0, userId);
+            if (slice == null) {
+                return new int[0];
+            }
+            apps = slice.getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        // State of each uid.  Key is the uid.  Value lower 16 bits is the number of apps
+        // associated with that uid, upper 16 bits is the number of those apps that is idle.
+        SparseIntArray uidStates = new SparseIntArray();
+
+        // Now resolve all app state.  Iterating over all apps, keeping track of how many
+        // we find for each uid and how many of those are idle.
+        for (int i = apps.size() - 1; i >= 0; i--) {
+            ApplicationInfo ai = apps.get(i);
+
+            // Check whether this app is idle.
+            boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
+                    userId, elapsedRealtime);
+
+            int index = uidStates.indexOfKey(ai.uid);
+            if (index < 0) {
+                uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
+            } else {
+                int value = uidStates.valueAt(index);
+                uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
+            }
+        }
+        if (DEBUG) {
+            Slog.d(TAG, "getIdleUids took " + (SystemClock.elapsedRealtime() - elapsedRealtime));
+        }
+        int numIdle = 0;
+        for (int i = uidStates.size() - 1; i >= 0; i--) {
+            int value = uidStates.valueAt(i);
+            if ((value&0x7fff) == (value>>16)) {
+                numIdle++;
+            }
+        }
+
+        int[] res = new int[numIdle];
+        numIdle = 0;
+        for (int i = uidStates.size() - 1; i >= 0; i--) {
+            int value = uidStates.valueAt(i);
+            if ((value&0x7fff) == (value>>16)) {
+                res[numIdle] = uidStates.keyAt(i);
+                numIdle++;
+            }
+        }
+
+        return res;
+    }
+
+    void setAppIdleAsync(String packageName, boolean idle, int userId) {
+        if (packageName == null) return;
+
+        mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
+                .sendToTarget();
+    }
+
+    private boolean isActiveDeviceAdmin(String packageName, int userId) {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        if (dpm == null) return false;
+        return dpm.packageHasActiveAdmins(packageName, userId);
+    }
+
+    /**
+     * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
+     * returns {@code false}.
+     */
+    private boolean isDeviceProvisioningPackage(String packageName) {
+        String deviceProvisioningPackage = mContext.getResources().getString(
+                com.android.internal.R.string.config_deviceProvisioningPackage);
+        return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
+    }
+
+    private boolean isCarrierApp(String packageName) {
+        synchronized (mAppIdleLock) {
+            if (!mHaveCarrierPrivilegedApps) {
+                fetchCarrierPrivilegedAppsLA();
+            }
+            if (mCarrierPrivilegedApps != null) {
+                return mCarrierPrivilegedApps.contains(packageName);
+            }
+            return false;
+        }
+    }
+
+    void clearCarrierPrivilegedApps() {
+        if (DEBUG) {
+            Slog.i(TAG, "Clearing carrier privileged apps list");
+        }
+        synchronized (mAppIdleLock) {
+            mHaveCarrierPrivilegedApps = false;
+            mCarrierPrivilegedApps = null; // Need to be refetched.
+        }
+    }
+
+    @GuardedBy("mAppIdleLock")
+    private void fetchCarrierPrivilegedAppsLA() {
+        TelephonyManager telephonyManager =
+                mContext.getSystemService(TelephonyManager.class);
+        mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
+        mHaveCarrierPrivilegedApps = true;
+        if (DEBUG) {
+            Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
+        }
+    }
+
+    private boolean isActiveNetworkScorer(String packageName) {
+        NetworkScoreManager nsm = (NetworkScoreManager) mContext.getSystemService(
+                Context.NETWORK_SCORE_SERVICE);
+        return packageName != null && packageName.equals(nsm.getActiveScorerPackage());
+    }
+
+    void informListeners(String packageName, int userId, boolean isIdle) {
+        for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) {
+            listener.onAppIdleStateChanged(packageName, userId, isIdle);
+        }
+    }
+
+    void informParoleStateChanged() {
+        final boolean paroled = isParoledOrCharging();
+        for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) {
+            listener.onParoleStateChanged(paroled);
+        }
+    }
+
+    void flushToDisk(int userId) {
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.writeAppIdleTimes(userId);
+        }
+    }
+
+    void flushDurationsToDisk() {
+        // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
+        // considered not-idle, which is the safest outcome in such an event.
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.writeAppIdleDurations();
+        }
+    }
+
+    boolean isDisplayOn() {
+        return mDisplayManager
+                .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
+    }
+
+    void clearAppIdleForPackage(String packageName, int userId) {
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.clearUsage(packageName, userId);
+        }
+    }
+
+    private class PackageReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+                    || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+                clearCarrierPrivilegedApps();
+            }
+            if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
+                    Intent.ACTION_PACKAGE_ADDED.equals(action))
+                    && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
+                        getSendingUserId());
+            }
+        }
+    }
+
+    void initializeDefaultsForSystemApps(int userId) {
+        Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+                PackageManager.MATCH_DISABLED_COMPONENTS,
+                userId);
+        final int packageCount = packages.size();
+        synchronized (mAppIdleLock) {
+            for (int i = 0; i < packageCount; i++) {
+                final PackageInfo pi = packages.get(i);
+                String packageName = pi.packageName;
+                if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
+                    mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
+                }
+            }
+        }
+    }
+
+    void postReportContentProviderUsage(String name, String packageName, int userId) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = name;
+        args.arg2 = packageName;
+        args.arg3 = userId;
+        mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
+                .sendToTarget();
+    }
+
+    void dumpHistory(IndentingPrintWriter idpw, int userId) {
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.dumpHistory(idpw, userId);
+        }
+    }
+
+    void dumpUser(IndentingPrintWriter idpw, int userId) {
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.dump(idpw, userId);
+        }
+    }
+
+    void dumpState(String[] args, PrintWriter pw) {
+        synchronized (mAppIdleLock) {
+            pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
+                    + "): " + mCarrierPrivilegedApps);
+        }
+
+        pw.println();
+        pw.println("Settings:");
+
+        pw.print("  mAppIdleDurationMillis=");
+        TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
+        pw.println();
+
+        pw.print("  mAppIdleWallclockThresholdMillis=");
+        TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
+        pw.println();
+
+        pw.print("  mCheckIdleIntervalMillis=");
+        TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
+        pw.println();
+
+        pw.print("  mAppIdleParoleIntervalMillis=");
+        TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw);
+        pw.println();
+
+        pw.print("  mAppIdleParoleDurationMillis=");
+        TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
+        pw.println();
+
+        pw.println();
+        pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
+        pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
+        pw.print(" mCharging="); pw.print(mCharging);
+        pw.print(" mLastAppIdleParoledTime=");
+        TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
+        pw.println();
+    }
+
+    class AppStandbyHandler extends Handler {
+
+        AppStandbyHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_FORCE_IDLE_STATE:
+                    forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
+                    break;
+
+                case MSG_CHECK_IDLE_STATES:
+                    if (checkIdleStates(msg.arg1)) {
+                        mHandler.sendMessageDelayed(mHandler.obtainMessage(
+                                MSG_CHECK_IDLE_STATES, msg.arg1, 0),
+                                mCheckIdleIntervalMillis);
+                    }
+                    break;
+
+                case MSG_ONE_TIME_CHECK_IDLE_STATES:
+                    mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
+                    checkIdleStates(UserHandle.USER_ALL);
+                    break;
+
+                case MSG_CHECK_PAROLE_TIMEOUT:
+                    checkParoleTimeout();
+                    break;
+
+                case MSG_PAROLE_END_TIMEOUT:
+                    if (DEBUG) Slog.d(TAG, "Ending parole");
+                    setAppIdleParoled(false);
+                    break;
+
+                case MSG_REPORT_CONTENT_PROVIDER_USAGE:
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    reportContentProviderUsage((String) args.arg1, // authority name
+                            (String) args.arg2, // package name
+                            (int) args.arg3); // userId
+                    args.recycle();
+                    break;
+
+                case MSG_PAROLE_STATE_CHANGED:
+                    if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
+                            + ", Charging state:" + mCharging);
+                    informParoleStateChanged();
+                    break;
+                default:
+                    super.handleMessage(msg);
+                    break;
+
+            }
+        }
+    };
+
+    private class DeviceStateReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+                setChargingState(intent.getIntExtra("plugged", 0) != 0);
+            } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+                onDeviceIdleModeChanged();
+            }
+        }
+    }
+
+    private final DisplayManager.DisplayListener mDisplayListener
+            = new DisplayManager.DisplayListener() {
+
+        @Override public void onDisplayAdded(int displayId) {
+        }
+
+        @Override public void onDisplayRemoved(int displayId) {
+        }
+
+        @Override public void onDisplayChanged(int displayId) {
+            if (displayId == Display.DEFAULT_DISPLAY) {
+                final boolean displayOn = isDisplayOn();
+                synchronized (mAppIdleLock) {
+                    mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime());
+                }
+            }
+        }
+    };
+
+    /**
+     * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
+     */
+    private class SettingsObserver extends ContentObserver {
+        /**
+         * This flag has been used to disable app idle on older builds with bug b/26355386.
+         */
+        @Deprecated
+        private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
+
+        private static final String KEY_IDLE_DURATION = "idle_duration2";
+        private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
+        private static final String KEY_PAROLE_INTERVAL = "parole_interval";
+        private static final String KEY_PAROLE_DURATION = "parole_duration";
+
+        private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        void registerObserver() {
+            mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.APP_IDLE_CONSTANTS), false, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            updateSettings();
+            postOneTimeCheckIdleStates();
+        }
+
+        void updateSettings() {
+            synchronized (mAppIdleLock) {
+                // Look at global settings for this.
+                // TODO: Maybe apply different thresholds for different users.
+                try {
+                    mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
+                            Settings.Global.APP_IDLE_CONSTANTS));
+                } catch (IllegalArgumentException e) {
+                    Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
+                    // fallthrough, mParser is empty and all defaults will be returned.
+                }
+
+                // Default: 12 hours of screen-on time sans dream-time
+                mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
+                        COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
+
+                mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
+                        COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
+
+                mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
+                        COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
+
+                // Default: 24 hours between paroles
+                mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
+                        COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
+
+                mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
+                        COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
+                mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
+                        mAppIdleScreenThresholdMillis);
+            }
+        }
+    }
+
+}
+
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 25e471c..afafea1 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -18,37 +18,24 @@
 
 import android.Manifest;
 import android.app.ActivityManager;
-import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.IUidObserver;
-import android.app.admin.DevicePolicyManager;
 import android.app.usage.ConfigurationStats;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
-import android.appwidget.AppWidgetManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
-import android.database.ContentObserver;
-import android.hardware.display.DisplayManager;
-import android.net.NetworkScoreManager;
-import android.os.BatteryManager;
-import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -56,7 +43,6 @@
 import android.os.IDeviceIdleController;
 import android.os.Looper;
 import android.os.Message;
-import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -64,21 +50,12 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
-import android.telephony.TelephonyManager;
 import android.util.ArraySet;
-import android.util.KeyValueListParser;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.view.Display;
 
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
@@ -88,7 +65,6 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -107,7 +83,6 @@
     static final boolean COMPRESS_TIME = false;
 
     private static final long TEN_SECONDS = 10 * 1000;
-    private static final long ONE_MINUTE = 60 * 1000;
     private static final long TWENTY_MINUTES = 20 * 60 * 1000;
     private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
     private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
@@ -115,24 +90,10 @@
     private static final boolean ENABLE_KERNEL_UPDATES = true;
     private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
 
-    long mAppIdleScreenThresholdMillis;
-    long mCheckIdleIntervalMillis;
-    long mAppIdleWallclockThresholdMillis;
-    long mAppIdleParoleIntervalMillis;
-    long mAppIdleParoleDurationMillis;
-
     // Handler message types.
     static final int MSG_REPORT_EVENT = 0;
     static final int MSG_FLUSH_TO_DISK = 1;
     static final int MSG_REMOVE_USER = 2;
-    static final int MSG_INFORM_LISTENERS = 3;
-    static final int MSG_FORCE_IDLE_STATE = 4;
-    static final int MSG_CHECK_IDLE_STATES = 5;
-    static final int MSG_CHECK_PAROLE_TIMEOUT = 6;
-    static final int MSG_PAROLE_END_TIMEOUT = 7;
-    static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
-    static final int MSG_PAROLE_STATE_CHANGED = 9;
-    static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
 
     private final Object mLock = new Object();
     Handler mHandler;
@@ -140,11 +101,7 @@
     UserManager mUserManager;
     PackageManager mPackageManager;
     PackageManagerInternal mPackageManagerInternal;
-    AppWidgetManager mAppWidgetManager;
     IDeviceIdleController mDeviceIdleController;
-    private DisplayManager mDisplayManager;
-    private PowerManager mPowerManager;
-    private IBatteryStats mBatteryStats;
 
     private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
     private final SparseIntArray mUidToKernelCounter = new SparseIntArray();
@@ -152,26 +109,7 @@
     long mRealTimeSnapshot;
     long mSystemTimeSnapshot;
 
-    boolean mAppIdleEnabled;
-    boolean mAppIdleTempParoled;
-    boolean mCharging;
-    private long mLastAppIdleParoledTime;
-
-    private volatile boolean mPendingOneTimeCheckIdleStates;
-    private boolean mSystemServicesReady = false;
-
-    private final Object mAppIdleLock = new Object();
-    @GuardedBy("mAppIdleLock")
-    private AppIdleHistory mAppIdleHistory;
-
-    @GuardedBy("mAppIdleLock")
-    private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
-            mPackageAccessListeners = new ArrayList<>();
-
-    @GuardedBy("mAppIdleLock")
-    private boolean mHaveCarrierPrivilegedApps;
-    @GuardedBy("mAppIdleLock")
-    private List<String> mCarrierPrivilegedApps;
+    AppStandbyController mAppStandby;
 
     public UsageStatsService(Context context) {
         super(context);
@@ -185,6 +123,8 @@
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mHandler = new H(BackgroundThread.get().getLooper());
 
+        mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());
+
         File systemDataDir = new File(Environment.getDataDirectory(), "system");
         mUsageStatsDir = new File(systemDataDir, "usagestats");
         mUsageStatsDir.mkdirs();
@@ -198,30 +138,9 @@
         getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
                 null, mHandler);
 
-        IntentFilter packageFilter = new IntentFilter();
-        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        packageFilter.addDataScheme("package");
-
-        getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
-                null, mHandler);
-
-        mAppIdleEnabled = getContext().getResources().getBoolean(
-                com.android.internal.R.bool.config_enableAutoPowerModes);
-        if (mAppIdleEnabled) {
-            IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
-            deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
-            deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
-            getContext().registerReceiver(new DeviceStateReceiver(), deviceStates);
-        }
-
         synchronized (mLock) {
             cleanUpRemovedUsersLocked();
         }
-        synchronized (mAppIdleLock) {
-            mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
-        }
 
         mRealTimeSnapshot = SystemClock.elapsedRealtime();
         mSystemTimeSnapshot = System.currentTimeMillis();
@@ -233,28 +152,10 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == PHASE_SYSTEM_SERVICES_READY) {
-            // Observe changes to the threshold
-            SettingsObserver settingsObserver = new SettingsObserver(mHandler);
-            settingsObserver.registerObserver();
-            settingsObserver.updateSettings();
+            mAppStandby.onBootPhase(phase);
 
-            mAppWidgetManager = getContext().getSystemService(AppWidgetManager.class);
             mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
                     ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
-            mBatteryStats = IBatteryStats.Stub.asInterface(
-                    ServiceManager.getService(BatteryStats.SERVICE_NAME));
-            mDisplayManager = (DisplayManager) getContext().getSystemService(
-                    Context.DISPLAY_SERVICE);
-            mPowerManager = getContext().getSystemService(PowerManager.class);
-
-            mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
-            synchronized (mAppIdleLock) {
-                mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime());
-            }
-
-            if (mPendingOneTimeCheckIdleStates) {
-                postOneTimeCheckIdleStates();
-            }
 
             if (ENABLE_KERNEL_UPDATES && KERNEL_COUNTER_FILE.exists()) {
                 try {
@@ -268,18 +169,9 @@
             } else {
                 Slog.w(TAG, "Missing procfs interface: " + KERNEL_COUNTER_FILE);
             }
-
-            mSystemServicesReady = true;
-        } else if (phase == PHASE_BOOT_COMPLETED) {
-            setChargingState(getContext().getSystemService(BatteryManager.class).isCharging());
         }
     }
 
-    private boolean isDisplayOn() {
-        return mDisplayManager
-                .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
-    }
-
     private class UserActionsReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -291,60 +183,12 @@
                 }
             } else if (Intent.ACTION_USER_STARTED.equals(action)) {
                 if (userId >=0) {
-                    postCheckIdleStates(userId);
+                    mAppStandby.postCheckIdleStates(userId);
                 }
             }
         }
     }
 
-    private class PackageReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (Intent.ACTION_PACKAGE_ADDED.equals(action)
-                    || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
-                clearCarrierPrivilegedApps();
-            }
-            if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
-                    Intent.ACTION_PACKAGE_ADDED.equals(action))
-                    && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
-                clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
-                        getSendingUserId());
-            }
-        }
-    }
-
-    private class DeviceStateReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
-                setChargingState(intent.getIntExtra("plugged", 0) != 0);
-            } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
-                onDeviceIdleModeChanged();
-            }
-        }
-    }
-
-    private final DisplayManager.DisplayListener mDisplayListener
-            = new DisplayManager.DisplayListener() {
-
-        @Override public void onDisplayAdded(int displayId) {
-        }
-
-        @Override public void onDisplayRemoved(int displayId) {
-        }
-
-        @Override public void onDisplayChanged(int displayId) {
-            if (displayId == Display.DEFAULT_DISPLAY) {
-                final boolean displayOn = isDisplayOn();
-                synchronized (UsageStatsService.this.mAppIdleLock) {
-                    mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime());
-                }
-            }
-        }
-    };
-
     private final IUidObserver mUidObserver = new IUidObserver.Stub() {
         @Override
         public void onUidStateChanged(int uid, int procState, long procStateSeq) {
@@ -388,42 +232,18 @@
 
     @Override
     public void onStatsReloaded() {
-        postOneTimeCheckIdleStates();
+        mAppStandby.postOneTimeCheckIdleStates();
     }
 
     @Override
     public void onNewUpdate(int userId) {
-        initializeDefaultsForSystemApps(userId);
-    }
-
-    private void initializeDefaultsForSystemApps(int userId) {
-        Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
-        List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
-                PackageManager.MATCH_DISABLED_COMPONENTS,
-                userId);
-        final int packageCount = packages.size();
-        synchronized (mAppIdleLock) {
-            for (int i = 0; i < packageCount; i++) {
-                final PackageInfo pi = packages.get(i);
-                String packageName = pi.packageName;
-                if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
-                    mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
-                }
-            }
-        }
+        mAppStandby.initializeDefaultsForSystemApps(userId);
     }
 
     private boolean shouldObfuscateInstantAppsForCaller(int callingUid, int userId) {
         return !mPackageManagerInternal.canAccessInstantApps(callingUid, userId);
     }
 
-    void clearAppIdleForPackage(String packageName, int userId) {
-        synchronized (mAppIdleLock) {
-            mAppIdleHistory.clearUsage(packageName, userId);
-        }
-    }
-
     private void cleanUpRemovedUsersLocked() {
         final List<UserInfo> users = mUserManager.getUsers(true);
         if (users == null || users.size() == 0) {
@@ -451,195 +271,6 @@
         }
     }
 
-    void setChargingState(boolean charging) {
-        synchronized (mAppIdleLock) {
-            if (mCharging != charging) {
-                mCharging = charging;
-                postParoleStateChanged();
-            }
-        }
-    }
-
-    /** Paroled here means temporary pardon from being inactive */
-    void setAppIdleParoled(boolean paroled) {
-        synchronized (mAppIdleLock) {
-            final long now = System.currentTimeMillis();
-            if (mAppIdleTempParoled != paroled) {
-                mAppIdleTempParoled = paroled;
-                if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
-                if (paroled) {
-                    postParoleEndTimeout();
-                } else {
-                    mLastAppIdleParoledTime = now;
-                    postNextParoleTimeout(now);
-                }
-                postParoleStateChanged();
-            }
-        }
-    }
-
-    boolean isParoledOrCharging() {
-        synchronized (mAppIdleLock) {
-            return mAppIdleTempParoled || mCharging;
-        }
-    }
-
-    private void postNextParoleTimeout(long now) {
-        if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
-        mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
-        // Compute when the next parole needs to happen. We check more frequently than necessary
-        // since the message handler delays are based on elapsedRealTime and not wallclock time.
-        // The comparison is done in wallclock time.
-        long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now;
-        if (timeLeft < 0) {
-            timeLeft = 0;
-        }
-        mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
-    }
-
-    private void postParoleEndTimeout() {
-        if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT");
-        mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT);
-        mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis);
-    }
-
-    private void postParoleStateChanged() {
-        if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
-        mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
-        mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
-    }
-
-    void postCheckIdleStates(int userId) {
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
-    }
-
-    /**
-     * We send a different message to check idle states once, otherwise we would end up
-     * scheduling a series of repeating checkIdleStates each time we fired off one.
-     */
-    void postOneTimeCheckIdleStates() {
-        if (mDeviceIdleController == null) {
-            // Not booted yet; wait for it!
-            mPendingOneTimeCheckIdleStates = true;
-        } else {
-            mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
-            mPendingOneTimeCheckIdleStates = false;
-        }
-    }
-
-    /**
-     * Check all running users' or specified user's apps to see if they enter an idle state.
-     * @return Returns whether checking should continue periodically.
-     */
-    boolean checkIdleStates(int checkUserId) {
-        if (!mAppIdleEnabled) {
-            return false;
-        }
-
-        final int[] runningUserIds;
-        try {
-            runningUserIds = ActivityManager.getService().getRunningUserIds();
-            if (checkUserId != UserHandle.USER_ALL
-                    && !ArrayUtils.contains(runningUserIds, checkUserId)) {
-                return false;
-            }
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
-        for (int i = 0; i < runningUserIds.length; i++) {
-            final int userId = runningUserIds[i];
-            if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
-                continue;
-            }
-            if (DEBUG) {
-                Slog.d(TAG, "Checking idle state for user " + userId);
-            }
-            List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
-                    PackageManager.MATCH_DISABLED_COMPONENTS,
-                    userId);
-            final int packageCount = packages.size();
-            for (int p = 0; p < packageCount; p++) {
-                final PackageInfo pi = packages.get(p);
-                final String packageName = pi.packageName;
-                final boolean isIdle = isAppIdleFiltered(packageName,
-                        UserHandle.getAppId(pi.applicationInfo.uid),
-                        userId, elapsedRealtime);
-                mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
-                        userId, isIdle ? 1 : 0, packageName));
-                if (isIdle) {
-                    synchronized (mAppIdleLock) {
-                        mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
-                    }
-                }
-            }
-        }
-        if (DEBUG) {
-            Slog.d(TAG, "checkIdleStates took "
-                    + (SystemClock.elapsedRealtime() - elapsedRealtime));
-        }
-        return true;
-    }
-
-    /** Check if it's been a while since last parole and let idle apps do some work */
-    void checkParoleTimeout() {
-        boolean setParoled = false;
-        synchronized (mAppIdleLock) {
-            final long now = System.currentTimeMillis();
-            if (!mAppIdleTempParoled) {
-                final long timeSinceLastParole = now - mLastAppIdleParoledTime;
-                if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
-                    if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
-                    setParoled = true;
-                } else {
-                    if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
-                    postNextParoleTimeout(now);
-                }
-            }
-        }
-        if (setParoled) {
-            setAppIdleParoled(true);
-        }
-    }
-
-    private void notifyBatteryStats(String packageName, int userId, boolean idle) {
-        try {
-            final int uid = mPackageManager.getPackageUidAsUser(packageName,
-                    PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
-            if (idle) {
-                mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
-                        packageName, uid);
-            } else {
-                mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
-                        packageName, uid);
-            }
-        } catch (NameNotFoundException | RemoteException e) {
-        }
-    }
-
-    void onDeviceIdleModeChanged() {
-        final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
-        if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
-        boolean paroled = false;
-        synchronized (mAppIdleLock) {
-            final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime;
-            if (!deviceIdle
-                    && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
-                if (DEBUG) {
-                    Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
-                }
-                paroled = true;
-            } else if (deviceIdle) {
-                if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
-                paroled = false;
-            } else {
-                return;
-            }
-        }
-        setAppIdleParoled(paroled);
-    }
-
     private static void deleteRecursively(File f) {
         File[] files = f.listFiles();
         if (files != null) {
@@ -724,76 +355,7 @@
                     getUserDataAndInitializeIfNeededLocked(userId, timeNow);
             service.reportEvent(event);
 
-            synchronized (mAppIdleLock) {
-                // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
-                // about apps that are on some kind of whitelist anyway.
-                final boolean previouslyIdle = mAppIdleHistory.isIdle(
-                        event.mPackage, userId, elapsedRealtime);
-                // Inform listeners if necessary
-                if ((event.mEventType == Event.MOVE_TO_FOREGROUND
-                        || event.mEventType == Event.MOVE_TO_BACKGROUND
-                        || event.mEventType == Event.SYSTEM_INTERACTION
-                        || event.mEventType == Event.USER_INTERACTION)) {
-                    mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
-                    if (previouslyIdle) {
-                        mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
-                                /* idle = */ 0, event.mPackage));
-                        notifyBatteryStats(event.mPackage, userId, false);
-                    }
-                }
-            }
-        }
-    }
-
-    void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
-        // Get sync adapters for the authority
-        String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
-                authority, userId);
-        for (String packageName: packages) {
-            // Only force the sync adapters to active if the provider is not in the same package and
-            // the sync adapter is a system package.
-            try {
-                PackageInfo pi = mPackageManager.getPackageInfoAsUser(
-                        packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
-                if (pi == null || pi.applicationInfo == null) {
-                    continue;
-                }
-                if (!packageName.equals(providerPkgName)) {
-                    setAppIdleAsync(packageName, false, userId);
-                }
-            } catch (NameNotFoundException e) {
-                // Shouldn't happen
-            }
-        }
-    }
-
-    /**
-     * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
-     * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
-     * the threshold for idle.
-     *
-     * This method is always called from the handler thread, so not much synchronization is
-     * required.
-     */
-    void forceIdleState(String packageName, int userId, boolean idle) {
-        final int appId = getAppId(packageName);
-        if (appId < 0) return;
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
-
-        final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
-                userId, elapsedRealtime);
-        synchronized (mAppIdleLock) {
-            mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
-        }
-        final boolean stillIdle = isAppIdleFiltered(packageName, appId,
-                userId, elapsedRealtime);
-        // Inform listeners if necessary
-        if (previouslyIdle != stillIdle) {
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
-                    /* idle = */ stillIdle ? 1 : 0, packageName));
-            if (!stillIdle) {
-                notifyBatteryStats(packageName, userId, idle);
-            }
+            mAppStandby.reportEvent(event, elapsedRealtime, userId);
         }
     }
 
@@ -813,9 +375,7 @@
         synchronized (mLock) {
             Slog.i(TAG, "Removing user " + userId + " and all data.");
             mUserState.remove(userId);
-            synchronized (mAppIdleLock) {
-                mAppIdleHistory.onUserRemoved(userId);
-            }
+            mAppStandby.onUserRemoved(userId);
             cleanUpRemovedUsersLocked();
         }
     }
@@ -887,253 +447,6 @@
         }
     }
 
-    private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
-        synchronized (mAppIdleLock) {
-            return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
-        }
-    }
-
-    void addListener(AppIdleStateChangeListener listener) {
-        synchronized (mAppIdleLock) {
-            if (!mPackageAccessListeners.contains(listener)) {
-                mPackageAccessListeners.add(listener);
-            }
-        }
-    }
-
-    void removeListener(AppIdleStateChangeListener listener) {
-        synchronized (mAppIdleLock) {
-            mPackageAccessListeners.remove(listener);
-        }
-    }
-
-    int getAppId(String packageName) {
-        try {
-            ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
-                    PackageManager.MATCH_ANY_USER
-                            | PackageManager.MATCH_DISABLED_COMPONENTS);
-            return ai.uid;
-        } catch (NameNotFoundException re) {
-            return -1;
-        }
-    }
-
-    boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
-            boolean shouldObfuscateInstantApps) {
-        if (isParoledOrCharging()) {
-            return false;
-        }
-        if (shouldObfuscateInstantApps &&
-                mPackageManagerInternal.isPackageEphemeral(userId, packageName)) {
-            return false;
-        }
-        return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
-    }
-
-    /**
-     * Checks if an app has been idle for a while and filters out apps that are excluded.
-     * It returns false if the current system state allows all apps to be considered active.
-     * This happens if the device is plugged in or temporarily allowed to make exceptions.
-     * Called by interface impls.
-     */
-    private boolean isAppIdleFiltered(String packageName, int appId, int userId,
-            long elapsedRealtime) {
-        if (packageName == null) return false;
-        // If not enabled at all, of course nobody is ever idle.
-        if (!mAppIdleEnabled) {
-            return false;
-        }
-        if (appId < Process.FIRST_APPLICATION_UID) {
-            // System uids never go idle.
-            return false;
-        }
-        if (packageName.equals("android")) {
-            // Nor does the framework (which should be redundant with the above, but for MR1 we will
-            // retain this for safety).
-            return false;
-        }
-        if (mSystemServicesReady) {
-            try {
-                // We allow all whitelisted apps, including those that don't want to be whitelisted
-                // for idle mode, because app idle (aka app standby) is really not as big an issue
-                // for controlling who participates vs. doze mode.
-                if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
-                    return false;
-                }
-            } catch (RemoteException re) {
-                throw re.rethrowFromSystemServer();
-            }
-
-            if (isActiveDeviceAdmin(packageName, userId)) {
-                return false;
-            }
-
-            if (isActiveNetworkScorer(packageName)) {
-                return false;
-            }
-
-            if (mAppWidgetManager != null
-                    && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
-                return false;
-            }
-
-            if (isDeviceProvisioningPackage(packageName)) {
-                return false;
-            }
-        }
-
-        if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
-            return false;
-        }
-
-        // Check this last, as it is the most expensive check
-        // TODO: Optimize this by fetching the carrier privileged apps ahead of time
-        if (isCarrierApp(packageName)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    int[] getIdleUidsForUser(int userId) {
-        if (!mAppIdleEnabled) {
-            return new int[0];
-        }
-
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
-
-        List<ApplicationInfo> apps;
-        try {
-            ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
-                    .getInstalledApplications(/* flags= */ 0, userId);
-            if (slice == null) {
-                return new int[0];
-            }
-            apps = slice.getList();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
-        // State of each uid.  Key is the uid.  Value lower 16 bits is the number of apps
-        // associated with that uid, upper 16 bits is the number of those apps that is idle.
-        SparseIntArray uidStates = new SparseIntArray();
-
-        // Now resolve all app state.  Iterating over all apps, keeping track of how many
-        // we find for each uid and how many of those are idle.
-        for (int i = apps.size() - 1; i >= 0; i--) {
-            ApplicationInfo ai = apps.get(i);
-
-            // Check whether this app is idle.
-            boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
-                    userId, elapsedRealtime);
-
-            int index = uidStates.indexOfKey(ai.uid);
-            if (index < 0) {
-                uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
-            } else {
-                int value = uidStates.valueAt(index);
-                uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
-            }
-        }
-        if (DEBUG) {
-            Slog.d(TAG, "getIdleUids took " + (SystemClock.elapsedRealtime() - elapsedRealtime));
-        }
-        int numIdle = 0;
-        for (int i = uidStates.size() - 1; i >= 0; i--) {
-            int value = uidStates.valueAt(i);
-            if ((value&0x7fff) == (value>>16)) {
-                numIdle++;
-            }
-        }
-
-        int[] res = new int[numIdle];
-        numIdle = 0;
-        for (int i = uidStates.size() - 1; i >= 0; i--) {
-            int value = uidStates.valueAt(i);
-            if ((value&0x7fff) == (value>>16)) {
-                res[numIdle] = uidStates.keyAt(i);
-                numIdle++;
-            }
-        }
-
-        return res;
-    }
-
-    void setAppIdleAsync(String packageName, boolean idle, int userId) {
-        if (packageName == null) return;
-
-        mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
-                .sendToTarget();
-    }
-
-    private boolean isActiveDeviceAdmin(String packageName, int userId) {
-        DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
-        if (dpm == null) return false;
-        return dpm.packageHasActiveAdmins(packageName, userId);
-    }
-
-    /**
-     * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
-     * returns {@code false}.
-     */
-    private boolean isDeviceProvisioningPackage(String packageName) {
-        String deviceProvisioningPackage = getContext().getResources().getString(
-                com.android.internal.R.string.config_deviceProvisioningPackage);
-        return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
-    }
-
-    private boolean isCarrierApp(String packageName) {
-        synchronized (mAppIdleLock) {
-            if (!mHaveCarrierPrivilegedApps) {
-                fetchCarrierPrivilegedAppsLA();
-            }
-            if (mCarrierPrivilegedApps != null) {
-                return mCarrierPrivilegedApps.contains(packageName);
-            }
-            return false;
-        }
-    }
-
-    void clearCarrierPrivilegedApps() {
-        if (DEBUG) {
-            Slog.i(TAG, "Clearing carrier privileged apps list");
-        }
-        synchronized (mAppIdleLock) {
-            mHaveCarrierPrivilegedApps = false;
-            mCarrierPrivilegedApps = null; // Need to be refetched.
-        }
-    }
-
-    @GuardedBy("mAppIdleLock")
-    private void fetchCarrierPrivilegedAppsLA() {
-        TelephonyManager telephonyManager =
-                getContext().getSystemService(TelephonyManager.class);
-        mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
-        mHaveCarrierPrivilegedApps = true;
-        if (DEBUG) {
-            Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
-        }
-    }
-
-    private boolean isActiveNetworkScorer(String packageName) {
-        NetworkScoreManager nsm = (NetworkScoreManager) getContext().getSystemService(
-                Context.NETWORK_SCORE_SERVICE);
-        return packageName != null && packageName.equals(nsm.getActiveScorerPackage());
-    }
-
-    void informListeners(String packageName, int userId, boolean isIdle) {
-        for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
-            listener.onAppIdleStateChanged(packageName, userId, isIdle);
-        }
-    }
-
-    void informParoleStateChanged() {
-        final boolean paroled = isParoledOrCharging();
-        for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
-            listener.onParoleStateChanged(paroled);
-        }
-    }
-
     private static boolean validRange(long currentTime, long beginTime, long endTime) {
         return beginTime <= currentTime && beginTime < endTime;
     }
@@ -1143,15 +456,10 @@
         for (int i = 0; i < userCount; i++) {
             UserUsageStatsService service = mUserState.valueAt(i);
             service.persistActiveStats();
-            synchronized (mAppIdleLock) {
-                mAppIdleHistory.writeAppIdleTimes(mUserState.keyAt(i));
-            }
+            mAppStandby.flushToDisk(mUserState.keyAt(i));
         }
-        // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
-        // considered not-idle, which is the safest outcome in such an event.
-        synchronized (mAppIdleLock) {
-            mAppIdleHistory.writeAppIdleDurations();
-        }
+        mAppStandby.flushDurationsToDisk();
+
         mHandler.removeMessages(MSG_FLUSH_TO_DISK);
     }
 
@@ -1166,7 +474,8 @@
 
             final int userCount = mUserState.size();
             for (int i = 0; i < userCount; i++) {
-                idpw.printPair("user", mUserState.keyAt(i));
+                int userId = mUserState.keyAt(i);
+                idpw.printPair("user", userId);
                 idpw.println();
                 idpw.increaseIndent();
                 if (argSet.contains("--checkin")) {
@@ -1176,57 +485,19 @@
                     idpw.println();
                     if (args.length > 0) {
                         if ("history".equals(args[0])) {
-                            synchronized (mAppIdleLock) {
-                                mAppIdleHistory.dumpHistory(idpw, mUserState.keyAt(i));
-                            }
+                            mAppStandby.dumpHistory(idpw, userId);
                         } else if ("flush".equals(args[0])) {
-                            UsageStatsService.this.flushToDiskLocked();
+                            flushToDiskLocked();
                             pw.println("Flushed stats to disk");
                         }
                     }
                 }
-                synchronized (mAppIdleLock) {
-                    mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
-                }
+                mAppStandby.dumpUser(idpw, userId);
                 idpw.decreaseIndent();
             }
 
             pw.println();
-            synchronized (mAppIdleLock) {
-                pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
-                        + "): " + mCarrierPrivilegedApps);
-            }
-
-            pw.println();
-            pw.println("Settings:");
-
-            pw.print("  mAppIdleDurationMillis=");
-            TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
-            pw.println();
-
-            pw.print("  mAppIdleWallclockThresholdMillis=");
-            TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
-            pw.println();
-
-            pw.print("  mCheckIdleIntervalMillis=");
-            TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
-            pw.println();
-
-            pw.print("  mAppIdleParoleIntervalMillis=");
-            TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw);
-            pw.println();
-
-            pw.print("  mAppIdleParoleDurationMillis=");
-            TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
-            pw.println();
-
-            pw.println();
-            pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
-            pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
-            pw.print(" mCharging="); pw.print(mCharging);
-            pw.print(" mLastAppIdleParoledTime=");
-            TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
-            pw.println();
+            mAppStandby.dumpState(args, pw);
         }
     }
 
@@ -1250,50 +521,6 @@
                     onUserRemoved(msg.arg1);
                     break;
 
-                case MSG_INFORM_LISTENERS:
-                    informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
-                    break;
-
-                case MSG_FORCE_IDLE_STATE:
-                    forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
-                    break;
-
-                case MSG_CHECK_IDLE_STATES:
-                    if (checkIdleStates(msg.arg1)) {
-                        mHandler.sendMessageDelayed(mHandler.obtainMessage(
-                                MSG_CHECK_IDLE_STATES, msg.arg1, 0),
-                                mCheckIdleIntervalMillis);
-                    }
-                    break;
-
-                case MSG_ONE_TIME_CHECK_IDLE_STATES:
-                    mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
-                    checkIdleStates(UserHandle.USER_ALL);
-                    break;
-
-                case MSG_CHECK_PAROLE_TIMEOUT:
-                    checkParoleTimeout();
-                    break;
-
-                case MSG_PAROLE_END_TIMEOUT:
-                    if (DEBUG) Slog.d(TAG, "Ending parole");
-                    setAppIdleParoled(false);
-                    break;
-
-                case MSG_REPORT_CONTENT_PROVIDER_USAGE:
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    reportContentProviderUsage((String) args.arg1, // authority name
-                            (String) args.arg2, // package name
-                            (int) args.arg3); // userId
-                    args.recycle();
-                    break;
-
-                case MSG_PAROLE_STATE_CHANGED:
-                    if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
-                            + ", Charging state:" + mCharging);
-                    informParoleStateChanged();
-                    break;
-
                 default:
                     super.handleMessage(msg);
                     break;
@@ -1301,72 +528,6 @@
         }
     }
 
-    /**
-     * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
-     */
-    private class SettingsObserver extends ContentObserver {
-        /**
-         * This flag has been used to disable app idle on older builds with bug b/26355386.
-         */
-        @Deprecated
-        private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
-
-        private static final String KEY_IDLE_DURATION = "idle_duration2";
-        private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
-        private static final String KEY_PAROLE_INTERVAL = "parole_interval";
-        private static final String KEY_PAROLE_DURATION = "parole_duration";
-
-        private final KeyValueListParser mParser = new KeyValueListParser(',');
-
-        SettingsObserver(Handler handler) {
-            super(handler);
-        }
-
-        void registerObserver() {
-            getContext().getContentResolver().registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.APP_IDLE_CONSTANTS), false, this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            updateSettings();
-            postOneTimeCheckIdleStates();
-        }
-
-        void updateSettings() {
-            synchronized (mAppIdleLock) {
-                // Look at global settings for this.
-                // TODO: Maybe apply different thresholds for different users.
-                try {
-                    mParser.setString(Settings.Global.getString(getContext().getContentResolver(),
-                            Settings.Global.APP_IDLE_CONSTANTS));
-                } catch (IllegalArgumentException e) {
-                    Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
-                    // fallthrough, mParser is empty and all defaults will be returned.
-                }
-
-                // Default: 12 hours of screen-on time sans dream-time
-                mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
-                       COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
-
-                mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
-                        COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
-
-                mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
-                        COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
-
-                // Default: 24 hours between paroles
-                mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
-                        COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
-
-                mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
-                        COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
-                mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
-                        mAppIdleScreenThresholdMillis);
-            }
-        }
-    }
-
     private final class BinderService extends IUsageStatsManager.Stub {
 
         private boolean hasPermission(String callingPackage) {
@@ -1462,7 +623,8 @@
                     Binder.getCallingUid(), userId);
             final long token = Binder.clearCallingIdentity();
             try {
-                return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId,
+                return mAppStandby.isAppIdleFilteredOrParoled(
+                        packageName, userId,
                         SystemClock.elapsedRealtime(), obfuscateInstantApps);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -1483,9 +645,9 @@
                     "No permission to change app idle state");
             final long token = Binder.clearCallingIdentity();
             try {
-                final int appId = getAppId(packageName);
+                final int appId = mAppStandby.getAppId(packageName);
                 if (appId < 0) return;
-                UsageStatsService.this.setAppIdleAsync(packageName, idle, userId);
+                mAppStandby.setAppIdleAsync(packageName, idle, userId);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1509,7 +671,7 @@
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.BIND_CARRIER_SERVICES,
                     "onCarrierPrivilegedAppsChanged can only be called by privileged apps.");
-            UsageStatsService.this.clearCarrierPrivilegedApps();
+            mAppStandby.clearCarrierPrivilegedApps();
         }
 
         @Override
@@ -1624,28 +786,23 @@
 
         @Override
         public void reportContentProviderUsage(String name, String packageName, int userId) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = name;
-            args.arg2 = packageName;
-            args.arg3 = userId;
-            mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
-                    .sendToTarget();
+            mAppStandby.postReportContentProviderUsage(name, packageName, userId);
         }
 
         @Override
         public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
-            return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId,
-                    SystemClock.elapsedRealtime());
+            return mAppStandby.isAppIdleFiltered(packageName, uidForAppId,
+                    userId, SystemClock.elapsedRealtime());
         }
 
         @Override
         public int[] getIdleUidsForUser(int userId) {
-            return UsageStatsService.this.getIdleUidsForUser(userId);
+            return mAppStandby.getIdleUidsForUser(userId);
         }
 
         @Override
         public boolean isAppIdleParoleOn() {
-            return isParoledOrCharging();
+            return mAppStandby.isParoledOrCharging();
         }
 
         @Override
@@ -1658,20 +815,20 @@
 
         @Override
         public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) {
-            UsageStatsService.this.addListener(listener);
+            mAppStandby.addListener(listener);
             listener.onParoleStateChanged(isAppIdleParoleOn());
         }
 
         @Override
         public void removeAppIdleStateChangeListener(
                 AppIdleStateChangeListener listener) {
-            UsageStatsService.this.removeListener(listener);
+            mAppStandby.removeListener(listener);
         }
 
         @Override
         public byte[] getBackupPayload(int user, String key) {
             // Check to ensure that only user 0's data is b/r for now
-            synchronized (UsageStatsService.this.mLock) {
+            synchronized (mLock) {
                 if (user == UserHandle.USER_SYSTEM) {
                     final UserUsageStatsService userStats =
                             getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
@@ -1684,7 +841,7 @@
 
         @Override
         public void applyRestoredPayload(int user, String key, byte[] payload) {
-            synchronized (UsageStatsService.this.mLock) {
+            synchronized (mLock) {
                 if (user == UserHandle.USER_SYSTEM) {
                     final UserUsageStatsService userStats =
                             getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 68c1d5f..acc27be 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -314,7 +314,11 @@
             return null;
         }
 
-        mDevicesParser.scan();
+        if (!mDevicesParser.scan()) {
+            Slog.e(TAG, "Error parsing ALSA devices file.");
+            return null;
+        }
+
         int device = mDevicesParser.getDefaultDeviceNum(card);
 
         boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card);
diff --git a/tests/net/java/com/android/internal/util/BitUtilsTest.java b/tests/net/java/com/android/internal/util/BitUtilsTest.java
index 0ad8a21..f4dc12a 100644
--- a/tests/net/java/com/android/internal/util/BitUtilsTest.java
+++ b/tests/net/java/com/android/internal/util/BitUtilsTest.java
@@ -56,6 +56,25 @@
     }
 
     @Test
+    public void testUnsignedShortComposition() {
+        byte b0 = 0;
+        byte b1 = 1;
+        byte b2 = 2;
+        byte b10 = 10;
+        byte b16 = 16;
+        byte b128 = -128;
+        byte b224 = -32;
+        byte b255 = -1;
+        assertEquals(0x0000, uint16(b0, b0));
+        assertEquals(0xffff, uint16(b255, b255));
+        assertEquals(0x0a01, uint16(b10, b1));
+        assertEquals(0x8002, uint16(b128, b2));
+        assertEquals(0x01ff, uint16(b1, b255));
+        assertEquals(0x80ff, uint16(b128, b255));
+        assertEquals(0xe010, uint16(b224, b16));
+    }
+
+    @Test
     public void testUnsignedIntWideningConversions() {
         assertEquals(0, uint32(0));
         assertEquals(1, uint32(1));
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 7a8a4ee..ff51d51 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -83,9 +83,13 @@
         "configuration/ConfigurationParser.cpp",
         "filter/AbiFilter.cpp",
         "filter/ConfigFilter.cpp",
-        "flatten/Archive.cpp",
-        "flatten/TableFlattener.cpp",
-        "flatten/XmlFlattener.cpp",
+        "format/Archive.cpp",
+        "format/binary/BinaryResourceParser.cpp",
+        "format/binary/ResChunkPullParser.cpp",
+        "format/binary/TableFlattener.cpp",
+        "format/binary/XmlFlattener.cpp",
+        "format/proto/ProtoDeserialize.cpp",
+        "format/proto/ProtoSerialize.cpp",
         "io/BigBufferStreams.cpp",
         "io/File.cpp",
         "io/FileInputStream.cpp",
@@ -106,13 +110,9 @@
         "optimize/ResourceDeduper.cpp",
         "optimize/VersionCollapser.cpp",
         "process/SymbolTable.cpp",
-        "proto/ProtoDeserialize.cpp",
-        "proto/ProtoSerialize.cpp",
         "split/TableSplitter.cpp",
         "text/Unicode.cpp",
         "text/Utf8Iterator.cpp",
-        "unflatten/BinaryResourceParser.cpp",
-        "unflatten/ResChunkPullParser.cpp",
         "util/BigBuffer.cpp",
         "util/Files.cpp",
         "util/Util.cpp",
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 6f2b865..c1815c8 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -18,9 +18,9 @@
 
 #include "ResourceValues.h"
 #include "ValueVisitor.h"
-#include "flatten/Archive.h"
-#include "flatten/TableFlattener.h"
-#include "flatten/XmlFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/TableFlattener.h"
+#include "format/binary/XmlFlattener.h"
 #include "io/BigBufferInputStream.h"
 #include "io/Util.h"
 #include "xml/XmlDom.h"
diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h
index d779b7e..d2dd5cf 100644
--- a/tools/aapt2/LoadedApk.h
+++ b/tools/aapt2/LoadedApk.h
@@ -21,27 +21,35 @@
 
 #include "ResourceTable.h"
 #include "filter/Filter.h"
-#include "flatten/Archive.h"
-#include "flatten/TableFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/BinaryResourceParser.h"
+#include "format/binary/TableFlattener.h"
 #include "io/ZipArchive.h"
-#include "unflatten/BinaryResourceParser.h"
 #include "xml/XmlDom.h"
 
 namespace aapt {
 
-/** Info about an APK loaded in memory. */
+// Info about an APK loaded in memory.
 class LoadedApk {
  public:
-  LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk,
-            std::unique_ptr<ResourceTable> table)
-      : source_(source), apk_(std::move(apk)), table_(std::move(table)) {
+  LoadedApk(
+      const Source& source,
+      std::unique_ptr<io::IFileCollection> apk,
+      std::unique_ptr<ResourceTable> table)
+      : source_(source), apk_(std::move(apk)), table_(std::move(table)) {}
+  virtual ~LoadedApk() = default;
+
+  io::IFileCollection* GetFileCollection() {
+    return apk_.get();
   }
 
-  io::IFileCollection* GetFileCollection() { return apk_.get(); }
+  ResourceTable* GetResourceTable() {
+    return table_.get();
+  }
 
-  ResourceTable* GetResourceTable() { return table_.get(); }
-
-  const Source& GetSource() { return source_; }
+  const Source& GetSource() {
+    return source_;
+  }
 
   /**
    * Writes the APK on disk at the given path, while also removing the resource
@@ -70,11 +78,11 @@
                                                     const android::StringPiece& path);
 
  private:
+  DISALLOW_COPY_AND_ASSIGN(LoadedApk);
+
   Source source_;
   std::unique_ptr<io::IFileCollection> apk_;
   std::unique_ptr<ResourceTable> table_;
-
-  DISALLOW_COPY_AND_ASSIGN(LoadedApk);
 };
 
 }  // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index f193fe0..6fac6e9 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -23,12 +23,12 @@
 
 #include "NameMangler.h"
 #include "SdkConstants.h"
-#include "flatten/ResourceTypeExtensions.h"
+#include "format/binary/ResourceTypeExtensions.h"
 #include "util/Files.h"
 #include "util/Util.h"
 
-using android::StringPiece;
-using android::StringPiece16;
+using ::android::StringPiece;
+using ::android::StringPiece16;
 
 namespace aapt {
 namespace ResourceUtils {
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 0690dc1..a5e6aefd1 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -35,12 +35,12 @@
 #include "compile/Png.h"
 #include "compile/PseudolocaleGenerator.h"
 #include "compile/XmlIdCollector.h"
-#include "flatten/Archive.h"
-#include "flatten/XmlFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/XmlFlattener.h"
+#include "format/proto/ProtoSerialize.h"
 #include "io/BigBufferOutputStream.h"
 #include "io/FileInputStream.h"
 #include "io/Util.h"
-#include "proto/ProtoSerialize.h"
 #include "util/Files.h"
 #include "util/Maybe.h"
 #include "util/Util.h"
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 0dedc91..44032f6 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -21,10 +21,10 @@
 #include "Debug.h"
 #include "Diagnostics.h"
 #include "Flags.h"
+#include "format/binary/BinaryResourceParser.h"
+#include "format/proto/ProtoDeserialize.h"
 #include "io/ZipArchive.h"
 #include "process/IResourceTableConsumer.h"
-#include "proto/ProtoDeserialize.h"
-#include "unflatten/BinaryResourceParser.h"
 #include "util/Files.h"
 
 using ::android::StringPiece;
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index f72069c..88e0f69 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -38,9 +38,12 @@
 #include "cmd/Util.h"
 #include "compile/IdAssigner.h"
 #include "filter/ConfigFilter.h"
-#include "flatten/Archive.h"
-#include "flatten/TableFlattener.h"
-#include "flatten/XmlFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/BinaryResourceParser.h"
+#include "format/binary/TableFlattener.h"
+#include "format/binary/XmlFlattener.h"
+#include "format/proto/ProtoDeserialize.h"
+#include "format/proto/ProtoSerialize.h"
 #include "io/BigBufferInputStream.h"
 #include "io/FileInputStream.h"
 #include "io/FileSystem.h"
@@ -58,10 +61,7 @@
 #include "optimize/VersionCollapser.h"
 #include "process/IResourceTableConsumer.h"
 #include "process/SymbolTable.h"
-#include "proto/ProtoDeserialize.h"
-#include "proto/ProtoSerialize.h"
 #include "split/TableSplitter.h"
-#include "unflatten/BinaryResourceParser.h"
 #include "util/Files.h"
 #include "xml/XmlDom.h"
 
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 56b61d0..67ac67a 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -31,8 +31,8 @@
 #include "cmd/Util.h"
 #include "configuration/ConfigurationParser.h"
 #include "filter/AbiFilter.h"
-#include "flatten/TableFlattener.h"
-#include "flatten/XmlFlattener.h"
+#include "format/binary/TableFlattener.h"
+#include "format/binary/XmlFlattener.h"
 #include "io/BigBufferInputStream.h"
 #include "io/Util.h"
 #include "optimize/MultiApkGenerator.h"
diff --git a/tools/aapt2/compile/PngChunkFilter.cpp b/tools/aapt2/compile/PngChunkFilter.cpp
index 5af91fd..bc2e699 100644
--- a/tools/aapt2/compile/PngChunkFilter.cpp
+++ b/tools/aapt2/compile/PngChunkFilter.cpp
@@ -21,8 +21,8 @@
 
 #include "io/Io.h"
 
-using android::StringPiece;
-using android::base::StringPrintf;
+using ::android::StringPiece;
+using ::android::base::StringPrintf;
 
 namespace aapt {
 
diff --git a/tools/aapt2/flatten/Archive.cpp b/tools/aapt2/format/Archive.cpp
similarity index 95%
rename from tools/aapt2/flatten/Archive.cpp
rename to tools/aapt2/format/Archive.cpp
index 5f8bd06..d152a9c 100644
--- a/tools/aapt2/flatten/Archive.cpp
+++ b/tools/aapt2/format/Archive.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "flatten/Archive.h"
+#include "format/Archive.h"
 
 #include <cstdio>
 #include <memory>
@@ -106,9 +106,13 @@
     return !in->HadError();
   }
 
-  bool HadError() const override { return !error_.empty(); }
+  bool HadError() const override {
+    return !error_.empty();
+  }
 
-  std::string GetError() const override { return error_; }
+  std::string GetError() const override {
+    return error_;
+  }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(DirectoryWriter);
@@ -221,9 +225,13 @@
     }
   }
 
-  bool HadError() const override { return !error_.empty(); }
+  bool HadError() const override {
+    return !error_.empty();
+  }
 
-  std::string GetError() const override { return error_; }
+  std::string GetError() const override {
+    return error_;
+  }
 
   virtual ~ZipFileWriter() {
     if (writer_) {
diff --git a/tools/aapt2/flatten/Archive.h b/tools/aapt2/format/Archive.h
similarity index 96%
rename from tools/aapt2/flatten/Archive.h
rename to tools/aapt2/format/Archive.h
index 4ee4ce7..4e8a39d 100644
--- a/tools/aapt2/flatten/Archive.h
+++ b/tools/aapt2/format/Archive.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_FLATTEN_ARCHIVE_H
-#define AAPT_FLATTEN_ARCHIVE_H
+#ifndef AAPT_FORMAT_ARCHIVE_H
+#define AAPT_FORMAT_ARCHIVE_H
 
 #include <fstream>
 #include <memory>
@@ -78,4 +78,4 @@
 
 }  // namespace aapt
 
-#endif /* AAPT_FLATTEN_ARCHIVE_H */
+#endif /* AAPT_FORMAT_ARCHIVE_H */
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
similarity index 76%
rename from tools/aapt2/unflatten/BinaryResourceParser.cpp
rename to tools/aapt2/format/binary/BinaryResourceParser.cpp
index b87278b..95eec4a 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "unflatten/BinaryResourceParser.h"
+#include "format/binary/BinaryResourceParser.h"
 
 #include <algorithm>
 #include <map>
@@ -31,15 +31,15 @@
 #include "ResourceValues.h"
 #include "Source.h"
 #include "ValueVisitor.h"
-#include "unflatten/ResChunkPullParser.h"
+#include "format/binary/ResChunkPullParser.h"
 #include "util/Util.h"
 
-namespace aapt {
-
 using namespace android;
 
 using ::android::base::StringPrintf;
 
+namespace aapt {
+
 namespace {
 
 // Visitor that converts a reference's resource ID to a resource name, given a mapping from
@@ -118,15 +118,11 @@
   return true;
 }
 
-/**
- * Parses the resource table, which contains all the packages, types, and
- * entries.
- */
+// Parses the resource table, which contains all the packages, types, and entries.
 bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) {
   const ResTable_header* table_header = ConvertTo<ResTable_header>(chunk);
   if (!table_header) {
-    context_->GetDiagnostics()->Error(DiagMessage(source_)
-                                      << "corrupt ResTable_header chunk");
+    context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_header chunk");
     return false;
   }
 
@@ -136,21 +132,20 @@
     switch (util::DeviceToHost16(parser.chunk()->type)) {
       case android::RES_STRING_POOL_TYPE:
         if (value_pool_.getError() == NO_INIT) {
-          status_t err = value_pool_.setTo(
-              parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
+          status_t err =
+              value_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
           if (err != NO_ERROR) {
-            context_->GetDiagnostics()->Error(
-                DiagMessage(source_) << "corrupt string pool in ResTable: "
-                                     << value_pool_.getError());
+            context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                              << "corrupt string pool in ResTable: "
+                                              << value_pool_.getError());
             return false;
           }
 
           // Reserve some space for the strings we are going to add.
-          table_->string_pool.HintWillAdd(value_pool_.size(),
-                                          value_pool_.styleCount());
+          table_->string_pool.HintWillAdd(value_pool_.size(), value_pool_.styleCount());
         } else {
-          context_->GetDiagnostics()->Warn(
-              DiagMessage(source_) << "unexpected string pool in ResTable");
+          context_->GetDiagnostics()->Warn(DiagMessage(source_)
+                                           << "unexpected string pool in ResTable");
         }
         break;
 
@@ -169,8 +164,8 @@
   }
 
   if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
-    context_->GetDiagnostics()->Error(
-        DiagMessage(source_) << "corrupt resource table: " << parser.error());
+    context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                      << "corrupt resource table: " << parser.error());
     return false;
   }
   return true;
@@ -187,26 +182,25 @@
 
   uint32_t package_id = util::DeviceToHost32(package_header->id);
   if (package_id > std::numeric_limits<uint8_t>::max()) {
-    context_->GetDiagnostics()->Error(
-        DiagMessage(source_) << "package ID is too big (" << package_id << ")");
+    context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                      << "package ID is too big (" << package_id << ")");
     return false;
   }
 
   // Extract the package name.
-  size_t len = strnlen16((const char16_t*)package_header->name,
-                         arraysize(package_header->name));
+  size_t len = strnlen16((const char16_t*)package_header->name, arraysize(package_header->name));
   std::u16string package_name;
   package_name.resize(len);
   for (size_t i = 0; i < len; i++) {
     package_name[i] = util::DeviceToHost16(package_header->name[i]);
   }
 
-  ResourceTablePackage* package = table_->CreatePackage(
-      util::Utf16ToUtf8(package_name), static_cast<uint8_t>(package_id));
+  ResourceTablePackage* package =
+      table_->CreatePackage(util::Utf16ToUtf8(package_name), static_cast<uint8_t>(package_id));
   if (!package) {
-    context_->GetDiagnostics()->Error(
-        DiagMessage(source_) << "incompatible package '" << package_name
-                             << "' with ID " << package_id);
+    context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                      << "incompatible package '" << package_name << "' with ID "
+                                      << package_id);
     return false;
   }
 
@@ -221,23 +215,21 @@
     switch (util::DeviceToHost16(parser.chunk()->type)) {
       case android::RES_STRING_POOL_TYPE:
         if (type_pool_.getError() == NO_INIT) {
-          status_t err = type_pool_.setTo(
-              parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
+          status_t err =
+              type_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
           if (err != NO_ERROR) {
             context_->GetDiagnostics()->Error(DiagMessage(source_)
                                               << "corrupt type string pool in "
-                                              << "ResTable_package: "
-                                              << type_pool_.getError());
+                                              << "ResTable_package: " << type_pool_.getError());
             return false;
           }
         } else if (key_pool_.getError() == NO_INIT) {
-          status_t err = key_pool_.setTo(
-              parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
+          status_t err =
+              key_pool_.setTo(parser.chunk(), util::DeviceToHost32(parser.chunk()->size));
           if (err != NO_ERROR) {
             context_->GetDiagnostics()->Error(DiagMessage(source_)
                                               << "corrupt key string pool in "
-                                              << "ResTable_package: "
-                                              << key_pool_.getError());
+                                              << "ResTable_package: " << key_pool_.getError());
             return false;
           }
         } else {
@@ -272,8 +264,8 @@
   }
 
   if (parser.event() == ResChunkPullParser::Event::kBadDocument) {
-    context_->GetDiagnostics()->Error(
-        DiagMessage(source_) << "corrupt ResTable_package: " << parser.error());
+    context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                      << "corrupt ResTable_package: " << parser.error());
     return false;
   }
 
@@ -286,22 +278,19 @@
 
 bool BinaryResourceParser::ParseTypeSpec(const ResChunk_header* chunk) {
   if (type_pool_.getError() != NO_ERROR) {
-    context_->GetDiagnostics()->Error(DiagMessage(source_)
-                                      << "missing type string pool");
+    context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing type string pool");
     return false;
   }
 
   const ResTable_typeSpec* type_spec = ConvertTo<ResTable_typeSpec>(chunk);
   if (!type_spec) {
-    context_->GetDiagnostics()->Error(DiagMessage(source_)
-                                      << "corrupt ResTable_typeSpec chunk");
+    context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_typeSpec chunk");
     return false;
   }
 
   if (type_spec->id == 0) {
     context_->GetDiagnostics()->Error(DiagMessage(source_)
-                                      << "ResTable_typeSpec has invalid id: "
-                                      << type_spec->id);
+                                      << "ResTable_typeSpec has invalid id: " << type_spec->id);
     return false;
   }
   return true;
@@ -310,14 +299,12 @@
 bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
                                      const ResChunk_header* chunk) {
   if (type_pool_.getError() != NO_ERROR) {
-    context_->GetDiagnostics()->Error(DiagMessage(source_)
-                                      << "missing type string pool");
+    context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing type string pool");
     return false;
   }
 
   if (key_pool_.getError() != NO_ERROR) {
-    context_->GetDiagnostics()->Error(DiagMessage(source_)
-                                      << "missing key string pool");
+    context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing key string pool");
     return false;
   }
 
@@ -325,15 +312,13 @@
   // a lot and has its own code to handle variable size.
   const ResTable_type* type = ConvertTo<ResTable_type, kResTableTypeMinSize>(chunk);
   if (!type) {
-    context_->GetDiagnostics()->Error(DiagMessage(source_)
-                                      << "corrupt ResTable_type chunk");
+    context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_type chunk");
     return false;
   }
 
   if (type->id == 0) {
     context_->GetDiagnostics()->Error(DiagMessage(source_)
-                                      << "ResTable_type has invalid id: "
-                                      << (int)type->id);
+                                      << "ResTable_type has invalid id: " << (int)type->id);
     return false;
   }
 
@@ -344,9 +329,9 @@
 
   const ResourceType* parsed_type = ParseResourceType(type_str);
   if (!parsed_type) {
-    context_->GetDiagnostics()->Error(
-        DiagMessage(source_) << "invalid type name '" << type_str
-                             << "' for type with ID " << (int)type->id);
+    context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                      << "invalid type name '" << type_str << "' for type with ID "
+                                      << (int)type->id);
     return false;
   }
 
@@ -357,12 +342,10 @@
       continue;
     }
 
-    const ResourceName name(
-        package->name, *parsed_type,
-        util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)));
+    const ResourceName name(package->name, *parsed_type,
+                            util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)));
 
-    const ResourceId res_id(package->id.value(), type->id,
-                            static_cast<uint16_t>(it.index()));
+    const ResourceId res_id(package->id.value(), type->id, static_cast<uint16_t>(it.index()));
 
     std::unique_ptr<Value> resource_value;
     if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
@@ -377,10 +360,9 @@
     }
 
     if (!resource_value) {
-      context_->GetDiagnostics()->Error(
-          DiagMessage(source_) << "failed to parse value for resource " << name
-                               << " (" << res_id << ") with configuration '"
-                               << config << "'");
+      context_->GetDiagnostics()->Error(DiagMessage(source_)
+                                        << "failed to parse value for resource " << name << " ("
+                                        << res_id << ") with configuration '" << config << "'");
       return false;
     }
 
@@ -433,19 +415,19 @@
     if (file_ref != nullptr) {
       file_ref->file = files_->FindFile(*file_ref->path);
       if (file_ref->file == nullptr) {
-        context_->GetDiagnostics()->Warn(DiagMessage() << "resource " << name << " for config '"
-                                                        << config << "' is a file reference to '"
-                                                        << *file_ref->path
-                                                        << "' but no such path exists");
+        context_->GetDiagnostics()->Warn(DiagMessage()
+                                         << "resource " << name << " for config '" << config
+                                         << "' is a file reference to '" << *file_ref->path
+                                         << "' but no such path exists");
       }
     }
   }
   return item;
 }
 
-std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(
-    const ResourceNameRef& name, const ConfigDescription& config,
-    const ResTable_map_entry* map) {
+std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(const ResourceNameRef& name,
+                                                           const ConfigDescription& config,
+                                                           const ResTable_map_entry* map) {
   switch (name.type) {
     case ResourceType::kStyle:
       return ParseStyle(name, config, map);
@@ -470,9 +452,9 @@
   return {};
 }
 
-std::unique_ptr<Style> BinaryResourceParser::ParseStyle(
-    const ResourceNameRef& name, const ConfigDescription& config,
-    const ResTable_map_entry* map) {
+std::unique_ptr<Style> BinaryResourceParser::ParseStyle(const ResourceNameRef& name,
+                                                        const ConfigDescription& config,
+                                                        const ResTable_map_entry* map) {
   std::unique_ptr<Style> style = util::make_unique<Style>();
   if (util::DeviceToHost32(map->parent.ident) != 0) {
     // The parent is a regular reference to a resource.
@@ -495,19 +477,16 @@
   return style;
 }
 
-std::unique_ptr<Attribute> BinaryResourceParser::ParseAttr(
-    const ResourceNameRef& name, const ConfigDescription& config,
-    const ResTable_map_entry* map) {
-  const bool is_weak =
-      (util::DeviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
+std::unique_ptr<Attribute> BinaryResourceParser::ParseAttr(const ResourceNameRef& name,
+                                                           const ConfigDescription& config,
+                                                           const ResTable_map_entry* map) {
+  const bool is_weak = (util::DeviceToHost16(map->flags) & ResTable_entry::FLAG_WEAK) != 0;
   std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(is_weak);
 
   // First we must discover what type of attribute this is. Find the type mask.
-  auto type_mask_iter =
-      std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
-        return util::DeviceToHost32(entry.name.ident) ==
-               ResTable_map::ATTR_TYPE;
-      });
+  auto type_mask_iter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
+    return util::DeviceToHost32(entry.name.ident) == ResTable_map::ATTR_TYPE;
+  });
 
   if (type_mask_iter != end(map)) {
     attr->type_mask = util::DeviceToHost32(type_mask_iter->value.data);
@@ -526,8 +505,7 @@
       continue;
     }
 
-    if (attr->type_mask &
-        (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
+    if (attr->type_mask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
       Attribute::Symbol symbol;
       symbol.value = util::DeviceToHost32(map_entry.value.data);
       symbol.symbol = Reference(util::DeviceToHost32(map_entry.name.ident));
@@ -539,9 +517,9 @@
   return attr;
 }
 
-std::unique_ptr<Array> BinaryResourceParser::ParseArray(
-    const ResourceNameRef& name, const ConfigDescription& config,
-    const ResTable_map_entry* map) {
+std::unique_ptr<Array> BinaryResourceParser::ParseArray(const ResourceNameRef& name,
+                                                        const ConfigDescription& config,
+                                                        const ResTable_map_entry* map) {
   std::unique_ptr<Array> array = util::make_unique<Array>();
   for (const ResTable_map& map_entry : map) {
     array->elements.push_back(ParseValue(name, config, map_entry.value));
@@ -549,9 +527,9 @@
   return array;
 }
 
-std::unique_ptr<Plural> BinaryResourceParser::ParsePlural(
-    const ResourceNameRef& name, const ConfigDescription& config,
-    const ResTable_map_entry* map) {
+std::unique_ptr<Plural> BinaryResourceParser::ParsePlural(const ResourceNameRef& name,
+                                                          const ConfigDescription& config,
+                                                          const ResTable_map_entry* map) {
   std::unique_ptr<Plural> plural = util::make_unique<Plural>();
   for (const ResTable_map& map_entry : map) {
     std::unique_ptr<Item> item = ParseValue(name, config, map_entry.value);
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h
similarity index 73%
rename from tools/aapt2/unflatten/BinaryResourceParser.h
rename to tools/aapt2/format/binary/BinaryResourceParser.h
index c41ada0..dc9a384 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_BINARY_RESOURCE_PARSER_H
-#define AAPT_BINARY_RESOURCE_PARSER_H
+#ifndef AAPT_FORMAT_BINARY_RESOURCEPARSER_H
+#define AAPT_FORMAT_BINARY_RESOURCEPARSER_H
 
 #include <string>
 
@@ -32,25 +32,17 @@
 
 struct SymbolTable_entry;
 
-/*
- * Parses a binary resource table (resources.arsc) and adds the entries
- * to a ResourceTable. This is different than the libandroidfw ResTable
- * in that it scans the table from top to bottom and doesn't require
- * support for random access. It is also able to parse non-runtime
- * chunks and types.
- */
+// Parses a binary resource table (resources.arsc) and adds the entries to a ResourceTable.
+// This is different than the libandroidfw ResTable in that it scans the table from top to bottom
+// and doesn't require support for random access.
 class BinaryResourceParser {
  public:
-  /*
-   * Creates a parser, which will read `len` bytes from `data`, and
-   * add any resources parsed to `table`. `source` is for logging purposes.
-   */
+  // Creates a parser, which will read `len` bytes from `data`, and add any resources parsed to
+  // `table`. `source` is for logging purposes.
   BinaryResourceParser(IAaptContext* context, ResourceTable* table, const Source& source,
                        const void* data, size_t data_len, io::IFileCollection* files = nullptr);
 
-  /*
-   * Parses the binary resource table and returns true if successful.
-   */
+  // Parses the binary resource table and returns true if successful.
   bool Parse();
 
  private:
@@ -59,31 +51,25 @@
   bool ParseTable(const android::ResChunk_header* chunk);
   bool ParsePackage(const android::ResChunk_header* chunk);
   bool ParseTypeSpec(const android::ResChunk_header* chunk);
-  bool ParseType(const ResourceTablePackage* package,
-                 const android::ResChunk_header* chunk);
+  bool ParseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
   bool ParseLibrary(const android::ResChunk_header* chunk);
 
   std::unique_ptr<Item> ParseValue(const ResourceNameRef& name, const ConfigDescription& config,
                                    const android::Res_value& value);
 
-  std::unique_ptr<Value> ParseMapEntry(const ResourceNameRef& name,
-                                       const ConfigDescription& config,
+  std::unique_ptr<Value> ParseMapEntry(const ResourceNameRef& name, const ConfigDescription& config,
                                        const android::ResTable_map_entry* map);
 
-  std::unique_ptr<Style> ParseStyle(const ResourceNameRef& name,
-                                    const ConfigDescription& config,
+  std::unique_ptr<Style> ParseStyle(const ResourceNameRef& name, const ConfigDescription& config,
                                     const android::ResTable_map_entry* map);
 
-  std::unique_ptr<Attribute> ParseAttr(const ResourceNameRef& name,
-                                       const ConfigDescription& config,
+  std::unique_ptr<Attribute> ParseAttr(const ResourceNameRef& name, const ConfigDescription& config,
                                        const android::ResTable_map_entry* map);
 
-  std::unique_ptr<Array> ParseArray(const ResourceNameRef& name,
-                                    const ConfigDescription& config,
+  std::unique_ptr<Array> ParseArray(const ResourceNameRef& name, const ConfigDescription& config,
                                     const android::ResTable_map_entry* map);
 
-  std::unique_ptr<Plural> ParsePlural(const ResourceNameRef& name,
-                                      const ConfigDescription& config,
+  std::unique_ptr<Plural> ParsePlural(const ResourceNameRef& name, const ConfigDescription& config,
                                       const android::ResTable_map_entry* map);
 
   /**
@@ -125,13 +111,10 @@
 
 namespace android {
 
-/**
- * Iterator functionality for ResTable_map_entry.
- */
+// Iterator functionality for ResTable_map_entry.
 
 inline const ResTable_map* begin(const ResTable_map_entry* map) {
-  return (const ResTable_map*)((const uint8_t*)map +
-                               aapt::util::DeviceToHost32(map->size));
+  return (const ResTable_map*)((const uint8_t*)map + ::aapt::util::DeviceToHost32(map->size));
 }
 
 inline const ResTable_map* end(const ResTable_map_entry* map) {
@@ -140,4 +123,4 @@
 
 }  // namespace android
 
-#endif  // AAPT_BINARY_RESOURCE_PARSER_H
+#endif  // AAPT_FORMAT_BINARY_RESOURCEPARSER_H
diff --git a/tools/aapt2/flatten/ChunkWriter.h b/tools/aapt2/format/binary/ChunkWriter.h
similarity index 85%
rename from tools/aapt2/flatten/ChunkWriter.h
rename to tools/aapt2/format/binary/ChunkWriter.h
index 968d3ee..1892a29 100644
--- a/tools/aapt2/flatten/ChunkWriter.h
+++ b/tools/aapt2/format/binary/ChunkWriter.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_FLATTEN_CHUNKWRITER_H
-#define AAPT_FLATTEN_CHUNKWRITER_H
+#ifndef AAPT_FORMAT_BINARY_CHUNKWRITER_H
+#define AAPT_FORMAT_BINARY_CHUNKWRITER_H
 
 #include "android-base/macros.h"
 #include "androidfw/ResourceTypes.h"
@@ -27,7 +27,8 @@
 
 class ChunkWriter {
  public:
-  explicit inline ChunkWriter(BigBuffer* buffer) : buffer_(buffer) {}
+  explicit inline ChunkWriter(BigBuffer* buffer) : buffer_(buffer) {
+  }
   ChunkWriter(ChunkWriter&&) = default;
   ChunkWriter& operator=(ChunkWriter&&) = default;
 
@@ -46,11 +47,17 @@
     return buffer_->NextBlock<T>(count);
   }
 
-  inline BigBuffer* buffer() { return buffer_; }
+  inline BigBuffer* buffer() {
+    return buffer_;
+  }
 
-  inline android::ResChunk_header* chunk_header() { return header_; }
+  inline android::ResChunk_header* chunk_header() {
+    return header_;
+  }
 
-  inline size_t size() { return buffer_->size() - start_size_; }
+  inline size_t size() {
+    return buffer_->size() - start_size_;
+  }
 
   inline android::ResChunk_header* Finish() {
     buffer_->Align4();
@@ -77,4 +84,4 @@
 
 }  // namespace aapt
 
-#endif /* AAPT_FLATTEN_CHUNKWRITER_H */
+#endif /* AAPT_FORMAT_BINARY_CHUNKWRITER_H */
diff --git a/tools/aapt2/unflatten/ResChunkPullParser.cpp b/tools/aapt2/format/binary/ResChunkPullParser.cpp
similarity index 92%
rename from tools/aapt2/unflatten/ResChunkPullParser.cpp
rename to tools/aapt2/format/binary/ResChunkPullParser.cpp
index 8d92bd9..fd6919d 100644
--- a/tools/aapt2/unflatten/ResChunkPullParser.cpp
+++ b/tools/aapt2/format/binary/ResChunkPullParser.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "unflatten/ResChunkPullParser.h"
+#include "format/binary/ResChunkPullParser.h"
 
 #include <inttypes.h>
 #include <cstddef>
@@ -44,9 +44,8 @@
   if (event_ == Event::kStartDocument) {
     current_chunk_ = data_;
   } else {
-    current_chunk_ =
-        (const ResChunk_header*)(((const char*)current_chunk_) +
-                                 util::DeviceToHost32(current_chunk_->size));
+    current_chunk_ = (const ResChunk_header*)(((const char*)current_chunk_) +
+                                              util::DeviceToHost32(current_chunk_->size));
   }
 
   const std::ptrdiff_t diff = (const char*)current_chunk_ - (const char*)data_;
diff --git a/tools/aapt2/unflatten/ResChunkPullParser.h b/tools/aapt2/format/binary/ResChunkPullParser.h
similarity index 64%
rename from tools/aapt2/unflatten/ResChunkPullParser.h
rename to tools/aapt2/format/binary/ResChunkPullParser.h
index 5827753..5ff1359 100644
--- a/tools/aapt2/unflatten/ResChunkPullParser.h
+++ b/tools/aapt2/format/binary/ResChunkPullParser.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_RES_CHUNK_PULL_PARSER_H
-#define AAPT_RES_CHUNK_PULL_PARSER_H
+#ifndef AAPT_FORMAT_BINARY_RESCHUNKPULLPARSER_H
+#define AAPT_FORMAT_BINARY_RESCHUNKPULLPARSER_H
 
 #include <string>
 
@@ -26,18 +26,13 @@
 
 namespace aapt {
 
-/**
- * A pull parser, modeled after XmlPullParser, that reads
- * android::ResChunk_header structs from a block of data.
- *
- * An android::ResChunk_header specifies a type, headerSize,
- * and size. The pull parser will verify that the chunk's size
- * doesn't extend beyond the available data, and will iterate
- * over each chunk in the given block of data.
- *
- * Processing nested chunks is done by creating a new ResChunkPullParser
- * pointing to the data portion of a chunk.
- */
+// A pull parser, modeled after XmlPullParser, that reads android::ResChunk_header structs from a
+// block of data.
+// An android::ResChunk_header specifies a type, headerSize, and size. The pull parser will verify
+// that the chunk's size doesn't extend beyond the available data, and will iterate over each chunk
+// in the given block of data.
+// Processing nested chunks is done by creating a new ResChunkPullParser pointing to the data
+// portion of a chunk.
 class ResChunkPullParser {
  public:
   enum class Event {
@@ -48,24 +43,18 @@
     kChunk,
   };
 
-  /**
-   * Returns false if the event is EndDocument or BadDocument.
-   */
+  // Returns false if the event is EndDocument or BadDocument.
   static bool IsGoodEvent(Event event);
 
-  /**
-   * Create a ResChunkPullParser to read android::ResChunk_headers
-   * from the memory pointed to by data, of len bytes.
-   */
+  // Create a ResChunkPullParser to read android::ResChunk_headers from the memory pointed to by
+  // data, of len bytes.
   ResChunkPullParser(const void* data, size_t len);
 
   Event event() const;
   const std::string& error() const;
   const android::ResChunk_header* chunk() const;
 
-  /**
-   * Move to the next android::ResChunk_header.
-   */
+  // Move to the next android::ResChunk_header.
   Event Next();
 
  private:
@@ -86,15 +75,12 @@
   return reinterpret_cast<const T*>(chunk);
 }
 
-inline static const uint8_t* GetChunkData(
-    const android::ResChunk_header* chunk) {
-  return reinterpret_cast<const uint8_t*>(chunk) +
-         util::DeviceToHost16(chunk->headerSize);
+inline static const uint8_t* GetChunkData(const android::ResChunk_header* chunk) {
+  return reinterpret_cast<const uint8_t*>(chunk) + util::DeviceToHost16(chunk->headerSize);
 }
 
 inline static uint32_t GetChunkDataLen(const android::ResChunk_header* chunk) {
-  return util::DeviceToHost32(chunk->size) -
-         util::DeviceToHost16(chunk->headerSize);
+  return util::DeviceToHost32(chunk->size) - util::DeviceToHost16(chunk->headerSize);
 }
 
 //
@@ -109,13 +95,16 @@
     : event_(Event::kStartDocument),
       data_(reinterpret_cast<const android::ResChunk_header*>(data)),
       len_(len),
-      current_chunk_(nullptr) {}
+      current_chunk_(nullptr) {
+}
 
 inline ResChunkPullParser::Event ResChunkPullParser::event() const {
   return event_;
 }
 
-inline const std::string& ResChunkPullParser::error() const { return error_; }
+inline const std::string& ResChunkPullParser::error() const {
+  return error_;
+}
 
 inline const android::ResChunk_header* ResChunkPullParser::chunk() const {
   return current_chunk_;
@@ -123,4 +112,4 @@
 
 }  // namespace aapt
 
-#endif  // AAPT_RES_CHUNK_PULL_PARSER_H
+#endif  // AAPT_FORMAT_BINARY_RESCHUNKPULLPARSER_H
diff --git a/tools/aapt2/flatten/ResourceTypeExtensions.h b/tools/aapt2/format/binary/ResourceTypeExtensions.h
similarity index 75%
rename from tools/aapt2/flatten/ResourceTypeExtensions.h
rename to tools/aapt2/format/binary/ResourceTypeExtensions.h
index 6359b41..7f58df80 100644
--- a/tools/aapt2/flatten/ResourceTypeExtensions.h
+++ b/tools/aapt2/format/binary/ResourceTypeExtensions.h
@@ -14,18 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_RESOURCE_TYPE_EXTENSIONS_H
-#define AAPT_RESOURCE_TYPE_EXTENSIONS_H
+#ifndef AAPT_FORMAT_BINARY_RESOURCETYPEEXTENSIONS_H
+#define AAPT_FORMAT_BINARY_RESOURCETYPEEXTENSIONS_H
 
 #include "androidfw/ResourceTypes.h"
 
 namespace aapt {
 
-/**
- * An alternative struct to use instead of ResTable_map_entry. This one is a
- * standard_layout
- * struct.
- */
+// An alternative struct to use instead of ResTable_map_entry. This one is a standard_layout struct.
 struct ResTable_entry_ext {
   android::ResTable_entry entry;
   android::ResTable_ref parent;
@@ -34,4 +30,4 @@
 
 }  // namespace aapt
 
-#endif  // AAPT_RESOURCE_TYPE_EXTENSIONS_H
+#endif  // AAPT_FORMAT_BINARY_RESOURCETYPEEXTENSIONS_H
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
similarity index 93%
rename from tools/aapt2/flatten/TableFlattener.cpp
rename to tools/aapt2/format/binary/TableFlattener.cpp
index 097727c..57565a5 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "flatten/TableFlattener.h"
+#include "format/binary/TableFlattener.h"
 
 #include <algorithm>
 #include <numeric>
@@ -28,8 +28,8 @@
 #include "ResourceValues.h"
 #include "SdkConstants.h"
 #include "ValueVisitor.h"
-#include "flatten/ChunkWriter.h"
-#include "flatten/ResourceTypeExtensions.h"
+#include "format/binary/ChunkWriter.h"
+#include "format/binary/ResourceTypeExtensions.h"
 #include "util/BigBuffer.h"
 
 using namespace android;
@@ -81,7 +81,8 @@
   using ValueVisitor::Visit;
 
   MapFlattenVisitor(ResTable_entry_ext* out_entry, BigBuffer* buffer)
-      : out_entry_(out_entry), buffer_(buffer) {}
+      : out_entry_(out_entry), buffer_(buffer) {
+  }
 
   void Visit(Attribute* attr) override {
     {
@@ -92,15 +93,13 @@
 
     if (attr->min_int != std::numeric_limits<int32_t>::min()) {
       Reference key = Reference(ResourceId(ResTable_map::ATTR_MIN));
-      BinaryPrimitive val(Res_value::TYPE_INT_DEC,
-                          static_cast<uint32_t>(attr->min_int));
+      BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->min_int));
       FlattenEntry(&key, &val);
     }
 
     if (attr->max_int != std::numeric_limits<int32_t>::max()) {
       Reference key = Reference(ResourceId(ResTable_map::ATTR_MAX));
-      BinaryPrimitive val(Res_value::TYPE_INT_DEC,
-                          static_cast<uint32_t>(attr->max_int));
+      BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->max_int));
       FlattenEntry(&key, &val);
     }
 
@@ -188,7 +187,9 @@
    * Call this after visiting a Value. This will finish any work that
    * needs to be done to prepare the entry.
    */
-  void Finish() { out_entry_->count = util::HostToDevice32(entry_count_); }
+  void Finish() {
+    out_entry_->count = util::HostToDevice32(entry_count_);
+  }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MapFlattenVisitor);
@@ -223,7 +224,8 @@
         diag_(context->GetDiagnostics()),
         package_(package),
         shared_libs_(shared_libs),
-        use_sparse_entries_(use_sparse_entries) {}
+        use_sparse_entries_(use_sparse_entries) {
+  }
 
   bool FlattenPackage(BigBuffer* buffer) {
     ChunkWriter pkg_writer(buffer);
@@ -271,9 +273,9 @@
 
   template <typename T, bool IsItem>
   T* WriteEntry(FlatEntry* entry, BigBuffer* buffer) {
-    static_assert(std::is_same<ResTable_entry, T>::value ||
-                      std::is_same<ResTable_entry_ext, T>::value,
-                  "T must be ResTable_entry or ResTable_entry_ext");
+    static_assert(
+        std::is_same<ResTable_entry, T>::value || std::is_same<ResTable_entry_ext, T>::value,
+        "T must be ResTable_entry or ResTable_entry_ext");
 
     T* result = buffer->NextBlock<T>();
     ResTable_entry* out_entry = (ResTable_entry*)result;
@@ -302,8 +304,7 @@
       CHECK(item->Flatten(outValue)) << "flatten failed";
       outValue->size = util::HostToDevice16(sizeof(*outValue));
     } else {
-      ResTable_entry_ext* out_entry =
-          WriteEntry<ResTable_entry_ext, false>(entry, buffer);
+      ResTable_entry_ext* out_entry = WriteEntry<ResTable_entry_ext, false>(entry, buffer);
       MapFlattenVisitor visitor(out_entry, buffer);
       entry->value->Accept(&visitor);
       visitor.Finish();
@@ -318,8 +319,7 @@
     CHECK(num_total_entries <= std::numeric_limits<uint16_t>::max());
 
     ChunkWriter type_writer(buffer);
-    ResTable_type* type_header =
-        type_writer.StartChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
+    ResTable_type* type_header = type_writer.StartChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
     type_header->id = type->id.value();
     type_header->config = config;
     type_header->config.swapHtoD();
@@ -395,8 +395,7 @@
 
       sorted_types.push_back(type.get());
     }
-    std::sort(sorted_types.begin(), sorted_types.end(),
-              cmp_ids<ResourceTableType>);
+    std::sort(sorted_types.begin(), sorted_types.end(), cmp_ids<ResourceTableType>);
     return sorted_types;
   }
 
@@ -411,13 +410,11 @@
     return sorted_entries;
   }
 
-  bool FlattenTypeSpec(ResourceTableType* type,
-                       std::vector<ResourceEntry*>* sorted_entries,
+  bool FlattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sorted_entries,
                        BigBuffer* buffer) {
     ChunkWriter type_spec_writer(buffer);
     ResTable_typeSpec* spec_header =
-        type_spec_writer.StartChunk<ResTable_typeSpec>(
-            RES_TABLE_TYPE_SPEC_TYPE);
+        type_spec_writer.StartChunk<ResTable_typeSpec>(RES_TABLE_TYPE_SPEC_TYPE);
     spec_header->id = type->id.value();
 
     if (sorted_entries->empty()) {
@@ -443,8 +440,7 @@
       // Populate the config masks for this entry.
 
       if (entry->symbol_status.state == SymbolState::kPublic) {
-        config_masks[entry->id.value()] |=
-            util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+        config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
       }
 
       const size_t config_count = entry->values.size();
diff --git a/tools/aapt2/flatten/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
similarity index 89%
rename from tools/aapt2/flatten/TableFlattener.h
rename to tools/aapt2/format/binary/TableFlattener.h
index 223aef8..88cbddf 100644
--- a/tools/aapt2/flatten/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_FLATTEN_TABLEFLATTENER_H
-#define AAPT_FLATTEN_TABLEFLATTENER_H
+#ifndef AAPT_FORMAT_BINARY_TABLEFLATTENER_H
+#define AAPT_FORMAT_BINARY_TABLEFLATTENER_H
 
 #include "android-base/macros.h"
 
@@ -40,7 +40,8 @@
 class TableFlattener : public IResourceTableConsumer {
  public:
   explicit TableFlattener(const TableFlattenerOptions& options, BigBuffer* buffer)
-      : options_(options), buffer_(buffer) {}
+      : options_(options), buffer_(buffer) {
+  }
 
   bool Consume(IAaptContext* context, ResourceTable* table) override;
 
@@ -53,4 +54,4 @@
 
 }  // namespace aapt
 
-#endif /* AAPT_FLATTEN_TABLEFLATTENER_H */
+#endif /* AAPT_FORMAT_BINARY_TABLEFLATTENER_H */
diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
similarity index 83%
rename from tools/aapt2/flatten/TableFlattener_test.cpp
rename to tools/aapt2/format/binary/TableFlattener_test.cpp
index 4fdb2ec..6d75973 100644
--- a/tools/aapt2/flatten/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#include "flatten/TableFlattener.h"
+#include "format/binary/TableFlattener.h"
 
 #include "android-base/stringprintf.h"
 
 #include "ResourceUtils.h"
 #include "SdkConstants.h"
+#include "format/binary/BinaryResourceParser.h"
 #include "test/Test.h"
-#include "unflatten/BinaryResourceParser.h"
 #include "util/Util.h"
 
 using namespace android;
@@ -34,10 +34,8 @@
 class TableFlattenerTest : public ::testing::Test {
  public:
   void SetUp() override {
-    context_ = test::ContextBuilder()
-                   .SetCompilationPackage("com.app.test")
-                   .SetPackageId(0x7f)
-                   .Build();
+    context_ =
+        test::ContextBuilder().SetCompilationPackage("com.app.test").SetPackageId(0x7f).Build();
   }
 
   ::testing::AssertionResult Flatten(IAaptContext* context, const TableFlattenerOptions& options,
@@ -80,12 +78,10 @@
     return ::testing::AssertionSuccess();
   }
 
-  ::testing::AssertionResult Exists(ResTable* table,
-                                    const StringPiece& expected_name,
+  ::testing::AssertionResult Exists(ResTable* table, const StringPiece& expected_name,
                                     const ResourceId& expected_id,
                                     const ConfigDescription& expected_config,
-                                    const uint8_t expected_data_type,
-                                    const uint32_t expected_data,
+                                    const uint8_t expected_data_type, const uint32_t expected_data,
                                     const uint32_t expected_spec_flags) {
     const ResourceName expected_res_name = test::ParseNameOrDie(expected_name);
 
@@ -94,28 +90,26 @@
     ResTable_config config;
     Res_value val;
     uint32_t spec_flags;
-    if (table->getResource(expected_id.id, &val, false, 0, &spec_flags,
-                           &config) < 0) {
+    if (table->getResource(expected_id.id, &val, false, 0, &spec_flags, &config) < 0) {
       return ::testing::AssertionFailure() << "could not find resource with";
     }
 
     if (expected_data_type != val.dataType) {
       return ::testing::AssertionFailure()
              << "expected data type " << std::hex << (int)expected_data_type
-             << " but got data type " << (int)val.dataType << std::dec
-             << " instead";
+             << " but got data type " << (int)val.dataType << std::dec << " instead";
     }
 
     if (expected_data != val.data) {
       return ::testing::AssertionFailure()
-             << "expected data " << std::hex << expected_data
-             << " but got data " << val.data << std::dec << " instead";
+             << "expected data " << std::hex << expected_data << " but got data " << val.data
+             << std::dec << " instead";
     }
 
     if (expected_spec_flags != spec_flags) {
       return ::testing::AssertionFailure()
-             << "expected specFlags " << std::hex << expected_spec_flags
-             << " but got specFlags " << spec_flags << std::dec << " instead";
+             << "expected specFlags " << std::hex << expected_spec_flags << " but got specFlags "
+             << spec_flags << std::dec << " instead";
     }
 
     ResTable::resource_name actual_name;
@@ -127,16 +121,14 @@
     if (!resName) {
       return ::testing::AssertionFailure()
              << "expected name '" << expected_res_name << "' but got '"
-             << StringPiece16(actual_name.package, actual_name.packageLen)
-             << ":" << StringPiece16(actual_name.type, actual_name.typeLen)
-             << "/" << StringPiece16(actual_name.name, actual_name.nameLen)
-             << "'";
+             << StringPiece16(actual_name.package, actual_name.packageLen) << ":"
+             << StringPiece16(actual_name.type, actual_name.typeLen) << "/"
+             << StringPiece16(actual_name.name, actual_name.nameLen) << "'";
     }
 
     if (expected_config != config) {
-      return ::testing::AssertionFailure() << "expected config '"
-                                           << expected_config << "' but got '"
-                                           << ConfigDescription(config) << "'";
+      return ::testing::AssertionFailure() << "expected config '" << expected_config
+                                           << "' but got '" << ConfigDescription(config) << "'";
     }
     return ::testing::AssertionSuccess();
   }
@@ -152,57 +144,46 @@
           .AddSimple("com.app.test:id/one", ResourceId(0x7f020000))
           .AddSimple("com.app.test:id/two", ResourceId(0x7f020001))
           .AddValue("com.app.test:id/three", ResourceId(0x7f020002),
-                    test::BuildReference("com.app.test:id/one",
-                                         ResourceId(0x7f020000)))
+                    test::BuildReference("com.app.test:id/one", ResourceId(0x7f020000)))
           .AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
-                    util::make_unique<BinaryPrimitive>(
-                        uint8_t(Res_value::TYPE_INT_DEC), 1u))
+                    util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
           .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
                     ResourceId(0x7f030000),
-                    util::make_unique<BinaryPrimitive>(
-                        uint8_t(Res_value::TYPE_INT_DEC), 2u))
+                    util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
           .AddString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
-          .AddString("com.app.test:layout/bar", ResourceId(0x7f050000),
-                     "res/layout/bar.xml")
+          .AddString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
           .Build();
 
   ResTable res_table;
   ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
 
-  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020000),
-                     {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020000), {},
+                     Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
 
-  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/two", ResourceId(0x7f020001),
-                     {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/two", ResourceId(0x7f020001), {},
+                     Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
 
-  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three",
-                     ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE,
-                     0x7f020000u, 0u));
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020002), {},
+                     Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
 
-  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one",
-                     ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
-                     ResTable_config::CONFIG_VERSION));
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000), {},
+                     Res_value::TYPE_INT_DEC, 1u, ResTable_config::CONFIG_VERSION));
 
-  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one",
-                     ResourceId(0x7f030000), test::ParseConfigOrDie("v1"),
-                     Res_value::TYPE_INT_DEC, 2u,
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000),
+                     test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 2u,
                      ResTable_config::CONFIG_VERSION));
 
   std::u16string foo_str = u"foo";
-  ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(),
-                                                                foo_str.size());
+  ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size());
   ASSERT_GE(idx, 0);
-  EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test",
-                     ResourceId(0x7f040000), {}, Res_value::TYPE_STRING,
-                     (uint32_t)idx, 0u));
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {},
+                     Res_value::TYPE_STRING, (uint32_t)idx, 0u));
 
   std::u16string bar_path = u"res/layout/bar.xml";
-  idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(),
-                                                        bar_path.size());
+  idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
   ASSERT_GE(idx, 0);
-  EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar",
-                     ResourceId(0x7f050000), {}, Res_value::TYPE_STRING,
-                     (uint32_t)idx, 0u));
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar", ResourceId(0x7f050000), {},
+                     Res_value::TYPE_STRING, (uint32_t)idx, 0u));
 }
 
 TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) {
@@ -216,11 +197,10 @@
   ResTable res_table;
   ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
 
-  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020001),
-                     {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
-  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three",
-                     ResourceId(0x7f020003), {}, Res_value::TYPE_INT_BOOLEAN,
-                     0u, 0u));
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f020001), {},
+                     Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+  EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020003), {},
+                     Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
 }
 
 TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) {
@@ -231,8 +211,7 @@
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("android", 0x01)
-          .AddValue("android:attr/foo", ResourceId(0x01010000),
-                    util::make_unique<Attribute>(attr))
+          .AddValue("android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>(attr))
           .Build();
 
   ResourceTable result;
@@ -308,9 +287,12 @@
   ASSERT_THAT(value, NotNull());
   EXPECT_EQ(0u, value->value.data);
 
-  ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1", sparse_config), IsNull());
+  ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1",
+                                                       sparse_config),
+              IsNull());
 
-  value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4", sparse_config);
+  value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4",
+                                                   sparse_config);
   ASSERT_THAT(value, NotNull());
   EXPECT_EQ(4u, value->value.data);
 }
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/format/binary/XmlFlattener.cpp
similarity index 93%
rename from tools/aapt2/flatten/XmlFlattener.cpp
rename to tools/aapt2/format/binary/XmlFlattener.cpp
index b3b308a..f8f09ab 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "flatten/XmlFlattener.h"
+#include "format/binary/XmlFlattener.h"
 
 #include <algorithm>
 #include <map>
@@ -26,8 +26,8 @@
 #include "utils/misc.h"
 
 #include "SdkConstants.h"
-#include "flatten/ChunkWriter.h"
-#include "flatten/ResourceTypeExtensions.h"
+#include "format/binary/ChunkWriter.h"
+#include "format/binary/ResourceTypeExtensions.h"
 #include "xml/XmlDom.h"
 
 using namespace android;
@@ -71,7 +71,8 @@
   std::vector<StringFlattenDest> string_refs;
 
   XmlFlattenerVisitor(BigBuffer* buffer, XmlFlattenerOptions options)
-      : buffer_(buffer), options_(options) {}
+      : buffer_(buffer), options_(options) {
+  }
 
   void Visit(xml::Text* node) override {
     if (util::TrimWhitespace(node->text).empty()) {
@@ -152,15 +153,14 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(XmlFlattenerVisitor);
 
-  void AddString(const StringPiece& str, uint32_t priority,
-                 android::ResStringPool_ref* dest,
+  void AddString(const StringPiece& str, uint32_t priority, android::ResStringPool_ref* dest,
                  bool treat_empty_string_as_null = false) {
     if (str.empty() && treat_empty_string_as_null) {
       // Some parts of the runtime treat null differently than empty string.
       dest->index = util::DeviceToHost32(-1);
     } else {
-      string_refs.push_back(StringFlattenDest{
-          pool.MakeRef(str, StringPool::Context(priority)), dest});
+      string_refs.push_back(
+          StringFlattenDest{pool.MakeRef(str, StringPool::Context(priority)), dest});
     }
   }
 
@@ -208,8 +208,7 @@
     uint16_t attribute_index = 1;
     for (const xml::Attribute* xml_attr : filtered_attrs_) {
       // Assign the indices for specific attributes.
-      if (xml_attr->compiled_attribute &&
-          xml_attr->compiled_attribute.value().id &&
+      if (xml_attr->compiled_attribute && xml_attr->compiled_attribute.value().id &&
           xml_attr->compiled_attribute.value().id.value() == kIdAttr) {
         flat_elem->idIndex = util::HostToDevice16(attribute_index);
       } else if (xml_attr->namespace_uri.empty()) {
@@ -241,9 +240,8 @@
         // Lookup the StringPool for this package and make the reference there.
         const xml::AaptAttribute& aapt_attr = xml_attr->compiled_attribute.value();
 
-        StringPool::Ref name_ref =
-            package_pools[aapt_attr.id.value().package_id()].MakeRef(
-                xml_attr->name, StringPool::Context(aapt_attr.id.value().id));
+        StringPool::Ref name_ref = package_pools[aapt_attr.id.value().package_id()].MakeRef(
+            xml_attr->name, StringPool::Context(aapt_attr.id.value().id));
 
         // Add it to the list of strings to flatten.
         AddString(name_ref, &flat_attr->name);
@@ -272,7 +270,7 @@
         flat_attr->typedValue.dataType = android::Res_value::TYPE_STRING;
 
         AddString(str_builder.ToString(), kLowPriority,
-                  (ResStringPool_ref*) &flat_attr->typedValue.data);
+                  (ResStringPool_ref*)&flat_attr->typedValue.data);
       }
 
       flat_attr->typedValue.size = util::HostToDevice16(sizeof(flat_attr->typedValue));
diff --git a/tools/aapt2/flatten/XmlFlattener.h b/tools/aapt2/format/binary/XmlFlattener.h
similarity index 83%
rename from tools/aapt2/flatten/XmlFlattener.h
rename to tools/aapt2/format/binary/XmlFlattener.h
index 87557f2..6a48835 100644
--- a/tools/aapt2/flatten/XmlFlattener.h
+++ b/tools/aapt2/format/binary/XmlFlattener.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_FLATTEN_XMLFLATTENER_H
-#define AAPT_FLATTEN_XMLFLATTENER_H
+#ifndef AAPT_FORMAT_BINARY_XMLFLATTENER_H
+#define AAPT_FORMAT_BINARY_XMLFLATTENER_H
 
 #include "android-base/macros.h"
 
@@ -26,16 +26,15 @@
 namespace aapt {
 
 struct XmlFlattenerOptions {
-  /**
-   * Keep attribute raw string values along with typed values.
-   */
+  // Keep attribute raw string values along with typed values.
   bool keep_raw_values = false;
 };
 
 class XmlFlattener : public IXmlResourceConsumer {
  public:
   XmlFlattener(BigBuffer* buffer, XmlFlattenerOptions options)
-      : buffer_(buffer), options_(options) {}
+      : buffer_(buffer), options_(options) {
+  }
 
   bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
 
@@ -50,4 +49,4 @@
 
 }  // namespace aapt
 
-#endif /* AAPT_FLATTEN_XMLFLATTENER_H */
+#endif /* AAPT_FORMAT_BINARY_XMLFLATTENER_H */
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp
similarity index 98%
rename from tools/aapt2/flatten/XmlFlattener_test.cpp
rename to tools/aapt2/format/binary/XmlFlattener_test.cpp
index a57e317..0450f6c 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "flatten/XmlFlattener.h"
+#include "format/binary/XmlFlattener.h"
 
 #include "androidfw/ResourceTypes.h"
 
@@ -55,8 +55,7 @@
                    .Build();
   }
 
-  ::testing::AssertionResult Flatten(xml::XmlResource* doc,
-                                     android::ResXMLTree* out_tree,
+  ::testing::AssertionResult Flatten(xml::XmlResource* doc, android::ResXMLTree* out_tree,
                                      const XmlFlattenerOptions& options = {}) {
     using namespace android;  // For NO_ERROR on windows because it is a macro.
 
diff --git a/tools/aapt2/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
similarity index 98%
rename from tools/aapt2/proto/ProtoDeserialize.cpp
rename to tools/aapt2/format/proto/ProtoDeserialize.cpp
index dc13881..c14f09a 100644
--- a/tools/aapt2/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "proto/ProtoDeserialize.h"
+#include "format/proto/ProtoDeserialize.h"
 
 #include "android-base/logging.h"
 #include "android-base/macros.h"
@@ -699,11 +699,11 @@
       } break;
 
       default:
-        LOG(FATAL) << "unknown compound value: " << (int) pb_compound_value.value_case();
+        LOG(FATAL) << "unknown compound value: " << (int)pb_compound_value.value_case();
         break;
     }
   } else {
-    LOG(FATAL) << "unknown value: " << (int) pb_value.value_case();
+    LOG(FATAL) << "unknown value: " << (int)pb_value.value_case();
     return {};
   }
 
@@ -764,7 +764,7 @@
     } break;
 
     default:
-      LOG(FATAL) << "unknown item: " << (int) pb_item.value_case();
+      LOG(FATAL) << "unknown item: " << (int)pb_item.value_case();
       break;
   }
   return {};
@@ -840,7 +840,7 @@
       } break;
 
       default:
-        LOG(FATAL) << "unknown XmlNode " << (int) pb_child.node_case();
+        LOG(FATAL) << "unknown XmlNode " << (int)pb_child.node_case();
         break;
     }
   }
diff --git a/tools/aapt2/proto/ProtoDeserialize.h b/tools/aapt2/format/proto/ProtoDeserialize.h
similarity index 95%
rename from tools/aapt2/proto/ProtoDeserialize.h
rename to tools/aapt2/format/proto/ProtoDeserialize.h
index 3d76ea4..c8a7199 100644
--- a/tools/aapt2/proto/ProtoDeserialize.h
+++ b/tools/aapt2/format/proto/ProtoDeserialize.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_PROTO_PROTODESERIALIZE_H
-#define AAPT_PROTO_PROTODESERIALIZE_H
+#ifndef AAPT_FORMAT_PROTO_PROTODESERIALIZE_H
+#define AAPT_FORMAT_PROTO_PROTODESERIALIZE_H
 
 #include "android-base/macros.h"
 #include "androidfw/ResourceTypes.h"
@@ -75,4 +75,4 @@
 
 }  // namespace aapt
 
-#endif /* AAPT_PROTO_PROTODESERIALIZE_H */
+#endif /* AAPT_FORMAT_PROTO_PROTODESERIALIZE_H */
diff --git a/tools/aapt2/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
similarity index 99%
rename from tools/aapt2/proto/ProtoSerialize.cpp
rename to tools/aapt2/format/proto/ProtoSerialize.cpp
index d36d668..c0d3614 100644
--- a/tools/aapt2/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "proto/ProtoSerialize.h"
+#include "format/proto/ProtoSerialize.h"
 
 #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
 
diff --git a/tools/aapt2/proto/ProtoSerialize.h b/tools/aapt2/format/proto/ProtoSerialize.h
similarity index 95%
rename from tools/aapt2/proto/ProtoSerialize.h
rename to tools/aapt2/format/proto/ProtoSerialize.h
index 4bf7e1c..1694b16 100644
--- a/tools/aapt2/proto/ProtoSerialize.h
+++ b/tools/aapt2/format/proto/ProtoSerialize.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_FLATTEN_TABLEPROTOSERIALIZER_H
-#define AAPT_FLATTEN_TABLEPROTOSERIALIZER_H
+#ifndef AAPT_FORMAT_PROTO_PROTOSERIALIZE_H
+#define AAPT_FORMAT_PROTO_PROTOSERIALIZE_H
 
 #include "android-base/macros.h"
 #include "google/protobuf/io/coded_stream.h"
@@ -86,4 +86,4 @@
 
 }  // namespace aapt
 
-#endif /* AAPT_FLATTEN_TABLEPROTOSERIALIZER_H */
+#endif /* AAPT_FORMAT_PROTO_PROTOSERIALIZE_H */
diff --git a/tools/aapt2/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
similarity index 99%
rename from tools/aapt2/proto/ProtoSerialize_test.cpp
rename to tools/aapt2/format/proto/ProtoSerialize_test.cpp
index b263aff..2154d5a 100644
--- a/tools/aapt2/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#include "proto/ProtoSerialize.h"
+#include "format/proto/ProtoSerialize.h"
 
 #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
 
 #include "ResourceUtils.h"
-#include "proto/ProtoDeserialize.h"
+#include "format/proto/ProtoDeserialize.h"
 #include "test/Test.h"
 
 using ::android::StringPiece;
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index ec1ddb8..02ee876 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -21,7 +21,7 @@
 
 #include "google/protobuf/message_lite.h"
 
-#include "flatten/Archive.h"
+#include "format/Archive.h"
 #include "io/File.h"
 #include "io/Io.h"
 #include "process/IResourceTableConsumer.h"
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index 8e4b82c..6803088 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -27,8 +27,8 @@
 #include "configuration/ConfigurationParser.h"
 #include "filter/AbiFilter.h"
 #include "filter/Filter.h"
-#include "flatten/Archive.h"
-#include "flatten/XmlFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/XmlFlattener.h"
 #include "optimize/VersionCollapser.h"
 #include "process/IResourceTableConsumer.h"
 #include "split/TableSplitter.h"
diff --git a/tools/aapt2/optimize/MultiApkGenerator_test.cpp b/tools/aapt2/optimize/MultiApkGenerator_test.cpp
index e8e6adc..c8f3524 100644
--- a/tools/aapt2/optimize/MultiApkGenerator_test.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator_test.cpp
@@ -25,8 +25,8 @@
 #include "ResourceTable.h"
 #include "configuration/ConfigurationParser.h"
 #include "filter/Filter.h"
-#include "flatten/Archive.h"
-#include "flatten/TableFlattener.h"
+#include "format/Archive.h"
+#include "format/binary/TableFlattener.h"
 #include "process/IResourceTableConsumer.h"
 #include "test/Context.h"
 #include "test/Test.h"
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index 6b97ae6..4ba0443 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -18,7 +18,7 @@
 
 #include <string>
 
-#include "flatten/XmlFlattener.h"
+#include "format/binary/XmlFlattener.h"
 #include "io/StringInputStream.h"
 #include "test/Test.h"
 
diff --git a/tools/incident_report/generic_message.h b/tools/incident_report/generic_message.h
index df3f7b2..7c4ad34 100644
--- a/tools/incident_report/generic_message.h
+++ b/tools/incident_report/generic_message.h
@@ -25,7 +25,7 @@
 /**
  * Class to represent a protobuf Message, where we don't actually
  * know what any of the fields are, just their type codes. In other
- * words, this loslessly stores a parsed protobuf object without
+ * words, this losslessly stores a parsed protobuf object without
  * having the .proto file that generated it.
  */
 class GenericMessage
diff --git a/tools/incident_report/main.cpp b/tools/incident_report/main.cpp
index 250d118..d4ad340 100644
--- a/tools/incident_report/main.cpp
+++ b/tools/incident_report/main.cpp
@@ -138,7 +138,6 @@
 static void
 print_value(Out* out, FieldDescriptor const* field, GenericMessage::Node const& node)
 {
-    uint32_t val32;
     FieldDescriptor::Type type = field->type();
 
     switch (node.type) {
@@ -160,23 +159,25 @@
             break;
         case GenericMessage::TYPE_VALUE64:
             switch (type) {
-                case FieldDescriptor::TYPE_FIXED64:
-                case FieldDescriptor::TYPE_SFIXED64:
                 case FieldDescriptor::TYPE_DOUBLE:
                     out->printf("%f", *(double*)&node.value64);
                     break;
+                // Int32s here were added with addInt64 from a WIRETYPE_VARINT,
+                // even if the definition is for a 32 bit int.
                 case FieldDescriptor::TYPE_SINT32:
                 case FieldDescriptor::TYPE_INT32:
-                    val32 = (uint32_t)node.value32;
-                    out->printf("%d", val32);
+                    out->printf("%d", node.value64);
                     break;
                 case FieldDescriptor::TYPE_INT64:
-                case FieldDescriptor::TYPE_UINT32:
-                    val32 = (uint32_t)node.value32;
-                    out->printf("%u", val32);
-                    break;
-                case FieldDescriptor::TYPE_UINT64:
                 case FieldDescriptor::TYPE_SINT64:
+                case FieldDescriptor::TYPE_SFIXED64:
+                    out->printf("%lld", node.value64);
+                    break;
+                case FieldDescriptor::TYPE_UINT32:
+                case FieldDescriptor::TYPE_UINT64:
+                case FieldDescriptor::TYPE_FIXED64:
+                    out->printf("%u", node.value64);
+                    break;
                 case FieldDescriptor::TYPE_BOOL:
                     if (node.value64) {
                         out->printf("true");
@@ -185,11 +186,15 @@
                     }
                     break;
                 case FieldDescriptor::TYPE_ENUM:
-                    out->printf("%s", field->enum_type()->FindValueByNumber((int)node.value64)
+                    if (field->enum_type()->FindValueByNumber((int)node.value64) == NULL) {
+                        out->printf("%lld", (int) node.value64);
+                    } else {
+                        out->printf("%s", field->enum_type()->FindValueByNumber((int)node.value64)
                             ->name().c_str());
+                    }
                     break;
                 default:
-                    out->printf("(unexpected value64 %ld (0x%x))", node.value64, node.value64);
+                    out->printf("(unexpected value64 %lld (0x%x))", node.value64, node.value64);
                     break;
             }
             break;
@@ -224,22 +229,13 @@
         out->printf("%s=", field->name().c_str());
         if (repeated) {
             if (it.first != it.second) {
-                out->printf("[");
-                if (type == FieldDescriptor::TYPE_MESSAGE
-                        || type == FieldDescriptor::TYPE_STRING
-                        || type == FieldDescriptor::TYPE_BYTES) {
-                    out->printf("\n");
-                }
+                out->printf("[\n");
                 out->indent();
 
                 for (GenericMessage::const_iterator_pair it = message->find(fieldId);
                         it.first != it.second; it.first++) {
                     print_value(out, field, it.first->second);
-                    if (type == FieldDescriptor::TYPE_MESSAGE
-                            || type == FieldDescriptor::TYPE_STRING
-                            || type == FieldDescriptor::TYPE_BYTES) {
-                        out->printf("\n");
-                    }
+                    out->printf("\n");
                 }
 
                 out->dedent();
@@ -335,7 +331,7 @@
             }
             id = field->number();
         }
-        
+
         int pfd[2];
         if (pipe(pfd) != 0) {
             fprintf(stderr, "pipe failed: %s\n", strerror(errno));
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index a145327..6438631 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -790,6 +790,28 @@
 
     /**
      * @hide
+     * Returns true if this WiFi config is for an open network.
+     */
+    public boolean isOpenNetwork() {
+        final int cardinality = allowedKeyManagement.cardinality();
+        final boolean hasNoKeyMgmt = cardinality == 0
+                || (cardinality == 1 && allowedKeyManagement.get(KeyMgmt.NONE));
+
+        boolean hasNoWepKeys = true;
+        if (wepKeys != null) {
+            for (int i = 0; i < wepKeys.length; i++) {
+                if (wepKeys[i] != null) {
+                    hasNoWepKeys = false;
+                    break;
+                }
+            }
+        }
+
+        return hasNoKeyMgmt && hasNoWepKeys;
+    }
+
+    /**
+     * @hide
      * Setting this value will force scan results associated with this configuration to
      * be included in the bucket of networks that are externally scored.
      * If not set, associated scan results will be treated as legacy saved networks and
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java
index 357f76e..9f73622 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java
@@ -20,7 +20,6 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.net.NetworkSpecifier;
-import android.net.wifi.RttManager;
 import android.util.Log;
 
 import dalvik.system.CloseGuard;
@@ -224,37 +223,6 @@
     }
 
     /**
-     * Start a ranging operation with the specified peers. The peer IDs are obtained from an
-     * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
-     * byte[], java.util.List)} or
-     * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
-     * byte[])} operation - can
-     * only range devices which are part of an ongoing discovery session.
-     *
-     * @param params   RTT parameters - each corresponding to a specific peer ID (the array sizes
-     *                 must be identical). The
-     *                 {@link android.net.wifi.RttManager.RttParams#bssid} member must be set to
-     *                 a peer ID - not to a MAC address.
-     * @param listener The listener to receive the results of the ranging session.
-     * @hide
-     * [TODO: b/28847998 - track RTT API & visilibity]
-     */
-    public void startRanging(RttManager.RttParams[] params, RttManager.RttListener listener) {
-        if (mTerminated) {
-            Log.w(TAG, "startRanging: called on terminated session");
-            return;
-        }
-
-        WifiAwareManager mgr = mMgr.get();
-        if (mgr == null) {
-            Log.w(TAG, "startRanging: called post GC on WifiAwareManager");
-            return;
-        }
-
-        mgr.startRanging(mClientId, mSessionId, params, listener);
-    }
-
-    /**
      * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
      * an unencrypted WiFi Aware connection (link) to the specified peer. The
      * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl
index 30dd64d..b646567 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl
@@ -17,7 +17,6 @@
 package android.net.wifi.aware;
 
 import android.net.wifi.aware.ConfigRequest;
-import android.net.wifi.RttManager;
 
 /**
  * Callback interface that WifiAwareManager implements
@@ -29,8 +28,4 @@
     void onConnectSuccess(int clientId);
     void onConnectFail(int reason);
     void onIdentityChanged(in byte[] mac);
-
-    void onRangingSuccess(int rangingId, in RttManager.ParcelableRttResults results);
-    void onRangingFailure(int rangingId, int reason, in String description);
-    void onRangingAborted(int rangingId);
 }
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
index 0f4910f..f33424b 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
@@ -24,7 +24,6 @@
 import android.net.wifi.aware.PublishConfig;
 import android.net.wifi.aware.SubscribeConfig;
 import android.net.wifi.aware.Characteristics;
-import android.net.wifi.RttManager;
 
 /**
  * Interface that WifiAwareService implements
@@ -53,5 +52,4 @@
     void sendMessage(int clientId, int discoverySessionId, int peerId, in byte[] message,
         int messageId, int retryCount);
     void terminateSession(int clientId, int discoverySessionId);
-    int startRanging(int clientId, int discoverySessionId, in RttManager.ParcelableRttParams parms);
 }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index df0d9d2..ed6804d 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -26,7 +26,6 @@
 import android.net.ConnectivityManager;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
-import android.net.wifi.RttManager;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -35,9 +34,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
 
 import libcore.util.HexEncoding;
 
@@ -45,7 +41,6 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.nio.BufferOverflowException;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -172,9 +167,6 @@
 
     private final Object mLock = new Object(); // lock access to the following vars
 
-    @GuardedBy("mLock")
-    private SparseArray<RttManager.RttListener> mRangingListeners = new SparseArray<>();
-
     /** @hide */
     public WifiAwareManager(Context context, IWifiAwareManager service) {
         mContext = context;
@@ -401,27 +393,6 @@
     }
 
     /** @hide */
-    public void startRanging(int clientId, int sessionId, RttManager.RttParams[] params,
-                             RttManager.RttListener listener) {
-        if (VDBG) {
-            Log.v(TAG, "startRanging: clientId=" + clientId + ", sessionId=" + sessionId + ", "
-                    + "params=" + Arrays.toString(params) + ", listener=" + listener);
-        }
-
-        int rangingKey = 0;
-        try {
-            rangingKey = mService.startRanging(clientId, sessionId,
-                    new RttManager.ParcelableRttParams(params));
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
-        synchronized (mLock) {
-            mRangingListeners.put(rangingKey, listener);
-        }
-    }
-
-    /** @hide */
     public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId,
             PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) {
         if (VDBG) {
@@ -500,29 +471,12 @@
         private static final int CALLBACK_CONNECT_SUCCESS = 0;
         private static final int CALLBACK_CONNECT_FAIL = 1;
         private static final int CALLBACK_IDENTITY_CHANGED = 2;
-        private static final int CALLBACK_RANGING_SUCCESS = 3;
-        private static final int CALLBACK_RANGING_FAILURE = 4;
-        private static final int CALLBACK_RANGING_ABORTED = 5;
 
         private final Handler mHandler;
         private final WeakReference<WifiAwareManager> mAwareManager;
         private final Binder mBinder;
         private final Looper mLooper;
 
-        RttManager.RttListener getAndRemoveRangingListener(int rangingId) {
-            WifiAwareManager mgr = mAwareManager.get();
-            if (mgr == null) {
-                Log.w(TAG, "getAndRemoveRangingListener: called post GC");
-                return null;
-            }
-
-            synchronized (mgr.mLock) {
-                RttManager.RttListener listener = mgr.mRangingListeners.get(rangingId);
-                mgr.mRangingListeners.delete(rangingId);
-                return listener;
-            }
-        }
-
         /**
          * Constructs a {@link AttachCallback} using the specified looper.
          * All callbacks will delivered on the thread of the specified looper.
@@ -567,37 +521,6 @@
                                 identityChangedListener.onIdentityChanged((byte[]) msg.obj);
                             }
                             break;
-                        case CALLBACK_RANGING_SUCCESS: {
-                            RttManager.RttListener listener = getAndRemoveRangingListener(msg.arg1);
-                            if (listener == null) {
-                                Log.e(TAG, "CALLBACK_RANGING_SUCCESS rangingId=" + msg.arg1
-                                        + ": no listener registered (anymore)");
-                            } else {
-                                listener.onSuccess(
-                                        ((RttManager.ParcelableRttResults) msg.obj).mResults);
-                            }
-                            break;
-                        }
-                        case CALLBACK_RANGING_FAILURE: {
-                            RttManager.RttListener listener = getAndRemoveRangingListener(msg.arg1);
-                            if (listener == null) {
-                                Log.e(TAG, "CALLBACK_RANGING_SUCCESS rangingId=" + msg.arg1
-                                        + ": no listener registered (anymore)");
-                            } else {
-                                listener.onFailure(msg.arg2, (String) msg.obj);
-                            }
-                            break;
-                        }
-                        case CALLBACK_RANGING_ABORTED: {
-                            RttManager.RttListener listener = getAndRemoveRangingListener(msg.arg1);
-                            if (listener == null) {
-                                Log.e(TAG, "CALLBACK_RANGING_SUCCESS rangingId=" + msg.arg1
-                                        + ": no listener registered (anymore)");
-                            } else {
-                                listener.onAborted();
-                            }
-                            break;
-                        }
                     }
                 }
             };
@@ -629,43 +552,6 @@
             msg.obj = mac;
             mHandler.sendMessage(msg);
         }
-
-        @Override
-        public void onRangingSuccess(int rangingId, RttManager.ParcelableRttResults results) {
-            if (VDBG) {
-                Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", results=" + results);
-            }
-
-            Message msg = mHandler.obtainMessage(CALLBACK_RANGING_SUCCESS);
-            msg.arg1 = rangingId;
-            msg.obj = results;
-            mHandler.sendMessage(msg);
-        }
-
-        @Override
-        public void onRangingFailure(int rangingId, int reason, String description) {
-            if (VDBG) {
-                Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", reason=" + reason
-                        + ", description=" + description);
-            }
-
-            Message msg = mHandler.obtainMessage(CALLBACK_RANGING_FAILURE);
-            msg.arg1 = rangingId;
-            msg.arg2 = reason;
-            msg.obj = description;
-            mHandler.sendMessage(msg);
-
-        }
-
-        @Override
-        public void onRangingAborted(int rangingId) {
-            if (VDBG) Log.v(TAG, "onRangingAborted: rangingId=" + rangingId);
-
-            Message msg = mHandler.obtainMessage(CALLBACK_RANGING_ABORTED);
-            msg.arg1 = rangingId;
-            mHandler.sendMessage(msg);
-
-        }
     }
 
     private static class WifiAwareDiscoverySessionCallbackProxy extends
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 632cfaf..622dce6 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -18,6 +18,8 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
 
 import android.os.Parcel;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
@@ -98,4 +100,73 @@
 
         assertEquals(networkSelectionStatus.isNotRecommended(), copy.isNotRecommended());
     }
+
+    @Test
+    public void testIsOpenNetwork_IsOpen_NullWepKeys() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.allowedKeyManagement.clear();
+        config.wepKeys = null;
+
+        assertTrue(config.isOpenNetwork());
+    }
+
+    @Test
+    public void testIsOpenNetwork_IsOpen_ZeroLengthWepKeysArray() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.allowedKeyManagement.clear();
+        config.wepKeys = new String[0];
+
+        assertTrue(config.isOpenNetwork());
+    }
+
+    @Test
+    public void testIsOpenNetwork_IsOpen_NullWepKeysArray() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.allowedKeyManagement.clear();
+        config.wepKeys = new String[1];
+
+        assertTrue(config.isOpenNetwork());
+    }
+
+    @Test
+    public void testIsOpenNetwork_NotOpen_HasWepKeys() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.allowedKeyManagement.clear();
+        config.wepKeys = new String[] {"test"};
+
+        assertFalse(config.isOpenNetwork());
+    }
+
+    @Test
+    public void testIsOpenNetwork_NotOpen_HasNullWepKeyFollowedByNonNullKey() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.allowedKeyManagement.clear();
+        config.wepKeys = new String[] {null, null, "test"};
+
+        assertFalse(config.isOpenNetwork());
+    }
+
+    @Test
+    public void testIsOpenNetwork_NotOpen_HasAuthType() {
+        for (int keyMgmt = 0; keyMgmt < WifiConfiguration.KeyMgmt.strings.length; keyMgmt++) {
+            if (keyMgmt == WifiConfiguration.KeyMgmt.NONE) continue;
+            WifiConfiguration config = new WifiConfiguration();
+            config.allowedKeyManagement.clear();
+            config.allowedKeyManagement.set(keyMgmt);
+            config.wepKeys = null;
+
+            assertFalse("Open network reported when key mgmt was set to "
+                            + WifiConfiguration.KeyMgmt.strings[keyMgmt], config.isOpenNetwork());
+        }
+    }
+
+    @Test
+    public void testIsOpenNetwork_NotOpen_HasAuthTypeNoneAndMore() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+        config.wepKeys = null;
+
+        assertFalse(config.isOpenNetwork());
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index d9433c5..1aeeee3 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -845,88 +845,6 @@
     }
 
     /*
-     * Ranging tests
-     */
-
-    /**
-     * Validate ranging + success flow: (1) connect, (2) create a (publish) session, (3) start
-     * ranging, (4) ranging success callback, (5) ranging aborted callback ignored (since
-     * listener removed).
-     */
-    @Test
-    public void testRangingCallbacks() throws Exception {
-        final int clientId = 4565;
-        final int sessionId = 123;
-        final int rangingId = 3482;
-        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
-        final PublishConfig publishConfig = new PublishConfig.Builder().build();
-        final RttManager.RttParams rttParams = new RttManager.RttParams();
-        rttParams.deviceType = RttManager.RTT_PEER_NAN;
-        rttParams.bssid = Integer.toString(1234);
-        final RttManager.RttResult rttResults = new RttManager.RttResult();
-        rttResults.distance = 10;
-
-        when(mockAwareService.startRanging(anyInt(), anyInt(), any())).thenReturn(rangingId);
-
-        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
-                mockPublishSession, mockRttListener);
-        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
-                WifiAwareSession.class);
-        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
-                .forClass(IWifiAwareEventCallback.class);
-        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
-                .forClass(IWifiAwareDiscoverySessionCallback.class);
-        ArgumentCaptor<PublishDiscoverySession> publishSession = ArgumentCaptor
-                .forClass(PublishDiscoverySession.class);
-        ArgumentCaptor<RttManager.ParcelableRttParams> rttParamCaptor = ArgumentCaptor
-                .forClass(RttManager.ParcelableRttParams.class);
-        ArgumentCaptor<RttManager.RttResult[]> rttResultsCaptor = ArgumentCaptor
-                .forClass(RttManager.RttResult[].class);
-
-        // (1) connect successfully
-        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
-        inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
-                eq(configRequest), eq(false));
-        clientProxyCallback.getValue().onConnectSuccess(clientId);
-        mMockLooper.dispatchAll();
-        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
-        WifiAwareSession session = sessionCaptor.getValue();
-
-        // (2) publish successfully
-        session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
-        inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
-                sessionProxyCallback.capture());
-        sessionProxyCallback.getValue().onSessionStarted(sessionId);
-        mMockLooper.dispatchAll();
-        inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
-
-        // (3) start ranging
-        publishSession.getValue().startRanging(new RttManager.RttParams[]{rttParams},
-                mockRttListener);
-        inOrder.verify(mockAwareService).startRanging(eq(clientId), eq(sessionId),
-                rttParamCaptor.capture());
-        collector.checkThat("RttParams.deviceType", rttParams.deviceType,
-                equalTo(rttParamCaptor.getValue().mParams[0].deviceType));
-        collector.checkThat("RttParams.bssid", rttParams.bssid,
-                equalTo(rttParamCaptor.getValue().mParams[0].bssid));
-
-        // (4) ranging success callback
-        clientProxyCallback.getValue().onRangingSuccess(rangingId,
-                new RttManager.ParcelableRttResults(new RttManager.RttResult[] { rttResults }));
-        mMockLooper.dispatchAll();
-        inOrder.verify(mockRttListener).onSuccess(rttResultsCaptor.capture());
-        collector.checkThat("RttResult.distance", rttResults.distance,
-                equalTo(rttResultsCaptor.getValue()[0].distance));
-
-        // (5) ranging aborted callback (should be ignored since listener cleared on first callback)
-        clientProxyCallback.getValue().onRangingAborted(rangingId);
-        mMockLooper.dispatchAll();
-
-        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
-                mockPublishSession, mockRttListener);
-    }
-
-    /*
      * Data-path tests
      */