Merge "Fix layer transform matrix for TextureView"
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 e72c4ba..936c80e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -45966,8 +45966,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 4c1323b5..39e11b7 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -49664,8 +49664,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 82be435..9560261 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -46506,8 +46506,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/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/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/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/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/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/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/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/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/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..ec83b02 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;
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/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/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/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index dc29404..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;
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/utils/ManagedApplicationService.java b/services/core/java/com/android/server/utils/ManagedApplicationService.java
index d965122..c555388 100644
--- a/services/core/java/com/android/server/utils/ManagedApplicationService.java
+++ b/services/core/java/com/android/server/utils/ManagedApplicationService.java
@@ -22,14 +22,18 @@
 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.
@@ -39,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;
@@ -46,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;
@@ -74,6 +160,9 @@
         mSettingsAction = settingsAction;
         mChecker = binderChecker;
         mIsImportant = isImportant;
+        mRetryType = retryType;
+        mHandler = handler;
+        mEventCb = eventCallback;
     }
 
     /**
@@ -88,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);
     }
 
     /**
@@ -104,14 +203,19 @@
      * @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, int clientLabel,
             @Nullable String settingsAction, @Nullable BinderChecker binderChecker,
-            boolean isImportant) {
+            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);
     }
 
 
@@ -145,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;
@@ -174,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;
         }
     }
@@ -192,7 +293,7 @@
      */
     public void connect() {
         synchronized (mLock) {
-            if (mConnection != null || mPendingConnection != null) {
+            if (mConnection != null) {
                 // We're already connected or are trying to connect
                 return;
             }
@@ -206,40 +307,56 @@
                         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);
-                            mBoundInterface = null;
-                            if (mChecker != null) {
-                                mBoundInterface = mChecker.asInterface(iBinder);
-                                if (!mChecker.checkType(mBoundInterface)) {
-                                    // Received an invalid binder, disconnect
-                                    mContext.unbindService(this);
-                                    mBoundInterface = null;
-                                }
-                                iface = mBoundInterface;
-                                pendingEvent = mPendingEvent;
-                                mPendingEvent = null;
+                        stopRetriesLocked();
+
+                        mBoundInterface = null;
+                        if (mChecker != null) {
+                            mBoundInterface = mChecker.asInterface(iBinder);
+                            if (!mChecker.checkType(mBoundInterface)) {
+                                // Received an invalid binder, disconnect.
+                                mBoundInterface = null;
+                                Slog.w(TAG, "Invalid binder from " + componentName);
+                                startRetriesLocked();
+                                return;
                             }
-                        } catch (RemoteException e) {
-                            // DOA
-                            Slog.w(TAG, "Unable to bind service: " + componentName, e);
-                            mBoundInterface = null;
+                            iface = mBoundInterface;
+                            pendingEvent = mPendingEvent;
+                            mPendingEvent = null;
                         }
                     }
                     if (iface != null && pendingEvent != null) {
@@ -247,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) {
+                    final long timestamp = System.currentTimeMillis();
                     Slog.w(TAG, "Service disconnected: " + componentName);
-                    mConnection = null;
-                    mBoundInterface = null;
+                    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();
             }
         }
     }
@@ -279,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 5a8de82..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;
@@ -148,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.*/
@@ -161,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;
@@ -277,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;
@@ -287,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;
@@ -311,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() {
@@ -505,9 +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()));
+                    ? "None" : mCurrentVrCompositorService.getComponent().flattenToString()));
             pw.println("Previous state transitions:\n");
             String tab = "  ";
             dumpStateTransitions(pw);
@@ -998,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);
     }
@@ -1033,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);
     }
 
     /**
@@ -1070,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));
             }
         }
     }
@@ -1202,6 +1286,8 @@
     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;
@@ -1210,9 +1296,8 @@
         if (componentName != null && mCurrentVrCompositorService == null) {
             // We don't have an existing service matching the requested component, so attempt to
             // connect one.
-            mCurrentVrCompositorService = ManagedApplicationService.build(mContext,
-                    componentName, userId, /*clientLabel*/0, /*settingsAction*/null,
-                    /*binderChecker*/null, /*isImportant*/true);
+            Slog.i(TAG, "Connecting compositor service: " + componentName);
+            mCurrentVrCompositorService = createVrCompositorService(componentName, userId);
             mCurrentVrCompositorService.connect();
         }
     }
@@ -1221,6 +1306,9 @@
         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/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/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/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));