Merge "Only init ARC when system audio mode is on"
diff --git a/Android.bp b/Android.bp
index 490de6e..ee3b396 100644
--- a/Android.bp
+++ b/Android.bp
@@ -279,6 +279,7 @@
         "core/java/android/os/storage/IStorageShutdownObserver.aidl",
         "core/java/android/os/storage/IObbActionListener.aidl",
         "core/java/android/permission/IPermissionController.aidl",
+        "core/java/android/permission/IPermissionManager.aidl",
         ":keystore_aidl",
         "core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl",
         "core/java/android/service/appprediction/IPredictionService.aidl",
diff --git a/api/current.txt b/api/current.txt
index 2cfdd87..25801ca 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -38638,6 +38638,7 @@
     field public static final String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
     field public static final String ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS";
     field public static final String ACTION_CHANNEL_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_NOTIFICATION_SETTINGS";
+    field public static final String ACTION_CONDITION_PROVIDER_SETTINGS = "android.settings.ACTION_CONDITION_PROVIDER_SETTINGS";
     field public static final String ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS";
     field public static final String ACTION_DATA_USAGE_SETTINGS = "android.settings.DATA_USAGE_SETTINGS";
     field public static final String ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS";
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5ab694f..d741b61 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -113,6 +113,7 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.permission.IPermissionManager;
 import android.provider.BlockedNumberContract;
 import android.provider.CalendarContract;
 import android.provider.CallLog;
@@ -287,6 +288,7 @@
 
     @UnsupportedAppUsage
     static volatile IPackageManager sPackageManager;
+    private static volatile IPermissionManager sPermissionManager;
 
     @UnsupportedAppUsage
     final ApplicationThread mAppThread = new ApplicationThread();
@@ -2136,16 +2138,23 @@
     @UnsupportedAppUsage
     public static IPackageManager getPackageManager() {
         if (sPackageManager != null) {
-            //Slog.v("PackageManager", "returning cur default = " + sPackageManager);
             return sPackageManager;
         }
-        IBinder b = ServiceManager.getService("package");
-        //Slog.v("PackageManager", "default service binder = " + b);
+        final IBinder b = ServiceManager.getService("package");
         sPackageManager = IPackageManager.Stub.asInterface(b);
-        //Slog.v("PackageManager", "default service = " + sPackageManager);
         return sPackageManager;
     }
 
+    /** Returns the permission manager */
+    public static IPermissionManager getPermissionManager() {
+        if (sPermissionManager != null) {
+            return sPermissionManager;
+        }
+        final IBinder b = ServiceManager.getService("permissionmgr");
+        sPermissionManager = IPermissionManager.Stub.asInterface(b);
+        return sPermissionManager;
+    }
+
     private Configuration mMainThreadConfig = new Configuration();
 
     Configuration applyConfigCompatMainThread(int displayDensity, Configuration config,
@@ -6317,7 +6326,8 @@
         final InstrumentationInfo ii;
         if (data.instrumentationName != null) {
             try {
-                ii = new ApplicationPackageManager(null, getPackageManager())
+                ii = new ApplicationPackageManager(
+                        null, getPackageManager(), getPermissionManager())
                         .getInstrumentationInfo(data.instrumentationName, 0);
             } catch (PackageManager.NameNotFoundException e) {
                 throw new RuntimeException(
diff --git a/core/java/android/app/AppGlobals.java b/core/java/android/app/AppGlobals.java
index 1f737b6..8312327 100644
--- a/core/java/android/app/AppGlobals.java
+++ b/core/java/android/app/AppGlobals.java
@@ -18,6 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.content.pm.IPackageManager;
+import android.permission.IPermissionManager;
 
 /**
  * Special private access for certain globals related to a process.
@@ -52,6 +53,14 @@
     }
 
     /**
+     * Return the raw interface to the permission manager.
+     * @return The permission manager.
+     */
+    public static IPermissionManager getPermissionManager() {
+        return ActivityThread.getPermissionManager();
+    }
+
+    /**
      * Gets the value of an integer core setting.
      *
      * @param key The setting key.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 525bc44b..1641afb 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -82,6 +82,7 @@
 import android.os.UserManager;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
+import android.permission.IPermissionManager;
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -320,30 +321,60 @@
     }
 
     @Override
-    public PermissionInfo getPermissionInfo(String name, int flags)
+    @SuppressWarnings("unchecked")
+    public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
+        try {
+            final ParceledListSlice<PermissionGroupInfo> parceledList =
+                    mPermissionManager.getAllPermissionGroups(flags);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags)
             throws NameNotFoundException {
         try {
-            PermissionInfo pi = mPM.getPermissionInfo(name,
-                    mContext.getOpPackageName(), flags);
+            final PermissionGroupInfo pgi =
+                    mPermissionManager.getPermissionGroupInfo(groupName, flags);
+            if (pgi != null) {
+                return pgi;
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        throw new NameNotFoundException(groupName);
+    }
+
+    @Override
+    public PermissionInfo getPermissionInfo(String permName, int flags)
+            throws NameNotFoundException {
+        try {
+            final String packageName = mContext.getOpPackageName();
+            final PermissionInfo pi =
+                    mPermissionManager.getPermissionInfo(permName, packageName, flags);
             if (pi != null) {
                 return pi;
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-
-        throw new NameNotFoundException(name);
+        throw new NameNotFoundException(permName);
     }
 
     @Override
     @SuppressWarnings("unchecked")
-    public List<PermissionInfo> queryPermissionsByGroup(String group, int flags)
+    public List<PermissionInfo> queryPermissionsByGroup(String groupName, int flags)
             throws NameNotFoundException {
         try {
-            ParceledListSlice<PermissionInfo> parceledList =
-                    mPM.queryPermissionsByGroup(group, flags);
+            final ParceledListSlice<PermissionInfo> parceledList =
+                    mPermissionManager.queryPermissionsByGroup(groupName, flags);
             if (parceledList != null) {
-                List<PermissionInfo> pi = parceledList.getList();
+                final List<PermissionInfo> pi = parceledList.getList();
                 if (pi != null) {
                     return pi;
                 }
@@ -351,8 +382,7 @@
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-
-        throw new NameNotFoundException(group);
+        throw new NameNotFoundException(groupName);
     }
 
     @Override
@@ -368,36 +398,6 @@
     }
 
     @Override
-    public PermissionGroupInfo getPermissionGroupInfo(String name,
-            int flags) throws NameNotFoundException {
-        try {
-            PermissionGroupInfo pgi = mPM.getPermissionGroupInfo(name, flags);
-            if (pgi != null) {
-                return pgi;
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
-        throw new NameNotFoundException(name);
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
-        try {
-            ParceledListSlice<PermissionGroupInfo> parceledList =
-                    mPM.getAllPermissionGroups(flags);
-            if (parceledList == null) {
-                return Collections.emptyList();
-            }
-            return parceledList.getList();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @Override
     public ApplicationInfo getApplicationInfo(String packageName, int flags)
             throws NameNotFoundException {
         return getApplicationInfoAsUser(packageName, flags, getUserId());
@@ -661,7 +661,7 @@
     @Override
     public boolean addPermission(PermissionInfo info) {
         try {
-            return mPM.addPermission(info);
+            return mPermissionManager.addPermission(info, false);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -670,7 +670,7 @@
     @Override
     public boolean addPermissionAsync(PermissionInfo info) {
         try {
-            return mPM.addPermissionAsync(info);
+            return mPermissionManager.addPermission(info, true);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -679,7 +679,7 @@
     @Override
     public void removePermission(String name) {
         try {
-            mPM.removePermission(name);
+            mPermissionManager.removePermission(name);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1655,10 +1655,11 @@
     }
 
     @UnsupportedAppUsage
-    protected ApplicationPackageManager(ContextImpl context,
-                              IPackageManager pm) {
+    protected ApplicationPackageManager(ContextImpl context, IPackageManager pm,
+            IPermissionManager permissionManager) {
         mContext = context;
         mPM = pm;
+        mPermissionManager = permissionManager;
     }
 
     /**
@@ -2930,6 +2931,7 @@
     private final ContextImpl mContext;
     @UnsupportedAppUsage
     private final IPackageManager mPM;
+    private final IPermissionManager mPermissionManager;
 
     /** Assume locked until we hear otherwise */
     private volatile boolean mUserUnlocked = false;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 41a4fba..ef23d5e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -68,6 +68,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
+import android.permission.IPermissionManager;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -297,10 +298,11 @@
             return mPackageManager;
         }
 
-        IPackageManager pm = ActivityThread.getPackageManager();
-        if (pm != null) {
+        final IPackageManager pm = ActivityThread.getPackageManager();
+        final IPermissionManager permissionManager = ActivityThread.getPermissionManager();
+        if (pm != null && permissionManager != null) {
             // Doesn't matter if we make more than one instance.
-            return (mPackageManager = new ApplicationPackageManager(this, pm));
+            return (mPackageManager = new ApplicationPackageManager(this, pm, permissionManager));
         }
 
         return null;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index a7eecd7..277e41d 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -78,15 +78,6 @@
     @UnsupportedAppUsage
     String[] canonicalToCurrentPackageNames(in String[] names);
 
-    PermissionInfo getPermissionInfo(String name, String packageName, int flags);
-
-    ParceledListSlice queryPermissionsByGroup(String group, int flags);
-
-    @UnsupportedAppUsage
-    PermissionGroupInfo getPermissionGroupInfo(String name, int flags);
-
-    ParceledListSlice getAllPermissionGroups(int flags);
-
     @UnsupportedAppUsage
     ApplicationInfo getApplicationInfo(String packageName, int flags ,int userId);
 
@@ -111,12 +102,6 @@
     int checkUidPermission(String permName, int uid);
 
     @UnsupportedAppUsage
-    boolean addPermission(in PermissionInfo info);
-
-    @UnsupportedAppUsage
-    void removePermission(String name);
-
-    @UnsupportedAppUsage
     void grantRuntimePermission(String packageName, String permissionName, int userId);
 
     void revokeRuntimePermission(String packageName, String permissionName, int userId);
@@ -171,9 +156,6 @@
     boolean isUidPrivileged(int uid);
 
     @UnsupportedAppUsage
-    String[] getAppOpPermissionPackages(String permissionName);
-
-    @UnsupportedAppUsage
     ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId);
 
     ResolveInfo findPersistentPreferredActivity(in Intent intent, int userId);
@@ -626,9 +608,6 @@
     int movePackage(in String packageName, in String volumeUuid);
     int movePrimaryStorage(in String volumeUuid);
 
-    @UnsupportedAppUsage
-    boolean addPermissionAsync(in PermissionInfo info);
-
     boolean setInstallLocation(int loc);
     @UnsupportedAppUsage
     int getInstallLocation();
@@ -772,4 +751,24 @@
     void setRuntimePermissionsVersion(int version, int userId);
 
     void notifyPackagesReplacedReceived(in String[] packages);
+
+    //------------------------------------------------------------------------
+    //
+    // The following binder interfaces have been moved to IPermissionManager
+    //
+    //------------------------------------------------------------------------
+    @UnsupportedAppUsage
+    String[] getAppOpPermissionPackages(String permissionName);
+
+    @UnsupportedAppUsage
+    PermissionGroupInfo getPermissionGroupInfo(String name, int flags);
+
+    @UnsupportedAppUsage
+    boolean addPermission(in PermissionInfo info);
+
+    @UnsupportedAppUsage
+    boolean addPermissionAsync(in PermissionInfo info);
+
+    @UnsupportedAppUsage
+    void removePermission(String name);
 }
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 672994e..1c1d709 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -999,4 +999,11 @@
      * Migrates legacy obb data to its new location.
      */
     public abstract void migrateLegacyObbData();
+
+    /**
+     * Writes all package manager settings to disk. If {@code async} is {@code true}, the
+     * settings are written at some point in the future. Otherwise, the call blocks until
+     * the settings have been written.
+     */
+    public abstract void writeSettings(boolean async);
 }
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index b0b1874..23fbefb 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -17,7 +17,6 @@
 package android.content.pm;
 
 import android.Manifest;
-import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -177,8 +176,7 @@
         mContext.registerReceiver(mUserRemovedReceiver, userFilter);
     }
 
-    @VisibleForTesting
-    protected void handlePackageEvent(Intent intent, int userId) {
+    private void handlePackageEvent(Intent intent, int userId) {
         // Don't regenerate the services map when the package is removed or its
         // ASEC container unmounted as a step in replacement.  The subsequent
         // _ADDED / _AVAILABLE call will regenerate the map in the final state.
@@ -240,9 +238,6 @@
 
     public void invalidateCache(int userId) {
         synchronized (mServicesLock) {
-            if (DEBUG) {
-                Slog.d(TAG, "invalidating cache for " + userId + " " + mInterfaceName);
-            }
             final UserServices<V> user = findOrCreateUserLocked(userId);
             user.services = null;
             onServicesChangedLocked(userId);
@@ -472,48 +467,34 @@
      *                    or null to assume that everything is affected.
      * @param userId the user for whom to update the services map.
      */
-    private void generateServicesMap(@Nullable int[] changedUids, int userId) {
+    private void generateServicesMap(int[] changedUids, int userId) {
         if (DEBUG) {
             Slog.d(TAG, "generateServicesMap() for " + userId + ", changed UIDs = "
                     + Arrays.toString(changedUids));
         }
 
+        final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>();
+        final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            try {
+                ServiceInfo<V> info = parseServiceInfo(resolveInfo);
+                if (info == null) {
+                    Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
+                    continue;
+                }
+                serviceInfos.add(info);
+            } catch (XmlPullParserException | IOException e) {
+                Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
+            }
+        }
+
         synchronized (mServicesLock) {
             final UserServices<V> user = findOrCreateUserLocked(userId);
-            final boolean cacheInvalid = user.services == null;
-            if (cacheInvalid) {
+            final boolean firstScan = user.services == null;
+            if (firstScan) {
                 user.services = Maps.newHashMap();
             }
 
-            final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>();
-            final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
-
-            for (ResolveInfo resolveInfo : resolveInfos) {
-                try {
-                    // when changedUids == null, we want to do a rescan of everything, this means
-                    // it's the initial scan, and containsUid will trivially return true
-                    // when changedUids != null, we got here because a package changed, but
-                    // invalidateCache could have been called (thus user.services == null), and we
-                    // should query from PackageManager again
-                    if (!cacheInvalid
-                            && !containsUid(
-                                    changedUids, resolveInfo.serviceInfo.applicationInfo.uid)) {
-                        if (DEBUG) {
-                            Slog.d(TAG, "Skipping parseServiceInfo for " + resolveInfo);
-                        }
-                        continue;
-                    }
-                    ServiceInfo<V> info = parseServiceInfo(resolveInfo);
-                    if (info == null) {
-                        Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
-                        continue;
-                    }
-                    serviceInfos.add(info);
-                } catch (XmlPullParserException | IOException e) {
-                    Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e);
-                }
-            }
-
             StringBuilder changes = new StringBuilder();
             boolean changed = false;
             for (ServiceInfo<V> info : serviceInfos) {
@@ -534,7 +515,7 @@
                     changed = true;
                     user.services.put(info.type, info);
                     user.persistentServices.put(info.type, info.uid);
-                    if (!(user.mPersistentServicesFileDidNotExist && cacheInvalid)) {
+                    if (!(user.mPersistentServicesFileDidNotExist && firstScan)) {
                         notifyListener(info.type, userId, false /* removed */);
                     }
                 } else if (previousUid == info.uid) {
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index d05ba79..972ae2a 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -35,8 +35,10 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -61,7 +63,31 @@
 
     private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
 
-    private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+    /**
+     * A cache of the current device's physical address. When device's HDMI out port
+     * is not connected to any device, it is set to {@link #INVALID_PHYSICAL_ADDRESS}.
+     *
+     * <p>Otherwise it is updated by the {@link ClientHotplugEventListener} registered
+     * with {@link com.android.server.hdmi.HdmiControlService} by the
+     * {@link #addHotplugEventListener(HotplugEventListener)} and the address is from
+     * {@link com.android.server.hdmi.HdmiControlService#getPortInfo()}
+     */
+    @GuardedBy("mLock")
+    private int mLocalPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+
+    private void setLocalPhysicalAddress(int physicalAddress) {
+        synchronized (mLock) {
+            mLocalPhysicalAddress = physicalAddress;
+        }
+    }
+
+    private int getLocalPhysicalAddress() {
+        synchronized (mLock) {
+            return mLocalPhysicalAddress;
+        }
+    }
+
+    private final Object mLock = new Object();
 
     /**
      * Broadcast Action: Display OSD message.
@@ -318,6 +344,37 @@
         mHasSwitchDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
         mIsSwitchDevice = SystemProperties.getBoolean(
             PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
+        addHotplugEventListener(new ClientHotplugEventListener());
+    }
+
+    private final class ClientHotplugEventListener implements HotplugEventListener {
+
+        @Override
+        public void onReceived(HdmiHotplugEvent event) {
+            List<HdmiPortInfo> ports = new ArrayList<>();
+            try {
+                ports = mService.getPortInfo();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+            if (ports.isEmpty()) {
+                Log.e(TAG, "Can't find port info, not updating connected status. "
+                        + "Hotplug event:" + event);
+                return;
+            }
+            // If the HDMI OUT port is plugged or unplugged, update the mLocalPhysicalAddress
+            for (HdmiPortInfo port : ports) {
+                if (port.getId() == event.getPort()) {
+                    if (port.getType() == HdmiPortInfo.PORT_OUTPUT) {
+                        setLocalPhysicalAddress(
+                                event.isConnected()
+                                ? port.getAddress()
+                                : INVALID_PHYSICAL_ADDRESS);
+                    }
+                    break;
+                }
+            }
+        }
     }
 
     private static boolean hasDeviceType(int[] types, int type) {
@@ -616,15 +673,7 @@
      */
     @SystemApi
     public int getPhysicalAddress() {
-        if (mPhysicalAddress != INVALID_PHYSICAL_ADDRESS) {
-            return mPhysicalAddress;
-        }
-        try {
-            mPhysicalAddress = mService.getPhysicalAddress();
-            return mPhysicalAddress;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getLocalPhysicalAddress();
     }
 
     /**
@@ -641,15 +690,15 @@
     @SystemApi
     public boolean isDeviceConnected(@NonNull HdmiDeviceInfo targetDevice) {
         Preconditions.checkNotNull(targetDevice);
-        mPhysicalAddress = getPhysicalAddress();
-        if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
+        int physicalAddress = getLocalPhysicalAddress();
+        if (physicalAddress == INVALID_PHYSICAL_ADDRESS) {
             return false;
         }
         int targetPhysicalAddress = targetDevice.getPhysicalAddress();
         if (targetPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
             return false;
         }
-        return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, mPhysicalAddress)
+        return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, physicalAddress)
             != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE;
     }
 
@@ -662,15 +711,15 @@
     @SystemApi
     public boolean isRemoteDeviceConnected(@NonNull HdmiDeviceInfo targetDevice) {
         Preconditions.checkNotNull(targetDevice);
-        mPhysicalAddress = getPhysicalAddress();
-        if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
+        int physicalAddress = getLocalPhysicalAddress();
+        if (physicalAddress == INVALID_PHYSICAL_ADDRESS) {
             return false;
         }
         int targetPhysicalAddress = targetDevice.getPhysicalAddress();
         if (targetPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
             return false;
         }
-        return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, mPhysicalAddress)
+        return HdmiUtils.getLocalPortFromPhysicalAddress(targetPhysicalAddress, physicalAddress)
             != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE;
     }
 
diff --git a/core/java/android/os/IServiceManager.java b/core/java/android/os/IServiceManager.java
deleted file mode 100644
index 053c5ed..0000000
--- a/core/java/android/os/IServiceManager.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2006 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.os;
-
-import android.annotation.UnsupportedAppUsage;
-
-/**
- * Basic interface for finding and publishing system services.
- *
- * An implementation of this interface is usually published as the
- * global context object, which can be retrieved via
- * BinderNative.getContextObject().  An easy way to retrieve this
- * is with the static method BnServiceManager.getDefault().
- *
- * @hide
- */
-public interface IServiceManager extends IInterface
-{
-    /**
-     * Retrieve an existing service called @a name from the
-     * service manager.  Blocks for a few seconds waiting for it to be
-     * published if it does not already exist.
-     */
-    @UnsupportedAppUsage
-    IBinder getService(String name) throws RemoteException;
-
-    /**
-     * Retrieve an existing service called @a name from the
-     * service manager.  Non-blocking.
-     */
-    @UnsupportedAppUsage
-    IBinder checkService(String name) throws RemoteException;
-
-    /**
-     * Place a new @a service called @a name into the service
-     * manager.
-     */
-    void addService(String name, IBinder service, boolean allowIsolated, int dumpFlags)
-            throws RemoteException;
-
-    /**
-     * Return a list of all currently running services.
-     */
-    String[] listServices(int dumpFlags) throws RemoteException;
-
-    static final String descriptor = "android.os.IServiceManager";
-
-    int GET_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
-    int CHECK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1;
-    int ADD_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2;
-    int LIST_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3;
-    int CHECK_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4;
-    int SET_PERMISSION_CONTROLLER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+5;
-
-    /*
-     * Must update values in IServiceManager.h
-     */
-    /* Allows services to dump sections according to priorities. */
-    int DUMP_FLAG_PRIORITY_CRITICAL = 1 << 0;
-    int DUMP_FLAG_PRIORITY_HIGH = 1 << 1;
-    int DUMP_FLAG_PRIORITY_NORMAL = 1 << 2;
-    /**
-     * Services are by default registered with a DEFAULT dump priority. DEFAULT priority has the
-     * same priority as NORMAL priority but the services are not called with dump priority
-     * arguments.
-     */
-    int DUMP_FLAG_PRIORITY_DEFAULT = 1 << 3;
-    int DUMP_FLAG_PRIORITY_ALL = DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_HIGH
-            | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PRIORITY_DEFAULT;
-    /* Allows services to dump sections in protobuf format. */
-    int DUMP_FLAG_PROTO = 1 << 4;
-
-}
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 011dfa0..7991cd4 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -18,8 +18,6 @@
 
 import android.annotation.UnsupportedAppUsage;
 
-import java.util.ArrayList;
-
 /**
  * Native implementation of the service manager.  Most clients will only
  * care about asInterface().
@@ -32,26 +30,27 @@
     /**
      * Cast a Binder object into a service manager interface, generating
      * a proxy if needed.
+     *
+     * TODO: delete this method and have clients use
+     *     IServiceManager.Stub.asInterface instead
      */
     @UnsupportedAppUsage
-    static public IServiceManager asInterface(IBinder obj)
-    {
+    public static IServiceManager asInterface(IBinder obj) {
         if (obj == null) {
             return null;
         }
-        IServiceManager in =
-                (IServiceManager) obj.queryLocalInterface(IServiceManager.descriptor);
-        if (in != null) {
-            return in;
-        }
 
+        // ServiceManager is never local
         return new ServiceManagerProxy(obj);
     }
 }
 
+// This class should be deleted and replaced with IServiceManager.Stub whenever
+// mRemote is no longer used
 class ServiceManagerProxy implements IServiceManager {
     public ServiceManagerProxy(IBinder remote) {
         mRemote = remote;
+        mServiceManager = IServiceManager.Stub.asInterface(remote);
     }
 
     public IBinder asBinder() {
@@ -60,73 +59,30 @@
 
     @UnsupportedAppUsage
     public IBinder getService(String name) throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IServiceManager.descriptor);
-        data.writeString(name);
-        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
-        IBinder binder = reply.readStrongBinder();
-        reply.recycle();
-        data.recycle();
-        return binder;
+        // Same as checkService (old versions of servicemanager had both methods).
+        return mServiceManager.checkService(name);
     }
 
     public IBinder checkService(String name) throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IServiceManager.descriptor);
-        data.writeString(name);
-        mRemote.transact(CHECK_SERVICE_TRANSACTION, data, reply, 0);
-        IBinder binder = reply.readStrongBinder();
-        reply.recycle();
-        data.recycle();
-        return binder;
+        return mServiceManager.checkService(name);
     }
 
     public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
             throws RemoteException {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-        data.writeInterfaceToken(IServiceManager.descriptor);
-        data.writeString(name);
-        data.writeStrongBinder(service);
-        data.writeInt(allowIsolated ? 1 : 0);
-        data.writeInt(dumpPriority);
-        mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
-        reply.recycle();
-        data.recycle();
+        mServiceManager.addService(name, service, allowIsolated, dumpPriority);
     }
 
     public String[] listServices(int dumpPriority) throws RemoteException {
-        ArrayList<String> services = new ArrayList<String>();
-        int n = 0;
-        while (true) {
-            Parcel data = Parcel.obtain();
-            Parcel reply = Parcel.obtain();
-            data.writeInterfaceToken(IServiceManager.descriptor);
-            data.writeInt(n);
-            data.writeInt(dumpPriority);
-            n++;
-            try {
-                boolean res = mRemote.transact(LIST_SERVICES_TRANSACTION, data, reply, 0);
-                if (!res) {
-                    break;
-                }
-            } catch (RuntimeException e) {
-                // The result code that is returned by the C++ code can
-                // cause the call to throw an exception back instead of
-                // returning a nice result...  so eat it here and go on.
-                break;
-            }
-            services.add(reply.readString());
-            reply.recycle();
-            data.recycle();
-        }
-        String[] array = new String[services.size()];
-        services.toArray(array);
-        return array;
+        return mServiceManager.listServices(dumpPriority);
     }
 
+    /**
+     * Same as mServiceManager but used by apps.
+     *
+     * Once this can be removed, ServiceManagerProxy should be removed entirely.
+     */
     @UnsupportedAppUsage
     private IBinder mRemote;
+
+    private IServiceManager mServiceManager;
 }
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
new file mode 100644
index 0000000..3b69b12
--- /dev/null
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 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.permission;
+
+import android.content.pm.ParceledListSlice;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+
+/**
+ * Interface to communicate directly with the permission manager service.
+ * @see PermissionManager
+ * @hide
+ */
+interface IPermissionManager {
+    String[] getAppOpPermissionPackages(String permName);
+
+    ParceledListSlice getAllPermissionGroups(int flags);
+
+    PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags);
+
+    PermissionInfo getPermissionInfo(String permName, String packageName, int flags);
+
+    ParceledListSlice queryPermissionsByGroup(String groupName, int flags);
+
+    boolean addPermission(in PermissionInfo info, boolean async);
+
+    void removePermission(String name);
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 01058f9..2ee6b7d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -42,6 +42,7 @@
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.app.Application;
+import android.app.AutomaticZenRule;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.SearchManager;
@@ -1313,7 +1314,17 @@
             "android.settings.NOTIFICATION_POLICY_ACCESS_DETAIL_SETTINGS";
 
     /**
-     * @hide
+     * Activity Action: Show the automatic do not disturb rule listing page
+     * <p>
+     *     Users can add, enable, disable, and remove automatic do not disturb rules from this
+     *     screen. See {@link NotificationManager#addAutomaticZenRule(AutomaticZenRule)} for more
+     *     details.
+     * </p>
+     * <p>
+     *     Input: Nothing
+     *     Output: Nothing
+     * </p>
+     *
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_CONDITION_PROVIDER_SETTINGS
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index f661e06..6563e03 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -23,7 +23,6 @@
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.CompatibilityInfo.Translator;
-import android.content.res.Configuration;
 import android.graphics.BlendMode;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -102,8 +101,7 @@
     private static final boolean DEBUG = false;
 
     @UnsupportedAppUsage
-    final ArrayList<SurfaceHolder.Callback> mCallbacks
-            = new ArrayList<SurfaceHolder.Callback>();
+    final ArrayList<SurfaceHolder.Callback> mCallbacks = new ArrayList<>();
 
     final int[] mLocation = new int[2];
 
@@ -127,7 +125,6 @@
     SurfaceControl mDeferredDestroySurfaceControl;
     SurfaceControl mBackgroundControl;
     final Rect mTmpRect = new Rect();
-    final Configuration mConfiguration = new Configuration();
 
     Paint mRoundedViewportPaint;
 
@@ -137,25 +134,16 @@
     boolean mIsCreating = false;
     private volatile boolean mRtHandlingPositionUpdates = false;
 
-    private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
-            = new ViewTreeObserver.OnScrollChangedListener() {
-                    @Override
-                    public void onScrollChanged() {
-                        updateSurface();
-                    }
-            };
+    private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener =
+            this::updateSurface;
 
     @UnsupportedAppUsage
-    private final ViewTreeObserver.OnPreDrawListener mDrawListener =
-            new ViewTreeObserver.OnPreDrawListener() {
-                @Override
-                public boolean onPreDraw() {
-                    // reposition ourselves where the surface is
-                    mHaveFrame = getWidth() > 0 && getHeight() > 0;
-                    updateSurface();
-                    return true;
-                }
-            };
+    private final ViewTreeObserver.OnPreDrawListener mDrawListener = () -> {
+        // reposition ourselves where the surface is
+        mHaveFrame = getWidth() > 0 && getHeight() > 0;
+        updateSurface();
+        return true;
+    };
 
     boolean mRequestedVisible = false;
     boolean mWindowVisibility = false;
@@ -190,7 +178,6 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     final Rect mSurfaceFrame = new Rect();
     int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
-    private Translator mTranslator;
 
     private boolean mGlobalListenersAdded;
     private boolean mAttachedToWindow;
@@ -555,9 +542,9 @@
             return;
         }
 
-        mTranslator = viewRoot.mTranslator;
-        if (mTranslator != null) {
-            mSurface.setCompatibilityTranslator(mTranslator);
+        final Translator translator = viewRoot.mTranslator;
+        if (translator != null) {
+            mSurface.setCompatibilityTranslator(translator);
         }
 
         int myWidth = mRequestedWidth;
@@ -596,8 +583,8 @@
                 mScreenRect.top = mWindowSpaceTop;
                 mScreenRect.right = mWindowSpaceLeft + getWidth();
                 mScreenRect.bottom = mWindowSpaceTop + getHeight();
-                if (mTranslator != null) {
-                    mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+                if (translator != null) {
+                    translator.translateRectInAppWindowToScreen(mScreenRect);
                 }
 
                 final Rect surfaceInsets = getParentSurfaceInsets();
@@ -683,11 +670,11 @@
 
                     mSurfaceFrame.left = 0;
                     mSurfaceFrame.top = 0;
-                    if (mTranslator == null) {
+                    if (translator == null) {
                         mSurfaceFrame.right = mSurfaceWidth;
                         mSurfaceFrame.bottom = mSurfaceHeight;
                     } else {
-                        float appInvertedScale = mTranslator.applicationInvertedScale;
+                        float appInvertedScale = translator.applicationInvertedScale;
                         mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
                         mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
                     }
@@ -821,8 +808,8 @@
                 mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop,
                         mWindowSpaceLeft + mLocation[0], mWindowSpaceTop + mLocation[1]);
 
-                if (mTranslator != null) {
-                    mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+                if (translator != null) {
+                    translator.translateRectInAppWindowToScreen(mScreenRect);
                 }
 
                 if (mSurfaceControl == null) {
@@ -1037,7 +1024,7 @@
             synchronized (mCallbacks) {
                 // This is a linear search, but in practice we'll
                 // have only a couple callbacks, so it doesn't matter.
-                if (mCallbacks.contains(callback) == false) {
+                if (!mCallbacks.contains(callback)) {
                     mCallbacks.add(callback);
                 }
             }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index f18aa81..a01e15e 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -288,9 +288,6 @@
     // applied as translation when updating the root render node.
     private int mInsetTop, mInsetLeft;
 
-    // Whether the surface has insets. Used to protect opacity.
-    private boolean mHasInsets;
-
     // Light properties specified by the theme.
     private final float mLightY;
     private final float mLightZ;
@@ -480,7 +477,6 @@
 
         if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0
                 || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) {
-            mHasInsets = true;
             mInsetLeft = surfaceInsets.left;
             mInsetTop = surfaceInsets.top;
             mSurfaceWidth = width + mInsetLeft + surfaceInsets.right;
@@ -489,7 +485,6 @@
             // If the surface has insets, it can't be opaque.
             setOpaque(false);
         } else {
-            mHasInsets = false;
             mInsetLeft = 0;
             mInsetTop = 0;
             mSurfaceWidth = width;
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 6f4f337..fe66cf9 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1311,7 +1311,7 @@
             return semiTransparentBarColor;
         } else if ((flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
             return Color.BLACK;
-        } else if (scrimTransparent && barColor == Color.TRANSPARENT) {
+        } else if (scrimTransparent && Color.alpha(barColor) == 0) {
             boolean light = (sysuiVis & lightSysuiFlag) != 0;
             return light ? SCRIM_LIGHT : semiTransparentBarColor;
         } else {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 5eb8408..d42c43b 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -33,17 +33,14 @@
 
     srcs: [
         "android_animation_PropertyValuesHolder.cpp",
-        "android_content_res_ApkAssets.cpp",
         "android_graphics_Canvas.cpp",
         "android_graphics_ColorSpace.cpp",
         "android_graphics_drawable_AnimatedVectorDrawable.cpp",
         "android_graphics_drawable_VectorDrawable.cpp",
         "android_graphics_Picture.cpp",
         "android_nio_utils.cpp",
-        "android_os_MessageQueue.cpp",
         "android_os_SystemClock.cpp",
         "android_os_SystemProperties.cpp",
-        "android_os_Trace.cpp",
         "android_util_EventLog.cpp",
         "android_util_Log.cpp",
         "android_util_PathParser.cpp",
@@ -76,9 +73,6 @@
         "android/graphics/fonts/FontFamily.cpp",
         "android/graphics/text/LineBreaker.cpp",
         "android/graphics/text/MeasuredText.cpp",
-        "android_util_AssetManager.cpp",
-        "android_util_StringBlock.cpp",
-        "android_util_XmlBlock.cpp",
         "com_android_internal_util_VirtualRefBasePtr.cpp",
         "com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp",
     ],
@@ -176,18 +170,23 @@
                 "android_os_HwRemoteBinder.cpp",
                 "android_os_NativeHandle.cpp",
                 "android_os_MemoryFile.cpp",
+                "android_os_MessageQueue.cpp",
                 "android_os_Parcel.cpp",
                 "android_os_SELinux.cpp",
                 "android_os_SharedMemory.cpp",
+                "android_os_Trace.cpp",
                 "android_os_UEventObserver.cpp",
                 "android_os_VintfObject.cpp",
                 "android_os_VintfRuntimeInfo.cpp",
                 "android_net_LocalSocketImpl.cpp",
                 "android_net_NetUtils.cpp",
+                "android_util_AssetManager.cpp",
                 "android_util_Binder.cpp",
                 "android_util_StatsLog.cpp",
                 "android_util_MemoryIntArray.cpp",
                 "android_util_Process.cpp",
+                "android_util_StringBlock.cpp",
+                "android_util_XmlBlock.cpp",
                 "android_util_jar_StrictJarFile.cpp",
                 "android/graphics/AnimatedImageDrawable.cpp",
                 "android/graphics/Camera.cpp",
@@ -243,6 +242,7 @@
                 "android_backup_FileBackupHelperBase.cpp",
                 "android_backup_BackupHelperDispatcher.cpp",
                 "android_app_backup_FullBackup.cpp",
+                "android_content_res_ApkAssets.cpp",
                 "android_content_res_ObbScanner.cpp",
                 "android_content_res_Configuration.cpp",
                 "android_security_Scrypt.cpp",
@@ -355,5 +355,15 @@
                 "libutils",
             ],
         },
+        linux_glibc: {
+            srcs: [
+                "android_content_res_ApkAssets.cpp",
+                "android_os_MessageQueue.cpp",
+                "android_os_Trace.cpp",
+                "android_util_AssetManager.cpp",
+                "android_util_StringBlock.cpp",
+                "android_util_XmlBlock.cpp",
+            ],
+        },
     },
 }
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index eb70035..549fd4d 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -91,10 +91,12 @@
 // The actual list of registered classes will be determined at runtime via the 'native_classes' System property
 static const std::unordered_map<std::string, RegJNIRec>  gRegJNIMap = {
     {"android.animation.PropertyValuesHolder", REG_JNI(register_android_animation_PropertyValuesHolder)},
+#ifdef __linux__
     {"android.content.AssetManager", REG_JNI(register_android_content_AssetManager)},
     {"android.content.StringBlock", REG_JNI(register_android_content_StringBlock)},
     {"android.content.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
     {"android.content.res.ApkAssets", REG_JNI(register_android_content_res_ApkAssets)},
+#endif
     {"android.graphics.Bitmap", REG_JNI(register_android_graphics_Bitmap)},
     {"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)},
     {"android.graphics.ByteBufferStreamAdaptor", REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)},
@@ -125,10 +127,14 @@
     {"android.graphics.fonts.FontFamily", REG_JNI(register_android_graphics_fonts_FontFamily)},
     {"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)},
     {"android.graphics.text.MeasuredText", REG_JNI(register_android_graphics_text_MeasuredText)},
+#ifdef __linux__
     {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
+#endif
     {"android.os.SystemClock", REG_JNI(register_android_os_SystemClock)},
     {"android.os.SystemProperties", REG_JNI(register_android_os_SystemProperties)},
+#ifdef __linux__
     {"android.os.Trace", REG_JNI(register_android_os_Trace)},
+#endif
     {"android.util.EventLog", REG_JNI(register_android_util_EventLog)},
     {"android.util.Log", REG_JNI(register_android_util_Log)},
     {"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index ccb840d..b0443a8 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -42,7 +42,6 @@
 #include <system/window.h>
 
 #include <FrameInfo.h>
-#include <IContextFactory.h>
 #include <Picture.h>
 #include <Properties.h>
 #include <RootRenderNode.h>
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 5a57163..784e854 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -175,6 +175,11 @@
     // is now set to 1, to prevent any batching at this level. Since we now do
     // batching through doze, that is a much better mechanism.
     optional int32 min_ready_jobs_count = 7;
+    // Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
+    optional int32 min_ready_non_active_jobs_count = 29;
+    // Don't batch a non-ACTIVE job if it's been delayed due to force batching attempts for
+    // at least this amount of time.
+    optional int64 max_non_active_job_batch_delay_ms = 30;
     // This is the job execution factor that is considered to be heavy use of
     // the system.
     optional double heavy_use_factor = 8;
@@ -307,6 +312,8 @@
 
     // In this time after screen turns on, we increase job concurrency.
     optional int32 screen_off_job_concurrency_increase_delay_ms = 28;
+
+    // Next tag: 31
 }
 
 // Next tag: 4
@@ -938,7 +945,15 @@
 
     optional int64 internal_flags = 24;
 
-    // Next tag: 28
+    // Amount of time since this job was first deferred due to standby bucketing policy. Will be
+    // 0 if this job was never deferred.
+    optional int64 time_since_first_deferral_ms = 28;
+
+    // Amount of time since JobScheduler first tried to force batch this job. Will be 0 if there
+    // was no attempt.
+    optional int64 time_since_first_force_batch_attempt_ms = 29;
+
+    // Next tag: 30
 }
 
 // Dump from com.android.server.job.JobConcurrencyManager.
diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
index 4b0ed65..95da532 100644
--- a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
@@ -90,7 +90,7 @@
         private boolean mAllow3rdPartyOnInternal = true;
 
         public MockedApplicationPackageManager() {
-            super(null, null);
+            super(null, null, null);
         }
 
         public void setForceAllowOnExternal(boolean forceAllowOnExternal) {
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index c8150b1..365e97d 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -16,7 +16,6 @@
 
 package android.content.pm;
 
-import android.content.Intent;
 import android.content.res.Resources;
 import android.os.FileUtils;
 import android.os.Parcel;
@@ -190,36 +189,6 @@
         assertEquals(0, cache.getPersistentServicesSize(u1));
     }
 
-    /**
-     * Check that an optimization to skip a call to PackageManager handles an invalidated cache.
-     *
-     * We added an optimization in generateServicesMap to only query PackageManager for packages
-     * that have been changed, because if a package is unchanged, we have already cached the
-     * services info for it, so we can save a query to PackageManager (and save some memory).
-     * However, if invalidateCache was called, we cannot optimize, and must do a full query.
-     * The initial optimization was buggy because it failed to check for an invalidated cache, and
-     * only scanned the changed packages, given in the ACTION_PACKAGE_CHANGED intent (b/122912184).
-     */
-    public void testParseServiceInfoOptimizationHandlesInvalidatedCache() {
-        TestServicesCache cache = new TestServicesCache();
-        cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
-        cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
-        assertEquals(2, cache.getAllServicesSize(U0));
-
-        // simulate the client of the cache invalidating it
-        cache.invalidateCache(U0);
-
-        // there should be 0 services (userServices.services == null ) at this point, but we don't
-        // call getAllServicesSize since that would force a full scan of packages,
-        // instead we trigger a package change in a package that is in the list of services
-        Intent intent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
-        intent.putExtra(Intent.EXTRA_UID, UID1);
-        cache.handlePackageEvent(intent, U0);
-
-        // check that the optimization does a full query and caches both services
-        assertEquals(2, cache.getAllServicesSize(U0));
-    }
-
     private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
             TestServiceType type, int uid) {
         final ComponentInfo info = new ComponentInfo();
@@ -297,11 +266,6 @@
                 map = new HashMap<>();
                 mServices.put(userId, map);
             }
-            // in actual cases, resolveInfo should always have a serviceInfo, since we specifically
-            // query for intent services
-            resolveInfo.serviceInfo = new android.content.pm.ServiceInfo();
-            resolveInfo.serviceInfo.applicationInfo =
-                new ApplicationInfo(serviceInfo.componentInfo.applicationInfo);
             map.put(resolveInfo, serviceInfo);
         }
 
@@ -340,11 +304,6 @@
         public void onUserRemoved(int userId) {
             super.onUserRemoved(userId);
         }
-
-        @Override
-        public void handlePackageEvent(Intent intent, int userId) {
-            super.handlePackageEvent(intent, userId);
-        }
     }
 
     static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index bfead1a..bf948b1 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -165,6 +165,9 @@
         "pipeline/skia/SkiaRecordingCanvas.cpp",
         "pipeline/skia/RenderNodeDrawable.cpp",
         "pipeline/skia/ReorderBarrierDrawables.cpp",
+        "renderthread/Frame.cpp",
+        "renderthread/RenderTask.cpp",
+        "renderthread/TimeLord.cpp",
         "hwui/AnimatedImageDrawable.cpp",
         "hwui/AnimatedImageThread.cpp",
         "hwui/Bitmap.cpp",
@@ -226,10 +229,7 @@
                 "renderthread/VulkanManager.cpp",
                 "renderthread/VulkanSurface.cpp",
                 "renderthread/RenderProxy.cpp",
-                "renderthread/RenderTask.cpp",
                 "renderthread/RenderThread.cpp",
-                "renderthread/TimeLord.cpp",
-                "renderthread/Frame.cpp",
                 "service/GraphicsStatsService.cpp",
                 "surfacetexture/EGLConsumer.cpp",
                 "surfacetexture/ImageConsumer.cpp",
diff --git a/libs/hwui/RootRenderNode.cpp b/libs/hwui/RootRenderNode.cpp
index d8c1b57..2480192 100644
--- a/libs/hwui/RootRenderNode.cpp
+++ b/libs/hwui/RootRenderNode.cpp
@@ -16,6 +16,8 @@
 
 #include "RootRenderNode.h"
 
+#include <utils/Looper.h>
+
 namespace android::uirenderer {
 
 class FinishAndInvokeListener : public MessageHandler {
diff --git a/libs/hwui/RootRenderNode.h b/libs/hwui/RootRenderNode.h
index ea10921..5c830e0 100644
--- a/libs/hwui/RootRenderNode.h
+++ b/libs/hwui/RootRenderNode.h
@@ -16,13 +16,12 @@
 
 #pragma once
 
-#include <utils/Looper.h>
-
 #include <set>
 #include <vector>
 
 #include "AnimationContext.h"
 #include "Animator.h"
+#include <IContextFactory.h>
 #include "PropertyValuesAnimatorSet.h"
 #include "RenderNode.h"
 
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 3dd1d44..e50428b 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -30,8 +30,6 @@
 #include "utils/TimeUtils.h"
 #include "utils/TraceUtils.h"
 
-#include <ui/GraphicBuffer.h>
-
 namespace android {
 namespace uirenderer {
 namespace renderthread {
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 1bdfe87..f5330b4 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -48,6 +48,7 @@
 import android.util.Log;
 
 import com.android.internal.location.ProviderProperties;
+import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -2298,18 +2299,19 @@
     }
 
     /**
-     * Sends additional commands to a location provider.
-     * Can be used to support provider specific extensions to the Location Manager API
+     * Sends additional commands to a location provider. Can be used to support provider specific
+     * extensions to the Location Manager API.
      *
      * @param provider name of the location provider.
-     * @param command name of the command to send to the provider.
-     * @param extras optional arguments for the command (or null).
-     * The provider may optionally fill the extras Bundle with results from the command.
-     *
-     * @return true if the command succeeds.
+     * @param command  name of the command to send to the provider.
+     * @param extras   optional arguments for the command (or null).
+     * @return true always
      */
     public boolean sendExtraCommand(
             @NonNull String provider, @NonNull String command, @Nullable Bundle extras) {
+        Preconditions.checkArgument(provider != null, "invalid null provider");
+        Preconditions.checkArgument(command != null, "invalid null command");
+
         try {
             return mService.sendExtraCommand(provider, command, extras);
         } catch (RemoteException e) {
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 4ac6d35..16ba63b 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -25,6 +25,7 @@
 import android.content.SharedPreferences;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
+import android.media.ExifInterface;
 import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.RemoteException;
@@ -47,6 +48,7 @@
 import com.google.android.collect.Sets;
 
 import java.io.File;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -799,6 +801,54 @@
     }
 
     @VisibleForNative
+    private boolean getThumbnailInfo(int handle, long[] outLongs) {
+        MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+        if (obj == null) {
+            return false;
+        }
+
+        String path = obj.getPath().toString();
+        switch (obj.getFormat()) {
+            case MtpConstants.FORMAT_HEIF:
+            case MtpConstants.FORMAT_EXIF_JPEG:
+            case MtpConstants.FORMAT_JFIF:
+                try {
+                    ExifInterface exif = new ExifInterface(path);
+                    long[] thumbOffsetAndSize = exif.getThumbnailRange();
+                    outLongs[0] = thumbOffsetAndSize != null ? thumbOffsetAndSize[1] : 0;
+                    outLongs[1] = exif.getAttributeInt(ExifInterface.TAG_PIXEL_X_DIMENSION, 0);
+                    outLongs[2] = exif.getAttributeInt(ExifInterface.TAG_PIXEL_Y_DIMENSION, 0);
+                    return true;
+                } catch (IOException e) {
+                    // ignore and fall through
+                }
+        }
+        return false;
+    }
+
+    @VisibleForNative
+    private byte[] getThumbnailData(int handle) {
+        MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+        if (obj == null) {
+            return null;
+        }
+
+        String path = obj.getPath().toString();
+        switch (obj.getFormat()) {
+            case MtpConstants.FORMAT_HEIF:
+            case MtpConstants.FORMAT_EXIF_JPEG:
+            case MtpConstants.FORMAT_JFIF:
+                try {
+                    ExifInterface exif = new ExifInterface(path);
+                    return exif.getThumbnail();
+                } catch (IOException e) {
+                    // ignore and fall through
+                }
+        }
+        return null;
+    }
+
+    @VisibleForNative
     private int beginDeleteObject(int handle) {
         MtpStorageManager.MtpObject obj = mManager.getObject(handle);
         if (obj == null) {
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 10f76b0..c49b1e4 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -47,7 +47,6 @@
         "libstagefright_foundation",
         "libcamera_client",
         "libmtp",
-        "libexif",
         "libpiex",
         "libprocessgroup",
         "libandroidfw",
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 1f89d86..0a3b47b 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -30,13 +30,6 @@
 #include "src/piex_types.h"
 #include "src/piex.h"
 
-extern "C" {
-#include "libexif/exif-content.h"
-#include "libexif/exif-data.h"
-#include "libexif/exif-tag.h"
-#include "libexif/exif-utils.h"
-}
-
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
 #include <jni.h>
@@ -70,6 +63,8 @@
 static jmethodID method_getObjectPropertyList;
 static jmethodID method_getObjectInfo;
 static jmethodID method_getObjectFilePath;
+static jmethodID method_getThumbnailInfo;
+static jmethodID method_getThumbnailData;
 static jmethodID method_beginDeleteObject;
 static jmethodID method_endDeleteObject;
 static jmethodID method_beginMoveObject;
@@ -219,7 +214,7 @@
         return; // Already threw.
     }
     mIntBuffer = (jintArray)env->NewGlobalRef(intArray);
-    jlongArray longArray = env->NewLongArray(2);
+    jlongArray longArray = env->NewLongArray(3);
     if (!longArray) {
         return; // Already threw.
     }
@@ -780,57 +775,6 @@
     return result;
 }
 
-static void foreachentry(ExifEntry *entry, void* /* user */) {
-    char buf[1024];
-    ALOGI("entry %x, format %d, size %d: %s",
-            entry->tag, entry->format, entry->size, exif_entry_get_value(entry, buf, sizeof(buf)));
-}
-
-static void foreachcontent(ExifContent *content, void *user) {
-    ALOGI("content %d", exif_content_get_ifd(content));
-    exif_content_foreach_entry(content, foreachentry, user);
-}
-
-static long getLongFromExifEntry(ExifEntry *e) {
-    ExifByteOrder o = exif_data_get_byte_order(e->parent->parent);
-    return exif_get_long(e->data, o);
-}
-
-static ExifData *getExifFromExtractor(const char *path) {
-    std::unique_ptr<uint8_t[]> exifBuf;
-    ExifData *exifdata = NULL;
-
-    FILE *fp = fopen (path, "rb");
-    if (!fp) {
-        ALOGE("failed to open file");
-        return NULL;
-    }
-
-    sp<NuMediaExtractor> extractor = new NuMediaExtractor();
-    fseek(fp, 0L, SEEK_END);
-    if (extractor->setDataSource(fileno(fp), 0, ftell(fp)) != OK) {
-        ALOGE("failed to setDataSource");
-        fclose(fp);
-        return NULL;
-    }
-
-    off64_t offset;
-    size_t size;
-    if (extractor->getExifOffsetSize(&offset, &size) != OK) {
-        fclose(fp);
-        return NULL;
-    }
-
-    exifBuf.reset(new uint8_t[size]);
-    fseek(fp, offset, SEEK_SET);
-    if (fread(exifBuf.get(), 1, size, fp) == size) {
-        exifdata = exif_data_new_from_data(exifBuf.get(), size);
-    }
-
-    fclose(fp);
-    return exifdata;
-}
-
 MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
                                              MtpObjectInfo& info) {
     MtpStringBuffer path;
@@ -877,26 +821,23 @@
         case MTP_FORMAT_EXIF_JPEG:
         case MTP_FORMAT_HEIF:
         case MTP_FORMAT_JFIF: {
-            ExifData *exifdata;
-            if (info.mFormat == MTP_FORMAT_HEIF) {
-                exifdata = getExifFromExtractor(path);
-            } else {
-                exifdata = exif_data_new_from_file(path);
-            }
-            if (exifdata) {
-                if ((false)) {
-                    exif_data_foreach_content(exifdata, foreachcontent, NULL);
-                }
+            env = AndroidRuntime::getJNIEnv();
+            if (env->CallBooleanMethod(
+                    mDatabase, method_getThumbnailInfo, (jint)handle, mLongBuffer)) {
 
-                ExifEntry *w = exif_content_get_entry(
-                        exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_X_DIMENSION);
-                ExifEntry *h = exif_content_get_entry(
-                        exifdata->ifd[EXIF_IFD_EXIF], EXIF_TAG_PIXEL_Y_DIMENSION);
-                info.mThumbCompressedSize = exifdata->data ? exifdata->size : 0;
-                info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
-                info.mImagePixWidth = w ? getLongFromExifEntry(w) : 0;
-                info.mImagePixHeight = h ? getLongFromExifEntry(h) : 0;
-                exif_data_unref(exifdata);
+                jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
+                jlong size = longValues[0];
+                jlong w = longValues[1];
+                jlong h = longValues[2];
+                if (size > 0 && size <= UINT32_MAX &&
+                        w > 0 && w <= UINT32_MAX &&
+                        h > 0 && h <= UINT32_MAX) {
+                    info.mThumbCompressedSize = size;
+                    info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
+                    info.mImagePixWidth = w;
+                    info.mImagePixHeight = h;
+                }
+                env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
             }
             break;
         }
@@ -941,22 +882,19 @@
             case MTP_FORMAT_EXIF_JPEG:
             case MTP_FORMAT_HEIF:
             case MTP_FORMAT_JFIF: {
-                ExifData *exifdata;
-                if (format == MTP_FORMAT_HEIF) {
-                    exifdata = getExifFromExtractor(path);
-                } else {
-                    exifdata = exif_data_new_from_file(path);
+                JNIEnv* env = AndroidRuntime::getJNIEnv();
+                jbyteArray thumbData = (jbyteArray) env->CallObjectMethod(
+                        mDatabase, method_getThumbnailData, (jint)handle);
+                if (thumbData == NULL) {
+                    return nullptr;
                 }
-                if (exifdata) {
-                    if (exifdata->data) {
-                        result = malloc(exifdata->size);
-                        if (result) {
-                            memcpy(result, exifdata->data, exifdata->size);
-                            outThumbSize = exifdata->size;
-                        }
-                    }
-                    exif_data_unref(exifdata);
+                jsize thumbSize = env->GetArrayLength(thumbData);
+                result = malloc(thumbSize);
+                if (result) {
+                    env->GetByteArrayRegion(thumbData, 0, thumbSize, (jbyte*)result);
+                    outThumbSize = thumbSize;
                 }
+                env->DeleteLocalRef(thumbData);
                 break;
             }
 
@@ -1389,6 +1327,8 @@
     GET_METHOD_ID(getObjectPropertyList, clazz, "(IIIII)Landroid/mtp/MtpPropertyList;");
     GET_METHOD_ID(getObjectInfo, clazz, "(I[I[C[J)Z");
     GET_METHOD_ID(getObjectFilePath, clazz, "(I[C[J)I");
+    GET_METHOD_ID(getThumbnailInfo, clazz, "(I[J)Z");
+    GET_METHOD_ID(getThumbnailData, clazz, "(I)[B");
     GET_METHOD_ID(beginDeleteObject, clazz, "(I)I");
     GET_METHOD_ID(endDeleteObject, clazz, "(IZ)V");
     GET_METHOD_ID(beginMoveObject, clazz, "(III)I");
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
index 44f0494..0f4cf21 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -118,6 +118,8 @@
                 options.setLaunchDisplayId(mContext.getDisplayId());
                 intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
                 mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
+                mContext.sendBroadcastAsUser(
+                        new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.CURRENT);
             });
 
             if (longPressIntentString != null) {
@@ -128,6 +130,8 @@
                     options.setLaunchDisplayId(mContext.getDisplayId());
                     mContext.startActivityAsUser(longPressIntent, options.toBundle(),
                             UserHandle.CURRENT);
+                    mContext.sendBroadcastAsUser(
+                            new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.CURRENT);
                     return true;
                 });
             }
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
index 1bc1e6f..9de4ef5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -95,6 +95,9 @@
                     try {
                         if (mBroadcastIntent) {
                             mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+                            mContext.sendBroadcastAsUser(
+                                    new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
+                                    UserHandle.CURRENT);
                             return;
                         }
                         ActivityOptions options = ActivityOptions.makeBasic();
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 881f4b1..c11e1a0 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -34,6 +34,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.permission.IPermissionManager;
 import android.util.Log;
 
 /**
@@ -45,12 +46,14 @@
 
     private static final String DOWNLOADS_AUTHORITY = "downloads";
     private IPackageManager mIPackageManager;
+    private IPermissionManager mIPermissionManager;
     private boolean mAbortInstall = false;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mIPackageManager = AppGlobals.getPackageManager();
+        mIPermissionManager = AppGlobals.getPermissionManager();
         Intent intent = getIntent();
         String callingPackage = getCallingPackage();
 
@@ -137,7 +140,7 @@
 
     private boolean declaresAppOpPermission(int uid, String permission) {
         try {
-            final String[] packages = mIPackageManager.getAppOpPermissionPackages(permission);
+            final String[] packages = mIPermissionManager.getAppOpPermissionPackages(permission);
             if (packages == null) {
                 return false;
             }
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index afe6d9c..f4970d0 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -382,22 +382,16 @@
     <!-- The width of the panel that holds the quick settings. -->
     <dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
 
-    <dimen name="volume_dialog_panel_transparent_padding_right">8dp</dimen>
+    <dimen name="volume_dialog_panel_transparent_padding_right">4dp</dimen>
 
     <dimen name="volume_dialog_panel_transparent_padding">20dp</dimen>
 
     <dimen name="volume_dialog_stream_padding">8dp</dimen>
 
-    <!-- the amount the volume panel should be offset at the end from the view next to it (or
-    the screen edge, in portrait-->
-    <dimen name="volume_dialog_base_margin">8dp</dimen>
-
     <dimen name="volume_dialog_panel_width">64dp</dimen>
 
     <dimen name="volume_dialog_slider_height">116dp</dimen>
 
-    <dimen name="volume_dialog_row_height">252dp</dimen>
-
     <dimen name="volume_dialog_ringer_size">64dp</dimen>
 
     <dimen name="volume_dialog_ringer_icon_padding">20dp</dimen>
@@ -414,8 +408,6 @@
 
     <dimen name="volume_dialog_row_margin_bottom">8dp</dimen>
 
-    <dimen name="volume_dialog_settings_icon_size">16dp</dimen>
-
     <dimen name="volume_dialog_elevation">9dp</dimen>
 
     <dimen name="volume_tool_tip_right_margin">76dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 776cd4d..22e3edb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -627,7 +627,7 @@
         if (mOverviewProxyService.isEnabled()) {
             // Force disable recents when not in legacy mode
             disableRecent |= !QuickStepContract.isLegacyMode(mNavBarMode);
-            if (pinningActive) {
+            if (pinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) {
                 disableBack = disableHome = false;
             }
         } else if (pinningActive) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 48a9fb6..e61a67c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -20,7 +20,6 @@
 import android.app.Dialog;
 import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.UserHandle;
@@ -34,10 +33,13 @@
 
 /**
  * Base class for dialogs that should appear over panels and keyguard.
+ * The SystemUIDialog registers a listener for the screen off / close system dialogs broadcast,
+ * and dismisses itself when it receives the broadcast.
  */
 public class SystemUIDialog extends AlertDialog {
 
     private final Context mContext;
+    private final DismissReceiver mDismissReceiver;
 
     public SystemUIDialog(Context context) {
         this(context, R.style.Theme_SystemUI_Dialog);
@@ -52,7 +54,19 @@
         attrs.setTitle(getClass().getSimpleName());
         getWindow().setAttributes(attrs);
 
-        registerDismissListener(this);
+        mDismissReceiver = new DismissReceiver(this);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mDismissReceiver.register();
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mDismissReceiver.unregister();
     }
 
     public void setShowForAllUsers(boolean show) {
@@ -100,12 +114,22 @@
         return dialog;
     }
 
+    /**
+     * Registers a listener that dismisses the given dialog when it receives
+     * the screen off / close system dialogs broadcast.
+     * <p>
+     * <strong>Note:</strong> Don't call dialog.setOnDismissListener() after
+     * calling this because it causes a leak of BroadcastReceiver.
+     *
+     * @param dialog The dialog to be associated with the listener.
+     */
     public static void registerDismissListener(Dialog dialog) {
         DismissReceiver dismissReceiver = new DismissReceiver(dialog);
+        dialog.setOnDismissListener(d -> dismissReceiver.unregister());
         dismissReceiver.register();
     }
 
-    private static class DismissReceiver extends BroadcastReceiver implements OnDismissListener {
+    private static class DismissReceiver extends BroadcastReceiver {
         private static final IntentFilter INTENT_FILTER = new IntentFilter();
         static {
             INTENT_FILTER.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -125,16 +149,16 @@
             mRegistered = true;
         }
 
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            mDialog.dismiss();
-        }
-
-        @Override
-        public void onDismiss(DialogInterface dialog) {
+        void unregister() {
             if (mRegistered) {
                 mDialog.getContext().unregisterReceiver(this);
                 mRegistered = false;
             }
         }
-    }}
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mDialog.dismiss();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 78e845a..40c3d9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -294,6 +294,18 @@
     }
 
     @Override
+    public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
+            int state, int bluetoothProfile) {
+        if (DEBUG) {
+            Log.d(TAG, "ProfileConnectionStateChanged=" + cachedDevice.getAddress() + " "
+                    + stateToString(state) + " profileId=" + bluetoothProfile);
+        }
+        mCachedState.remove(cachedDevice);
+        updateConnected();
+        mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
+    }
+
+    @Override
     public void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
         if (DEBUG) {
             Log.d(TAG, "ACLConnectionStateChanged=" + cachedDevice.getAddress() + " "
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 69d2e31..3f3e1e3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -243,8 +243,11 @@
         pw.print("  mState: "); pw.println(mState.toString(4));
         pw.print("  mShowDndTile: "); pw.println(mShowDndTile);
         pw.print("  mHasVibrator: "); pw.println(mHasVibrator);
-        pw.print("  mRemoteStreams: "); pw.println(mMediaSessionsCallbacksW.mRemoteStreams
-                .values());
+        synchronized (mMediaSessionsCallbacksW.mRemoteStreams) {
+            pw.print("  mRemoteStreams: ");
+            pw.println(mMediaSessionsCallbacksW.mRemoteStreams
+                    .values());
+        }
         pw.print("  mShowA11yStream: "); pw.println(mShowA11yStream);
         pw.println();
         mMediaSessions.dump(pw);
@@ -1075,7 +1078,10 @@
         @Override
         public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) {
             addStream(token, "onRemoteUpdate");
-            final int stream = mRemoteStreams.get(token);
+            int stream = 0;
+            synchronized (mRemoteStreams) {
+                 stream = mRemoteStreams.get(token);
+            }
             boolean changed = mState.states.indexOfKey(stream) < 0;
             final StreamState ss = streamStateW(stream);
             ss.dynamic = true;
@@ -1100,7 +1106,10 @@
         @Override
         public void onRemoteVolumeChanged(Token token, int flags) {
             addStream(token, "onRemoteVolumeChanged");
-            final int stream = mRemoteStreams.get(token);
+            int stream = 0;
+            synchronized (mRemoteStreams) {
+                stream = mRemoteStreams.get(token);
+            }
             final boolean showUI = shouldShowUI(flags);
             boolean changed = updateActiveStreamW(stream);
             if (showUI) {
@@ -1116,12 +1125,15 @@
 
         @Override
         public void onRemoteRemoved(Token token) {
-            if (!mRemoteStreams.containsKey(token)) {
-                if (D.BUG) Log.d(TAG, "onRemoteRemoved: stream doesn't exist, "
-                        + "aborting remote removed for token:" +  token.toString());
-                return;
+            int stream = 0;
+            synchronized (mRemoteStreams) {
+                if (!mRemoteStreams.containsKey(token)) {
+                    if (D.BUG) Log.d(TAG, "onRemoteRemoved: stream doesn't exist, "
+                            + "aborting remote removed for token:" +  token.toString());
+                    return;
+                }
+                stream = mRemoteStreams.get(token);
             }
-            final int stream = mRemoteStreams.get(token);
             mState.states.remove(stream);
             if (mState.activeStream == stream) {
                 updateActiveStreamW(-1);
@@ -1139,20 +1151,24 @@
         }
 
         private Token findToken(int stream) {
-            for (Map.Entry<Token, Integer> entry : mRemoteStreams.entrySet()) {
-                if (entry.getValue().equals(stream)) {
-                    return entry.getKey();
+            synchronized (mRemoteStreams) {
+                for (Map.Entry<Token, Integer> entry : mRemoteStreams.entrySet()) {
+                    if (entry.getValue().equals(stream)) {
+                        return entry.getKey();
+                    }
                 }
             }
             return null;
         }
 
         private void addStream(Token token, String triggeringMethod) {
-            if (!mRemoteStreams.containsKey(token)) {
-                mRemoteStreams.put(token, mNextStream);
-                if (D.BUG) Log.d(TAG, triggeringMethod + ": added stream " +  mNextStream
-                        + " from token + "+ token.toString());
-                mNextStream++;
+            synchronized (mRemoteStreams) {
+                if (!mRemoteStreams.containsKey(token)) {
+                    mRemoteStreams.put(token, mNextStream);
+                    if (D.BUG) Log.d(TAG, triggeringMethod + ": added stream " + mNextStream
+                            + " from token + " + token.toString());
+                    mNextStream++;
+                }
             }
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index ca64823..a9a1392 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -17,9 +17,11 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -52,13 +54,18 @@
 
     @Test
     public void testRegisterReceiver() {
+        final ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
         final ArgumentCaptor<IntentFilter> intentFilterCaptor =
                 ArgumentCaptor.forClass(IntentFilter.class);
 
-        verify(mContextSpy).registerReceiverAsUser(any(), any(),
+        mDialog.show();
+        verify(mContextSpy).registerReceiverAsUser(broadcastReceiverCaptor.capture(), any(),
                 intentFilterCaptor.capture(), any(), any());
 
         assertTrue(intentFilterCaptor.getValue().hasAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
-    }
 
+        mDialog.dismiss();
+        verify(mContextSpy).unregisterReceiver(eq(broadcastReceiverCaptor.getValue()));
+    }
 }
diff --git a/packages/SystemUI/tools/lint/baseline.xml b/packages/SystemUI/tools/lint/baseline.xml
index 8c43222..096a639 100644
--- a/packages/SystemUI/tools/lint/baseline.xml
+++ b/packages/SystemUI/tools/lint/baseline.xml
@@ -2685,39 +2685,6 @@
 
     <issue
         id="UnusedResources"
-        message="The resource `R.dimen.volume_dialog_base_margin` appears to be unused"
-        errorLine1="    &lt;dimen name=&quot;volume_dialog_base_margin&quot;>8dp&lt;/dimen>"
-        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="res/values/dimens.xml"
-            line="308"
-            column="12"/>
-    </issue>
-
-    <issue
-        id="UnusedResources"
-        message="The resource `R.dimen.volume_dialog_row_height` appears to be unused"
-        errorLine1="    &lt;dimen name=&quot;volume_dialog_row_height&quot;>252dp&lt;/dimen>"
-        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="res/values/dimens.xml"
-            line="314"
-            column="12"/>
-    </issue>
-
-    <issue
-        id="UnusedResources"
-        message="The resource `R.dimen.volume_dialog_settings_icon_size` appears to be unused"
-        errorLine1="    &lt;dimen name=&quot;volume_dialog_settings_icon_size&quot;>16dp&lt;/dimen>"
-        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="res/values/dimens.xml"
-            line="328"
-            column="12"/>
-    </issue>
-
-    <issue
-        id="UnusedResources"
         message="The resource `R.dimen.carrier_label_height` appears to be unused"
         errorLine1="    &lt;dimen name=&quot;carrier_label_height&quot;>24dp&lt;/dimen>"
         errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index a3f26ad..3ebe702 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1098,12 +1098,9 @@
         @GuardedBy("mLock")
         public void sendExtraCommandLocked(String command, Bundle extras) {
             if (mProvider != null) {
-                long identity = Binder.clearCallingIdentity();
-                try {
-                    mProvider.sendExtraCommand(command, extras);
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
-                }
+                // intentionally do not clear binder identity so that providers can evaluate who
+                // is sending the extra command
+                mProvider.sendExtraCommand(command, extras);
             }
         }
 
diff --git a/services/core/java/com/android/server/NewNetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
similarity index 98%
rename from services/core/java/com/android/server/NewNetworkTimeUpdateService.java
rename to services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
index d21741a..b0b45f4 100644
--- a/services/core/java/com/android/server/NewNetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
@@ -55,7 +55,7 @@
  * available.
  * </p>
  */
-public class NewNetworkTimeUpdateService extends Binder implements NetworkTimeUpdateService {
+public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeUpdateService {
 
     private static final String TAG = "NetworkTimeUpdateService";
     private static final boolean DBG = false;
@@ -98,7 +98,7 @@
     // connection to happen.
     private int mTryAgainCounter;
 
-    public NewNetworkTimeUpdateService(Context context) {
+    public NetworkTimeUpdateServiceImpl(Context context) {
         mContext = context;
         mTime = NtpTrustedTime.getInstance(context);
         mAlarmManager = mContext.getSystemService(AlarmManager.class);
diff --git a/services/core/java/com/android/server/OldNetworkTimeUpdateService.java b/services/core/java/com/android/server/OldNetworkTimeUpdateService.java
deleted file mode 100644
index 068b83d..0000000
--- a/services/core/java/com/android/server/OldNetworkTimeUpdateService.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.NtpTrustedTime;
-import android.util.TimeUtils;
-
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.DumpUtils;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Monitors the network time and updates the system time if it is out of sync
- * and there hasn't been any NITZ update from the carrier recently.
- * If looking up the network time fails for some reason, it tries a few times with a short
- * interval and then resets to checking on longer intervals.
- * <p>
- * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
- * available.
- * </p>
- */
-public class OldNetworkTimeUpdateService extends Binder implements NetworkTimeUpdateService {
-
-    private static final String TAG = "NetworkTimeUpdateService";
-    private static final boolean DBG = false;
-
-    private static final int EVENT_AUTO_TIME_CHANGED = 1;
-    private static final int EVENT_POLL_NETWORK_TIME = 2;
-    private static final int EVENT_NETWORK_CHANGED = 3;
-
-    private static final String ACTION_POLL =
-            "com.android.server.NetworkTimeUpdateService.action.POLL";
-
-    private static final int POLL_REQUEST = 0;
-
-    private static final long NOT_SET = -1;
-    private long mNitzTimeSetTime = NOT_SET;
-    private Network mDefaultNetwork = null;
-
-    private final Context mContext;
-    private final NtpTrustedTime mTime;
-    private final AlarmManager mAlarmManager;
-    private final ConnectivityManager mCM;
-    private final PendingIntent mPendingPollIntent;
-    private final PowerManager.WakeLock mWakeLock;
-
-    // NTP lookup is done on this thread and handler
-    private Handler mHandler;
-    private SettingsObserver mSettingsObserver;
-    private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
-
-    // Normal polling frequency
-    private final long mPollingIntervalMs;
-    // Try-again polling interval, in case the network request failed
-    private final long mPollingIntervalShorterMs;
-    // Number of times to try again
-    private final int mTryAgainTimesMax;
-    // If the time difference is greater than this threshold, then update the time.
-    private final int mTimeErrorThresholdMs;
-    // Keeps track of how many quick attempts were made to fetch NTP time.
-    // During bootup, the network may not have been up yet, or it's taking time for the
-    // connection to happen.
-    private int mTryAgainCounter;
-
-    public OldNetworkTimeUpdateService(Context context) {
-        mContext = context;
-        mTime = NtpTrustedTime.getInstance(context);
-        mAlarmManager = mContext.getSystemService(AlarmManager.class);
-        mCM = mContext.getSystemService(ConnectivityManager.class);
-
-        Intent pollIntent = new Intent(ACTION_POLL, null);
-        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
-
-        mPollingIntervalMs = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_ntpPollingInterval);
-        mPollingIntervalShorterMs = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_ntpPollingIntervalShorter);
-        mTryAgainTimesMax = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_ntpRetry);
-        mTimeErrorThresholdMs = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_ntpThreshold);
-
-        mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
-                PowerManager.PARTIAL_WAKE_LOCK, TAG);
-    }
-
-    @Override
-    public void systemRunning() {
-        registerForTelephonyIntents();
-        registerForAlarms();
-
-        HandlerThread thread = new HandlerThread(TAG);
-        thread.start();
-        mHandler = new MyHandler(thread.getLooper());
-        mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
-        mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
-
-        mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
-        mSettingsObserver.observe(mContext);
-    }
-
-    private void registerForTelephonyIntents() {
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
-        mContext.registerReceiver(mNitzReceiver, intentFilter);
-    }
-
-    private void registerForAlarms() {
-        mContext.registerReceiver(
-            new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
-                }
-            }, new IntentFilter(ACTION_POLL));
-    }
-
-    private void onPollNetworkTime(int event) {
-        // If Automatic time is not set, don't bother. Similarly, if we don't
-        // have any default network, don't bother.
-        if (mDefaultNetwork == null) return;
-        mWakeLock.acquire();
-        try {
-            onPollNetworkTimeUnderWakeLock(event);
-        } finally {
-            mWakeLock.release();
-        }
-    }
-
-    private void onPollNetworkTimeUnderWakeLock(int event) {
-        // Force an NTP fix when outdated
-        if (mTime.getCacheAge() >= mPollingIntervalMs) {
-            if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
-            mTime.forceRefresh();
-        }
-
-        if (mTime.getCacheAge() < mPollingIntervalMs) {
-            // Obtained fresh fix; schedule next normal update
-            resetAlarm(mPollingIntervalMs);
-            if (isAutomaticTimeRequested()) {
-                updateSystemClock(event);
-            }
-
-        } else {
-            // No fresh fix; schedule retry
-            mTryAgainCounter++;
-            if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
-                resetAlarm(mPollingIntervalShorterMs);
-            } else {
-                // Try much later
-                mTryAgainCounter = 0;
-                resetAlarm(mPollingIntervalMs);
-            }
-        }
-    }
-
-    private long getNitzAge() {
-        if (mNitzTimeSetTime == NOT_SET) {
-            return Long.MAX_VALUE;
-        } else {
-            return SystemClock.elapsedRealtime() - mNitzTimeSetTime;
-        }
-    }
-
-    /**
-     * Consider updating system clock based on current NTP fix, if requested by
-     * user, significant enough delta, and we don't have a recent NITZ.
-     */
-    private void updateSystemClock(int event) {
-        final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
-        if (!forceUpdate) {
-            if (getNitzAge() < mPollingIntervalMs) {
-                if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
-                return;
-            }
-
-            final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
-            if (skew < mTimeErrorThresholdMs) {
-                if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
-                return;
-            }
-        }
-
-        SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
-    }
-
-    /**
-     * Cancel old alarm and starts a new one for the specified interval.
-     *
-     * @param interval when to trigger the alarm, starting from now.
-     */
-    private void resetAlarm(long interval) {
-        mAlarmManager.cancel(mPendingPollIntent);
-        long now = SystemClock.elapsedRealtime();
-        long next = now + interval;
-        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
-    }
-
-    /**
-     * Checks if the user prefers to automatically set the time.
-     */
-    private boolean isAutomaticTimeRequested() {
-        return Settings.Global.getInt(
-                mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
-    }
-
-    /** Receiver for Nitz time events */
-    private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (DBG) Log.d(TAG, "Received " + action);
-            if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
-                mNitzTimeSetTime = SystemClock.elapsedRealtime();
-            }
-        }
-    };
-
-    /** Handler to do the network accesses on */
-    private class MyHandler extends Handler {
-
-        public MyHandler(Looper l) {
-            super(l);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case EVENT_AUTO_TIME_CHANGED:
-                case EVENT_POLL_NETWORK_TIME:
-                case EVENT_NETWORK_CHANGED:
-                    onPollNetworkTime(msg.what);
-                    break;
-            }
-        }
-    }
-
-    private class NetworkTimeUpdateCallback extends NetworkCallback {
-        @Override
-        public void onAvailable(Network network) {
-            Log.d(TAG, String.format("New default network %s; checking time.", network));
-            mDefaultNetwork = network;
-            // Running on mHandler so invoke directly.
-            onPollNetworkTime(EVENT_NETWORK_CHANGED);
-        }
-
-        @Override
-        public void onLost(Network network) {
-            if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
-        }
-    }
-
-    /** Observer to watch for changes to the AUTO_TIME setting */
-    private static class SettingsObserver extends ContentObserver {
-
-        private int mMsg;
-        private Handler mHandler;
-
-        SettingsObserver(Handler handler, int msg) {
-            super(handler);
-            mHandler = handler;
-            mMsg = msg;
-        }
-
-        void observe(Context context) {
-            ContentResolver resolver = context.getContentResolver();
-            resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
-                    false, this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            mHandler.obtainMessage(mMsg).sendToTarget();
-        }
-    }
-
-    @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-        pw.print("PollingIntervalMs: ");
-        TimeUtils.formatDuration(mPollingIntervalMs, pw);
-        pw.print("\nPollingIntervalShorterMs: ");
-        TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
-        pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
-        pw.print("TimeErrorThresholdMs: ");
-        TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
-        pw.println("\nTryAgainCounter: " + mTryAgainCounter);
-        pw.println("NTP cache age: " + mTime.getCacheAge());
-        pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
-        pw.println();
-    }
-}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f0243c3..8cd675c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -271,8 +271,8 @@
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.provider.DeviceConfig;
-import android.provider.Settings;
 import android.provider.DeviceConfig.Properties;
+import android.provider.Settings;
 import android.server.ServerProtoEnums;
 import android.sysprop.VoldProperties;
 import android.text.TextUtils;
@@ -2890,6 +2890,7 @@
             if (mIsolatedAppBindArgs == null) {
                 mIsolatedAppBindArgs = new ArrayMap<>(1);
                 addServiceToMap(mIsolatedAppBindArgs, "package");
+                addServiceToMap(mIsolatedAppBindArgs, "permissionmgr");
             }
             return mIsolatedAppBindArgs;
         }
@@ -2901,6 +2902,7 @@
             // IMPORTANT: Before adding services here, make sure ephemeral apps can access them too.
             // Enable the check in ApplicationThread.bindApplication() to make sure.
             addServiceToMap(mAppBindArgs, "package");
+            addServiceToMap(mAppBindArgs, "permissionmgr");
             addServiceToMap(mAppBindArgs, Context.WINDOW_SERVICE);
             addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE);
             addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE);
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 746c250..56208a95 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -30,7 +30,6 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ResolveInfo;
@@ -44,6 +43,7 @@
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.permission.IPermissionManager;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseIntArray;
@@ -921,7 +921,7 @@
         if (perms == null) {
             return false;
         }
-        IPackageManager pm = AppGlobals.getPackageManager();
+        IPermissionManager pm = AppGlobals.getPermissionManager();
         for (int i = perms.length-1; i >= 0; i--) {
             try {
                 PermissionInfo pi = pm.getPermissionInfo(perms[i], "android", 0);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d3cafb7..8ef5b07 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1917,8 +1917,8 @@
                         if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
                             final long ident = Binder.clearCallingIdentity();
                             try {
-                                mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
-                                mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
+                                mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, true);
+                                mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, false);
                             } finally {
                                 Binder.restoreCallingIdentity(ident);
                             }
diff --git a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
index 7ec890a..6d26934 100644
--- a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
@@ -91,8 +91,8 @@
     }
 
     private void handleInitiateArcTimeout() {
+        // Keep ARC status as what it is when TV does not respond to ARC init
         HdmiLogger.debug("handleInitiateArcTimeout");
-        audioSystem().setArcStatus(false);
         finish();
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
index eb7c0cd..dedf2e2 100644
--- a/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
@@ -76,6 +76,11 @@
         sendCommand(HdmiCecMessageBuilder.buildTerminateArc(getSourceAddress(), Constants.ADDR_TV),
             result -> {
                 if (result != SendMessageResult.SUCCESS) {
+                    // If the physical connection is already off or TV does not handle
+                    // Terminate ARC, turn off ARC internally.
+                    if (result == SendMessageResult.NACK) {
+                        audioSystem().setArcStatus(false);
+                    }
                     HdmiLogger.debug("Terminate ARC was not successfully sent.");
                     finish();
                 }
diff --git a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
index 15ec486..46b4f48 100644
--- a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
+++ b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
@@ -64,7 +64,7 @@
         }
     }
 
-    private void removeActiveSource() {
+    protected void removeActiveSource() {
         // Uses iterator to remove elements while looping through the list.
         for (Iterator<HdmiCecMessage> iter = mBuffer.iterator(); iter.hasNext(); ) {
             HdmiCecMessage message = iter.next();
diff --git a/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java b/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java
index 45ff261..dc53688 100644
--- a/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java
+++ b/services/core/java/com/android/server/hdmi/DetectTvSystemAudioModeSupportAction.java
@@ -53,9 +53,14 @@
                 return false;
             }
             if (HdmiUtils.getAbortFeatureOpcode(cmd) == Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE) {
-                if(HdmiUtils.getAbortReason(cmd) == Constants.ABORT_NOT_IN_CORRECT_MODE) {
+                if (HdmiUtils.getAbortReason(cmd) == Constants.ABORT_NOT_IN_CORRECT_MODE) {
                     mActionTimer.clearTimerMessage();
                     mState = STATE_WAITING_FOR_SET_SAM;
+                    // Outgoing User Control Press commands, when in 'Press and Hold' mode, should
+                    // be this much apart from the adjacent one so as not to place unnecessarily
+                    // heavy load on the CEC line. We also wait this much time to send the next
+                    // retry of the System Audio Mode support detection message.
+                    addTimer(mState, HdmiConfig.IRT_MS);
                 } else {
                     finishAction(false);
                 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 30ec2ab..a358707 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -876,7 +876,7 @@
     }
 
     ActiveSource getActiveSource() {
-        return mService.getActiveSource();
+        return mService.getLocalActiveSource();
     }
 
     void setActiveSource(ActiveSource newActive) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 0e47b3f..548553d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -110,6 +110,12 @@
     // device id is used as key of container.
     private final SparseArray<HdmiDeviceInfo> mDeviceInfos = new SparseArray<>();
 
+    // Message buffer used to buffer selected messages to process later. <Active Source>
+    // from a source device, for instance, needs to be buffered if the device is not
+    // discovered yet. The buffered commands are taken out and when they are ready to
+    // handle.
+    private final DelayedMessageBuffer mDelayedMessageBuffer = new DelayedMessageBuffer(this);
+
     protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
         super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
         mRoutingControlFeatureEnabled =
@@ -151,6 +157,9 @@
             }
             mPortIdToTvInputs.put(info.getPortId(), inputId);
             mTvInputsToDeviceInfo.put(inputId, info);
+            if (info.isCecDevice()) {
+                processDelayedActiveSource(info.getLogicalAddress());
+            }
         }
     }
 
@@ -167,6 +176,15 @@
         }
     }
 
+    @Override
+    @ServiceThreadOnly
+    protected boolean isInputReady(int portId) {
+        assertRunOnServiceThread();
+        String tvInputId = mPortIdToTvInputs.get(portId);
+        HdmiDeviceInfo info = mTvInputsToDeviceInfo.get(tvInputId);
+        return info != null;
+    }
+
     /**
      * Called when a device is newly added or a new device is detected or
      * an existing device is updated.
@@ -233,6 +251,7 @@
     @VisibleForTesting
     protected HdmiDeviceInfo addDeviceInfo(HdmiDeviceInfo deviceInfo) {
         assertRunOnServiceThread();
+        mService.checkLogicalAddressConflictAndReallocate(deviceInfo.getLogicalAddress());
         HdmiDeviceInfo oldDeviceInfo = getCecDeviceInfo(deviceInfo.getLogicalAddress());
         if (oldDeviceInfo != null) {
             removeDeviceInfo(deviceInfo.getId());
@@ -304,6 +323,15 @@
         }
         if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
             mCecMessageCache.flushAll();
+            if (!connected) {
+                if (isSystemAudioActivated()) {
+                    mTvSystemAudioModeSupport = null;
+                    checkSupportAndSetSystemAudioMode(false);
+                }
+                if (isArcEnabled()) {
+                    setArcStatus(false);
+                }
+            }
         } else if (!connected && mPortIdToTvInputs.get(portId) != null) {
             String tvInputId = mPortIdToTvInputs.get(portId);
             HdmiDeviceInfo info = mTvInputsToDeviceInfo.get(tvInputId);
@@ -406,6 +434,32 @@
                 Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, String.valueOf(addr));
     }
 
+    @ServiceThreadOnly
+    void processDelayedActiveSource(int address) {
+        assertRunOnServiceThread();
+        mDelayedMessageBuffer.processActiveSource(address);
+    }
+
+    @Override
+    @ServiceThreadOnly
+    protected boolean handleActiveSource(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        int logicalAddress = message.getSource();
+        int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+        HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress);
+        if (info == null) {
+            HdmiLogger.debug("Device info %X not found; buffering the command", logicalAddress);
+            mDelayedMessageBuffer.add(message);
+        } else if (!isInputReady(info.getPortId())){
+            HdmiLogger.debug("Input not ready for device: %X; buffering the command", info.getId());
+            mDelayedMessageBuffer.add(message);
+        } else {
+            mDelayedMessageBuffer.removeActiveSource();
+            super.handleActiveSource(message);
+        }
+        return true;
+    }
+
     @Override
     @ServiceThreadOnly
     protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
@@ -791,7 +845,31 @@
             int sourcePhysicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
             if (sourcePhysicalAddress != getActiveSource().physicalAddress) {
                 // If the Active Source recorded by the current device is not synced up with TV,
-                // TODO(amyjojo): update Active Source internally
+                // update the Active Source internally.
+                if (sourcePhysicalAddress == mService.getPhysicalAddress()) {
+                    // If the active path is the current device itself, update with local info
+                    if (mService.playback() != null) {
+                        setActiveSource(mService.playback().mAddress, sourcePhysicalAddress);
+                    } else {
+                        setActiveSource(mAddress, sourcePhysicalAddress);
+                    }
+                } else {
+                    // If it's not the current device, look for the device info from the list
+                    for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
+                        if (info.getPhysicalAddress() == sourcePhysicalAddress) {
+                            setActiveSource(info.getLogicalAddress(), info.getPhysicalAddress());
+                            break;
+                        }
+                    }
+                }
+                // If the Active path from TV's System Audio Mode request does not belong to any
+                // device in the local device list, record the new Active physicalAddress with an
+                // unregistered logical address first. Then query the Active Source again.
+                if (sourcePhysicalAddress != getActiveSource().physicalAddress) {
+                    setActiveSource(Constants.ADDR_UNREGISTERED, sourcePhysicalAddress);
+                    mService.sendCecCommand(
+                        HdmiCecMessageBuilder.buildRequestActiveSource(mAddress));
+                }
             }
             switchInputOnReceivingNewActivePath(sourcePhysicalAddress);
         }
@@ -930,6 +1008,19 @@
                 mService.announceSystemAudioModeChange(newSystemAudioMode);
             }
         }
+        // Since ARC is independent from System Audio Mode control, when the TV requests
+        // System Audio Mode off, it does not need to terminate ARC at the same time.
+        // When the current audio device is using ARC as a TV input and disables muting,
+        // it needs to automatically switch to the previous active input source when System
+        // Audio Mode is off even without terminating the ARC. This can stop the current
+        // audio device from playing audio when system audio mode is off.
+        if (mArcIntentUsed
+            && !mService.readBooleanSystemProperty(
+                    Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, true)
+            && !newSystemAudioMode
+            && getLocalActivePort() == Constants.CEC_SWITCH_ARC) {
+            routeToInputFromPortId(getRoutingPort());
+        }
         // Init arc whenever System Audio Mode is on
         // Since some TVs don't request ARC on with System Audio Mode on request
         if (SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)
@@ -1150,10 +1241,12 @@
         }
         // Wake up if the current device if ready to route.
         mService.wakeUp();
+        if (getLocalActivePort() == portId) {
+            HdmiLogger.debug("Not switching to the same port " + portId);
+            return;
+        }
         // Switch to HOME if the current active port is not HOME yet
-        if (portId == Constants.CEC_SWITCH_HOME
-            && mService.isPlaybackDevice()
-            && getLocalActivePort() != Constants.CEC_SWITCH_HOME) {
+        if (portId == Constants.CEC_SWITCH_HOME && mService.isPlaybackDevice()) {
             switchToHomeTvInput();
         } else if (portId == Constants.CEC_SWITCH_ARC) {
             switchToTvInput(SystemProperties.get(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT));
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 7eb669a..413e7a0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -163,10 +163,17 @@
     @ServiceThreadOnly
     protected void onStandby(boolean initiatedByCec, int standbyAction) {
         assertRunOnServiceThread();
+        if (!mService.isControlEnabled()) {
+            return;
+        }
+        if (mIsActiveSource) {
+            mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
+                mAddress, mService.getPhysicalAddress()));
+        }
         // Invalidate the internal active source record when goes to standby
         // This set will also update mIsActiveSource
         mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS);
-        if (!mService.isControlEnabled() || initiatedByCec || !mAutoTvOff) {
+        if (initiatedByCec || !mAutoTvOff) {
             return;
         }
         switch (standbyAction) {
@@ -349,11 +356,6 @@
         super.disableDevice(initiatedByCec, callback);
 
         assertRunOnServiceThread();
-        if (!initiatedByCec && mIsActiveSource && mService.isControlEnabled()) {
-            mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
-                    mAddress, mService.getPhysicalAddress()));
-        }
-        setIsActiveSource(false);
         checkIfPendingActionsCleared();
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 9aaf33c..4d5dc6a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -56,6 +56,7 @@
 import android.media.tv.TvInputManager.TvInputCallback;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -1172,6 +1173,29 @@
         return mCecController.getLocalDeviceList();
     }
 
+    /**
+     * Check if a logical address is conflict with the current device's. Reallocate the logical
+     * address of the current device if there is conflict.
+     *
+     * Android HDMI CEC 1.4 is handling logical address allocation in the framework side. This could
+     * introduce delay between the logical address allocation and notifying the driver that the
+     * address is occupied. Adding this check to avoid such case.
+     *
+     * @param logicalAddress logical address of the remote device that might have the same logical
+     * address as the current device.
+     */
+    protected void checkLogicalAddressConflictAndReallocate(int logicalAddress) {
+        for (HdmiCecLocalDevice device : getAllLocalDevices()) {
+            if (device.getDeviceInfo().getLogicalAddress() == logicalAddress) {
+                HdmiLogger.debug("allocate logical address for " + device.getDeviceInfo());
+                ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
+                localDevices.add(device);
+                allocateLogicalAddress(localDevices, HdmiControlService.INITIATED_BY_HOTPLUG);
+                return;
+            }
+        }
+    }
+
     Object getServiceLock() {
         return mLock;
     }
@@ -1461,25 +1485,34 @@
                         return playback().getDeviceInfo();
                     }
                     // Otherwise get the active source and look for it from the device list
-                    ActiveSource activeSource = mActiveSource;
-                    // If the active source is not set yet, return null
-                    if (!activeSource.isValid()) {
+                    ActiveSource activeSource = getLocalActiveSource();
+                    // If the physical address is not set yet, return null
+                    if (activeSource.physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS) {
                         return null;
                     }
                     if (audioSystem() != null) {
                         HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
                         for (HdmiDeviceInfo info : audioSystem.getSafeCecDevicesLocked()) {
-                            if (info.getLogicalAddress() == activeSource.logicalAddress) {
+                            if (info.getPhysicalAddress() == activeSource.physicalAddress) {
                                 return info;
                             }
                         }
                     }
                     // If the device info is not in the list yet, return a device info with minimum
                     // information from mActiveSource.
-                    return new HdmiDeviceInfo(activeSource.logicalAddress,
-                        activeSource.physicalAddress, pathToPortId(activeSource.physicalAddress),
-                        HdmiUtils.getTypeFromAddress(activeSource.logicalAddress), 0,
-                        HdmiUtils.getDefaultDeviceName(activeSource.logicalAddress));
+                    // If the Active Source has unregistered logical address, return with an
+                    // HdmiDeviceInfo built from physical address information only.
+                    return HdmiUtils.isValidAddress(activeSource.logicalAddress)
+                        ?
+                        new HdmiDeviceInfo(activeSource.logicalAddress,
+                            activeSource.physicalAddress,
+                            pathToPortId(activeSource.physicalAddress),
+                            HdmiUtils.getTypeFromAddress(activeSource.logicalAddress), 0,
+                            HdmiUtils.getDefaultDeviceName(activeSource.logicalAddress))
+                        :
+                            new HdmiDeviceInfo(activeSource.physicalAddress,
+                                pathToPortId(activeSource.physicalAddress));
+
                 }
                 return null;
             }
@@ -1630,6 +1663,8 @@
         @Override
         public void oneTouchPlay(final IHdmiControlCallback callback) {
             enforceAccessPermission();
+            int pid = Binder.getCallingPid();
+            Slog.d(TAG, "Proccess pid: " + pid + " is calling oneTouchPlay.");
             runOnServiceThread(new Runnable() {
                 @Override
                 public void run() {
@@ -2808,7 +2843,7 @@
         setLastInputForMhl(Constants.INVALID_PORT_ID);
     }
 
-    ActiveSource getActiveSource() {
+    ActiveSource getLocalActiveSource() {
         synchronized (mLock) {
             return mActiveSource;
         }
@@ -2819,6 +2854,21 @@
             mActiveSource.logicalAddress = logicalAddress;
             mActiveSource.physicalAddress = physicalAddress;
         }
+        // If the current device is a source device, check if the current Active Source matches
+        // the local device info. Set mIsActiveSource of the local device accordingly.
+        for (HdmiCecLocalDevice device : getAllLocalDevices()) {
+            // mIsActiveSource only exists in source device, ignore this setting if the current
+            // device is not an HdmiCecLocalDeviceSource.
+            if (!(device instanceof HdmiCecLocalDeviceSource)) {
+                continue;
+            }
+            if (logicalAddress == device.getDeviceInfo().getLogicalAddress()
+                && physicalAddress == getPhysicalAddress()) {
+                ((HdmiCecLocalDeviceSource) device).setIsActiveSource(true);
+            } else {
+                ((HdmiCecLocalDeviceSource) device).setIsActiveSource(false);
+            }
+        }
     }
 
     // This method should only be called when the device can be the active source
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 8181900..e44e902 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -187,6 +187,9 @@
     private final StorageController mStorageController;
     /** Need directly for sending uid state changes */
     private final DeviceIdleJobsController mDeviceIdleJobsController;
+    /** Needed to get remaining quota time. */
+    private final QuotaController mQuotaController;
+
     /** Need directly for receiving thermal events */
     private IThermalService mThermalService;
     /** Thermal constraint. */
@@ -443,6 +446,10 @@
         private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
         private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
         private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
+        private static final String KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT =
+                "min_ready_non_active_jobs_count";
+        private static final String KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS =
+                "max_non_active_job_batch_delay_ms";
         private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
         private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
 
@@ -473,6 +480,8 @@
         private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
         private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
         private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
+        private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
+        private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
         private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
         private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
         private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
@@ -524,6 +533,18 @@
          * a much better mechanism.
          */
         int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
+
+        /**
+         * Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
+         */
+        int MIN_READY_NON_ACTIVE_JOBS_COUNT = DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT;
+
+        /**
+         * Don't batch a non-ACTIVE job if it's been delayed due to force batching attempts for
+         * at least this amount of time.
+         */
+        long MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS;
+
         /**
          * This is the job execution factor that is considered to be heavy use of the system.
          */
@@ -657,6 +678,12 @@
                     DEFAULT_MIN_CONTENT_COUNT);
             MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
                     DEFAULT_MIN_READY_JOBS_COUNT);
+            MIN_READY_NON_ACTIVE_JOBS_COUNT = mParser.getInt(
+                    KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT,
+                    DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT);
+            MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = mParser.getLong(
+                    KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS,
+                    DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS);
             HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
                     DEFAULT_HEAVY_USE_FACTOR);
             MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
@@ -707,6 +734,10 @@
             pw.printPair(KEY_MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT).println();
             pw.printPair(KEY_MIN_CONTENT_COUNT, MIN_CONTENT_COUNT).println();
             pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println();
+            pw.printPair(KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT,
+                    MIN_READY_NON_ACTIVE_JOBS_COUNT).println();
+            pw.printPair(KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS,
+                    MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS).println();
             pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
             pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
 
@@ -749,6 +780,10 @@
             proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
             proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
             proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
+            proto.write(ConstantsProto.MIN_READY_NON_ACTIVE_JOBS_COUNT,
+                    MIN_READY_NON_ACTIVE_JOBS_COUNT);
+            proto.write(ConstantsProto.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS,
+                    MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS);
             proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
             proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
 
@@ -1338,7 +1373,8 @@
         mControllers.add(new ContentObserverController(this));
         mDeviceIdleJobsController = new DeviceIdleJobsController(this);
         mControllers.add(mDeviceIdleJobsController);
-        mControllers.add(new QuotaController(this));
+        mQuotaController = new QuotaController(this);
+        mControllers.add(mQuotaController);
 
         // If the job store determined that it can't yet reschedule persisted jobs,
         // we need to start watching the clock.
@@ -1494,16 +1530,16 @@
     }
 
     /**
-     * Called when we want to remove a JobStatus object that we've finished executing. Returns the
-     * object removed.
+     * Called when we want to remove a JobStatus object that we've finished executing.
+     * @return true if the job was removed.
      */
     private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
-            boolean writeBack) {
+            boolean removeFromPersisted) {
         // Deal with any remaining work items in the old job.
         jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
 
         // Remove from store as well as controllers.
-        final boolean removed = mJobs.remove(jobStatus, writeBack);
+        final boolean removed = mJobs.remove(jobStatus, removeFromPersisted);
         if (removed && mReadyToRock) {
             for (int i=0; i<mControllers.size(); i++) {
                 StateController controller = mControllers.get(i);
@@ -1985,7 +2021,7 @@
     }
 
     final class ReadyJobQueueFunctor implements Consumer<JobStatus> {
-        ArrayList<JobStatus> newReadyJobs;
+        final ArrayList<JobStatus> newReadyJobs = new ArrayList<>();
 
         @Override
         public void accept(JobStatus job) {
@@ -1993,9 +2029,6 @@
                 if (DEBUG) {
                     Slog.d(TAG, "    queued " + job.toShortString());
                 }
-                if (newReadyJobs == null) {
-                    newReadyJobs = new ArrayList<JobStatus>();
-                }
                 newReadyJobs.add(job);
             } else {
                 evaluateControllerStatesLocked(job);
@@ -2003,14 +2036,13 @@
         }
 
         public void postProcess() {
-            if (newReadyJobs != null) {
-                noteJobsPending(newReadyJobs);
-                mPendingJobs.addAll(newReadyJobs);
-                if (mPendingJobs.size() > 1) {
-                    mPendingJobs.sort(mEnqueueTimeComparator);
-                }
+            noteJobsPending(newReadyJobs);
+            mPendingJobs.addAll(newReadyJobs);
+            if (mPendingJobs.size() > 1) {
+                mPendingJobs.sort(mEnqueueTimeComparator);
             }
-            newReadyJobs = null;
+
+            newReadyJobs.clear();
         }
     }
     private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
@@ -2027,7 +2059,9 @@
         int backoffCount;
         int connectivityCount;
         int contentCount;
-        List<JobStatus> runnableJobs;
+        int forceBatchedCount;
+        int unbatchedCount;
+        final List<JobStatus> runnableJobs = new ArrayList<>();
 
         public MaybeReadyJobQueueFunctor() {
             reset();
@@ -2047,29 +2081,39 @@
                     }
                 } catch (RemoteException e) {
                 }
-                if (job.getNumFailures() > 0) {
-                    backoffCount++;
-                }
-                if (job.hasIdleConstraint()) {
-                    idleCount++;
-                }
-                if (job.hasConnectivityConstraint()) {
-                    connectivityCount++;
-                }
-                if (job.hasChargingConstraint()) {
-                    chargingCount++;
-                }
-                if (job.hasBatteryNotLowConstraint()) {
-                    batteryNotLowCount++;
-                }
-                if (job.hasStorageNotLowConstraint()) {
-                    storageNotLowCount++;
-                }
-                if (job.hasContentTriggerConstraint()) {
-                    contentCount++;
-                }
-                if (runnableJobs == null) {
-                    runnableJobs = new ArrayList<>();
+                if (mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
+                        && job.getStandbyBucket() != ACTIVE_INDEX
+                        && (job.getFirstForceBatchedTimeElapsed() == 0
+                        || sElapsedRealtimeClock.millis() - job.getFirstForceBatchedTimeElapsed()
+                                < mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS)) {
+                    // Force batching non-ACTIVE jobs. Don't include them in the other counts.
+                    forceBatchedCount++;
+                    if (job.getFirstForceBatchedTimeElapsed() == 0) {
+                        job.setFirstForceBatchedTimeElapsed(sElapsedRealtimeClock.millis());
+                    }
+                } else {
+                    unbatchedCount++;
+                    if (job.getNumFailures() > 0) {
+                        backoffCount++;
+                    }
+                    if (job.hasIdleConstraint()) {
+                        idleCount++;
+                    }
+                    if (job.hasConnectivityConstraint()) {
+                        connectivityCount++;
+                    }
+                    if (job.hasChargingConstraint()) {
+                        chargingCount++;
+                    }
+                    if (job.hasBatteryNotLowConstraint()) {
+                        batteryNotLowCount++;
+                    }
+                    if (job.hasStorageNotLowConstraint()) {
+                        storageNotLowCount++;
+                    }
+                    if (job.hasContentTriggerConstraint()) {
+                        contentCount++;
+                    }
                 }
                 runnableJobs.add(job);
             } else {
@@ -2085,8 +2129,9 @@
                     batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
                     storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
                     contentCount >= mConstants.MIN_CONTENT_COUNT ||
-                    (runnableJobs != null
-                            && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
+                    forceBatchedCount >= mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT ||
+                    (unbatchedCount > 0 && (unbatchedCount + forceBatchedCount)
+                            >= mConstants.MIN_READY_JOBS_COUNT)) {
                 if (DEBUG) {
                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
                 }
@@ -2105,7 +2150,8 @@
             reset();
         }
 
-        private void reset() {
+        @VisibleForTesting
+        void reset() {
             chargingCount = 0;
             idleCount =  0;
             backoffCount = 0;
@@ -2113,7 +2159,9 @@
             batteryNotLowCount = 0;
             storageNotLowCount = 0;
             contentCount = 0;
-            runnableJobs = null;
+            forceBatchedCount = 0;
+            unbatchedCount = 0;
+            runnableJobs.clear();
         }
     }
     private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
@@ -2222,7 +2270,8 @@
      *      - The job's standby bucket has come due to be runnable.
      *      - The component is enabled and runnable.
      */
-    private boolean isReadyToBeExecutedLocked(JobStatus job) {
+    @VisibleForTesting
+    boolean isReadyToBeExecutedLocked(JobStatus job) {
         final boolean jobReady = job.isReady();
 
         if (DEBUG) {
@@ -2354,7 +2403,8 @@
         return !appIsBad;
     }
 
-    private void evaluateControllerStatesLocked(final JobStatus job) {
+    @VisibleForTesting
+    void evaluateControllerStatesLocked(final JobStatus job) {
         for (int c = mControllers.size() - 1; c >= 0; --c) {
             final StateController sc = mControllers.get(c);
             sc.evaluateStateLocked(job);
@@ -2397,6 +2447,17 @@
         return isComponentUsable(job);
     }
 
+    /** Returns the maximum amount of time this job could run for. */
+    public long getMaxJobExecutionTimeMs(JobStatus job) {
+        synchronized (mLock) {
+            if (mConstants.USE_HEARTBEATS) {
+                return JobServiceContext.EXECUTING_TIMESLICE_MILLIS;
+            }
+            return Math.min(mQuotaController.getMaxJobExecutionTimeMsLocked(job),
+                    JobServiceContext.EXECUTING_TIMESLICE_MILLIS);
+        }
+    }
+
     /**
      * Reconcile jobs in the pending queue against available execution contexts.
      * A controller can force a job into the pending queue even if it's already running, but
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 4ef37a2..d69faf3 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -235,10 +235,11 @@
 
     /**
      * Remove the provided job. Will also delete the job if it was persisted.
-     * @param writeBack If true, the job will be deleted (if it was persisted) immediately.
+     * @param removeFromPersisted If true, the job will be removed from the persisted job list
+     *                            immediately (if it was persisted).
      * @return Whether or not the job existed to be removed.
      */
-    public boolean remove(JobStatus jobStatus, boolean writeBack) {
+    public boolean remove(JobStatus jobStatus, boolean removeFromPersisted) {
         boolean removed = mJobSet.remove(jobStatus);
         if (!removed) {
             if (DEBUG) {
@@ -246,7 +247,7 @@
             }
             return false;
         }
-        if (writeBack && jobStatus.isPersisted()) {
+        if (removeFromPersisted && jobStatus.isPersisted()) {
             maybeWriteStatusToDiskAsync();
         }
         return removed;
@@ -344,6 +345,19 @@
         new ReadJobMapFromDiskRunnable(jobSet, rtcGood).run();
     }
 
+    /** Write persisted JobStore state to disk synchronously. Should only be used for testing. */
+    @VisibleForTesting
+    public void writeStatusToDiskForTesting() {
+        synchronized (mWriteScheduleLock) {
+            if (mWriteScheduled) {
+                throw new IllegalStateException("An asynchronous write is already scheduled.");
+            }
+
+            mWriteScheduled = mWriteInProgress = true;
+            mWriteRunnable.run();
+        }
+    }
+
     /**
      * Wait for any pending write to the persistent store to clear
      * @param maxWaitMillis Maximum time from present to wait
@@ -1049,7 +1063,9 @@
         }
     }
 
-    static final class JobSet {
+    /** Set of all tracked jobs. */
+    @VisibleForTesting
+    public static final class JobSet {
         @VisibleForTesting // Key is the getUid() originator of the jobs in each sheaf
         final SparseArray<ArraySet<JobStatus>> mJobs;
 
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 490e87f..f8cf6ae 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -88,8 +88,11 @@
     @GuardedBy("mLock")
     private final ArraySet<Network> mAvailableNetworks = new ArraySet<>();
 
+    private boolean mUseQuotaLimit;
+
     private static final int MSG_DATA_SAVER_TOGGLED = 0;
     private static final int MSG_UID_RULES_CHANGES = 1;
+    private static final int MSG_REEVALUATE_JOBS = 2;
 
     private final Handler mHandler;
 
@@ -107,6 +110,8 @@
         mConnManager.registerNetworkCallback(request, mNetworkCallback);
 
         mNetPolicyManager.registerListener(mNetPolicyListener);
+
+        mUseQuotaLimit = !mConstants.USE_HEARTBEATS;
     }
 
     @GuardedBy("mLock")
@@ -149,6 +154,10 @@
             }
             mRequestedWhitelistJobs.clear();
         }
+        if (mUseQuotaLimit == mConstants.USE_HEARTBEATS) {
+            mUseQuotaLimit = !mConstants.USE_HEARTBEATS;
+            mHandler.obtainMessage(MSG_REEVALUATE_JOBS).sendToTarget();
+        }
     }
 
     /**
@@ -318,9 +327,12 @@
      * connection, it would take 10.4 minutes, and has no chance of succeeding
      * before the job times out, so we'd be insane to try running it.
      */
-    @SuppressWarnings("unused")
-    private static boolean isInsane(JobStatus jobStatus, Network network,
+    private boolean isInsane(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
+        final long maxJobExecutionTimeMs = mUseQuotaLimit
+                ? mService.getMaxJobExecutionTimeMs(jobStatus)
+                : JobServiceContext.EXECUTING_TIMESLICE_MILLIS;
+
         final long downloadBytes = jobStatus.getEstimatedNetworkDownloadBytes();
         if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
             final long bandwidth = capabilities.getLinkDownstreamBandwidthKbps();
@@ -329,10 +341,11 @@
                 // Divide by 8 to convert bits to bytes.
                 final long estimatedMillis = ((downloadBytes * DateUtils.SECOND_IN_MILLIS)
                         / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
-                if (estimatedMillis > JobServiceContext.EXECUTING_TIMESLICE_MILLIS) {
+                if (estimatedMillis > maxJobExecutionTimeMs) {
                     // If we'd never finish before the timeout, we'd be insane!
                     Slog.w(TAG, "Estimated " + downloadBytes + " download bytes over " + bandwidth
-                            + " kbps network would take " + estimatedMillis + "ms; that's insane!");
+                            + " kbps network would take " + estimatedMillis + "ms and job has "
+                            + maxJobExecutionTimeMs + "ms to run; that's insane!");
                     return true;
                 }
             }
@@ -346,10 +359,11 @@
                 // Divide by 8 to convert bits to bytes.
                 final long estimatedMillis = ((uploadBytes * DateUtils.SECOND_IN_MILLIS)
                         / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
-                if (estimatedMillis > JobServiceContext.EXECUTING_TIMESLICE_MILLIS) {
+                if (estimatedMillis > maxJobExecutionTimeMs) {
                     // If we'd never finish before the timeout, we'd be insane!
                     Slog.w(TAG, "Estimated " + uploadBytes + " upload bytes over " + bandwidth
-                            + " kbps network would take " + estimatedMillis + "ms; that's insane!");
+                            + " kbps network would take " + estimatedMillis + "ms and job has "
+                            + maxJobExecutionTimeMs + "ms to run; that's insane!");
                     return true;
                 }
             }
@@ -358,7 +372,6 @@
         return false;
     }
 
-    @SuppressWarnings("unused")
     private static boolean isCongestionDelayed(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
         // If network is congested, and job is less than 50% through the
@@ -370,14 +383,12 @@
         }
     }
 
-    @SuppressWarnings("unused")
     private static boolean isStrictSatisfied(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
         return jobStatus.getJob().getRequiredNetwork().networkCapabilities
                 .satisfiedByNetworkCapabilities(capabilities);
     }
 
-    @SuppressWarnings("unused")
     private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
         // Only consider doing this for prefetching jobs
@@ -398,7 +409,7 @@
     }
 
     @VisibleForTesting
-    static boolean isSatisfied(JobStatus jobStatus, Network network,
+    boolean isSatisfied(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
         // Zeroth, we gotta have a network to think about being satisfied
         if (network == null || capabilities == null) return false;
@@ -594,6 +605,9 @@
                     case MSG_UID_RULES_CHANGES:
                         updateTrackedJobs(msg.arg1, null);
                         break;
+                    case MSG_REEVALUATE_JOBS:
+                        updateTrackedJobs(-1, null);
+                        break;
                 }
             }
         }
@@ -603,6 +617,8 @@
     @Override
     public void dumpControllerStateLocked(IndentingPrintWriter pw,
             Predicate<JobStatus> predicate) {
+        pw.print("mUseQuotaLimit="); pw.println(mUseQuotaLimit);
+
         if (mRequestedWhitelistJobs.size() > 0) {
             pw.print("Requested standby exceptions:");
             for (int i = 0; i < mRequestedWhitelistJobs.size(); i++) {
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index a628b17..d73c253 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -24,7 +24,6 @@
 import android.app.job.JobWorkItem;
 import android.content.ClipData;
 import android.content.ComponentName;
-import android.content.pm.PackageManagerInternal;
 import android.net.Network;
 import android.net.Uri;
 import android.os.RemoteException;
@@ -131,7 +130,6 @@
      * that underly Sync Manager operation.
      */
     final int callingUid;
-    final int targetSdkVersion;
     final String batteryName;
 
     /**
@@ -188,6 +186,9 @@
      */
     private long whenStandbyDeferred;
 
+    /** The first time this job was force batched. */
+    private long mFirstForceBatchedTimeElapsed;
+
     // Constraints.
     final int requiredConstraints;
     private final int mRequiredConstraintsOfInterest;
@@ -344,7 +345,6 @@
      * @param job The actual requested parameters for the job
      * @param callingUid Identity of the app that is scheduling the job.  This may not be the
      *     app in which the job is implemented; such as with sync jobs.
-     * @param targetSdkVersion The targetSdkVersion of the app in which the job will run.
      * @param sourcePackageName The package name of the app in which the job will run.
      * @param sourceUserId The user in which the job will run
      * @param standbyBucket The standby bucket that the source package is currently assigned to,
@@ -363,13 +363,12 @@
      * @param lastFailedRunTime When did we last run this job only to have it stop incomplete?
      * @param internalFlags Non-API property flags about this job
      */
-    private JobStatus(JobInfo job, int callingUid, int targetSdkVersion, String sourcePackageName,
+    private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
             int sourceUserId, int standbyBucket, long heartbeat, String tag, int numFailures,
             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
             long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags) {
         this.job = job;
         this.callingUid = callingUid;
-        this.targetSdkVersion = targetSdkVersion;
         this.standbyBucket = standbyBucket;
         this.baseHeartbeat = heartbeat;
 
@@ -439,7 +438,7 @@
     /** Copy constructor: used specifically when cloning JobStatus objects for persistence,
      *   so we preserve RTC window bounds if the source object has them. */
     public JobStatus(JobStatus jobStatus) {
-        this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.targetSdkVersion,
+        this(jobStatus.getJob(), jobStatus.getUid(),
                 jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
                 jobStatus.getStandbyBucket(), jobStatus.getBaseHeartbeat(),
                 jobStatus.getSourceTag(), jobStatus.getNumFailures(),
@@ -468,7 +467,7 @@
             long lastSuccessfulRunTime, long lastFailedRunTime,
             Pair<Long, Long> persistedExecutionTimesUTC,
             int innerFlags) {
-        this(job, callingUid, resolveTargetSdkVersion(job), sourcePkgName, sourceUserId,
+        this(job, callingUid, sourcePkgName, sourceUserId,
                 standbyBucket, baseHeartbeat,
                 sourceTag, 0,
                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
@@ -491,7 +490,7 @@
             long newEarliestRuntimeElapsedMillis,
             long newLatestRuntimeElapsedMillis, int backoffAttempt,
             long lastSuccessfulRunTime, long lastFailedRunTime) {
-        this(rescheduling.job, rescheduling.getUid(), resolveTargetSdkVersion(rescheduling.job),
+        this(rescheduling.job, rescheduling.getUid(),
                 rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(),
                 rescheduling.getStandbyBucket(), newBaseHeartbeat,
                 rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
@@ -533,7 +532,7 @@
         long currentHeartbeat = js != null
                 ? js.baseHeartbeatForApp(jobPackage, sourceUserId, standbyBucket)
                 : 0;
-        return new JobStatus(job, callingUid, resolveTargetSdkVersion(job), sourcePkg, sourceUserId,
+        return new JobStatus(job, callingUid, sourcePkg, sourceUserId,
                 standbyBucket, currentHeartbeat, tag, 0,
                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
                 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
@@ -681,10 +680,6 @@
         return job.getId();
     }
 
-    public int getTargetSdkVersion() {
-        return targetSdkVersion;
-    }
-
     public void printUniqueId(PrintWriter pw) {
         UserHandle.formatUid(pw, callingUid);
         pw.print("/");
@@ -737,6 +732,18 @@
         whenStandbyDeferred = now;
     }
 
+    /**
+     * Returns the first time this job was force batched, in the elapsed realtime timebase. Will be
+     * 0 if this job was never force batched.
+     */
+    public long getFirstForceBatchedTimeElapsed() {
+        return mFirstForceBatchedTimeElapsed;
+    }
+
+    public void setFirstForceBatchedTimeElapsed(long now) {
+        mFirstForceBatchedTimeElapsed = now;
+    }
+
     public String getSourceTag() {
         return sourceTag;
     }
@@ -1441,11 +1448,6 @@
         }
     }
 
-    private static int resolveTargetSdkVersion(JobInfo job) {
-        return LocalServices.getService(PackageManagerInternal.class)
-                .getPackageTargetSdkVersion(job.getService().getPackageName());
-    }
-
     // Dumpsys infrastructure
     public void dump(PrintWriter pw, String prefix, boolean full, long elapsedRealtimeMillis) {
         pw.print(prefix); UserHandle.formatUid(pw, callingUid);
@@ -1638,6 +1640,12 @@
             TimeUtils.formatDuration(whenStandbyDeferred, elapsedRealtimeMillis, pw);
             pw.println();
         }
+        if (mFirstForceBatchedTimeElapsed != 0) {
+            pw.print(prefix);
+            pw.print("  Time since first force batch attempt: ");
+            TimeUtils.formatDuration(mFirstForceBatchedTimeElapsed, elapsedRealtimeMillis, pw);
+            pw.println();
+        }
         pw.print(prefix); pw.print("Enqueue time: ");
         TimeUtils.formatDuration(enqueueTime, elapsedRealtimeMillis, pw);
         pw.println();
@@ -1830,6 +1838,11 @@
 
         proto.write(JobStatusDumpProto.STANDBY_BUCKET, standbyBucket);
         proto.write(JobStatusDumpProto.ENQUEUE_DURATION_MS, elapsedRealtimeMillis - enqueueTime);
+        proto.write(JobStatusDumpProto.TIME_SINCE_FIRST_DEFERRAL_MS,
+                whenStandbyDeferred == 0 ? 0 : elapsedRealtimeMillis - whenStandbyDeferred);
+        proto.write(JobStatusDumpProto.TIME_SINCE_FIRST_FORCE_BATCH_ATTEMPT_MS,
+                mFirstForceBatchedTimeElapsed == 0
+                        ? 0 : elapsedRealtimeMillis - mFirstForceBatchedTimeElapsed);
         if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) {
             proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS, 0);
         } else {
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
index 18d193a..b8cfac4 100644
--- a/services/core/java/com/android/server/job/controllers/QuotaController.java
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -68,6 +68,7 @@
 import com.android.server.LocalServices;
 import com.android.server.job.ConstantsProto;
 import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobServiceContext;
 import com.android.server.job.StateControllerProto;
 
 import java.util.ArrayList;
@@ -737,6 +738,18 @@
         return mTopStartedJobs.contains(jobStatus);
     }
 
+    /** Returns the maximum amount of time this job could run for. */
+    public long getMaxJobExecutionTimeMsLocked(@NonNull final JobStatus jobStatus) {
+        // If quota is currently "free", then the job can run for the full amount of time.
+        if (mChargeTracker.isCharging()
+                || mInParole
+                || isTopStartedJobLocked(jobStatus)
+                || isUidInForeground(jobStatus.getSourceUid())) {
+            return JobServiceContext.EXECUTING_TIMESLICE_MILLIS;
+        }
+        return getRemainingExecutionTimeLocked(jobStatus);
+    }
+
     /**
      * Returns an appropriate standby bucket for the job, taking into account any standby
      * exemptions.
@@ -2278,8 +2291,10 @@
                     mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
                     changed = true;
                 }
-                long newQuotaBufferMs = Math.max(0,
-                        Math.min(5 * MINUTE_IN_MILLIS, IN_QUOTA_BUFFER_MS));
+                // Make sure quota buffer is non-negative, not greater than allowed time per period,
+                // and no more than 5 minutes.
+                long newQuotaBufferMs = Math.max(0, Math.min(mAllowedTimePerPeriodMs,
+                        Math.min(5 * MINUTE_IN_MILLIS, IN_QUOTA_BUFFER_MS)));
                 if (mQuotaBufferMs != newQuotaBufferMs) {
                     mQuotaBufferMs = newQuotaBufferMs;
                     mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index 0edd17b..8107e9f 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -142,6 +142,9 @@
         return 0;
     }
 
-    /** Sends a custom command to this provider. */
+    /**
+     * Sends a custom command to this provider. Called with the original binder identity of the
+     * caller.
+     */
     public abstract void sendExtraCommand(String command, Bundle extras);
 }
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index c312b76..8857618 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -182,7 +182,6 @@
     private static final int INJECT_NTP_TIME = 5;
     // PSDS stands for Predicted Satellite Data Service
     private static final int DOWNLOAD_PSDS_DATA = 6;
-    private static final int UPDATE_LOCATION = 7;  // Handle external location from network listener
     private static final int DOWNLOAD_PSDS_DATA_FINISHED = 11;
     private static final int INITIALIZE_HANDLER = 13;
     private static final int REQUEST_LOCATION = 16;
@@ -877,7 +876,7 @@
         });
     }
 
-    private void handleUpdateLocation(Location location) {
+    private void injectLocation(Location location) {
         if (location.hasAccuracy()) {
             if (DEBUG) {
                 Log.d(TAG, "injectLocation: " + location);
@@ -2034,9 +2033,6 @@
                 case DOWNLOAD_PSDS_DATA_FINISHED:
                     mDownloadPsdsDataPending = STATE_IDLE;
                     break;
-                case UPDATE_LOCATION:
-                    handleUpdateLocation((Location) msg.obj);
-                    break;
                 case INITIALIZE_HANDLER:
                     handleInitialize();
                     break;
@@ -2132,7 +2128,7 @@
         public void onLocationChanged(Location location) {
             // this callback happens on mHandler looper
             if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
-                handleUpdateLocation(location);
+                injectLocation(location);
             }
         }
     }
@@ -2161,8 +2157,6 @@
                 return "DOWNLOAD_PSDS_DATA";
             case DOWNLOAD_PSDS_DATA_FINISHED:
                 return "DOWNLOAD_PSDS_DATA_FINISHED";
-            case UPDATE_LOCATION:
-                return "UPDATE_LOCATION";
             case INITIALIZE_HANDLER:
                 return "INITIALIZE_HANDLER";
             case REPORT_LOCATION:
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7529434..f032fa6 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2256,7 +2256,7 @@
             final int callingUid = Binder.getCallingUid();
             final boolean isSystemToast = isCallerSystemOrPhone()
                     || PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg);
-            final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
+            final boolean isPackageSuspended = isPackagePaused(pkg);
             final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg,
                     callingUid);
 
@@ -4104,17 +4104,7 @@
             Preconditions.checkNotNull(pkg);
             checkCallerIsSameApp(pkg);
 
-            boolean isPaused;
-
-            final PackageManagerInternal pmi = LocalServices.getService(
-                    PackageManagerInternal.class);
-            int flags = pmi.getDistractingPackageRestrictions(
-                    pkg, Binder.getCallingUserHandle().getIdentifier());
-            isPaused = ((flags & PackageManager.RESTRICTION_HIDE_NOTIFICATIONS) != 0);
-
-            isPaused |= isPackageSuspendedForUser(pkg, Binder.getCallingUid());
-
-            return isPaused;
+            return isPackagePausedOrSuspended(pkg, Binder.getCallingUid());
         }
 
         private void verifyPrivilegedListener(INotificationListener token, UserHandle user,
@@ -5375,11 +5365,18 @@
     }
 
     @GuardedBy("mNotificationLock")
-    private boolean isPackageSuspendedLocked(NotificationRecord r) {
-        final String pkg = r.sbn.getPackageName();
-        final int callingUid = r.sbn.getUid();
+    boolean isPackagePausedOrSuspended(String pkg, int uid) {
+        boolean isPaused;
 
-        return isPackageSuspendedForUser(pkg, callingUid);
+        final PackageManagerInternal pmi = LocalServices.getService(
+                PackageManagerInternal.class);
+        int flags = pmi.getDistractingPackageRestrictions(
+                pkg, Binder.getCallingUserHandle().getIdentifier());
+        isPaused = ((flags & PackageManager.RESTRICTION_HIDE_NOTIFICATIONS) != 0);
+
+        isPaused |= isPackageSuspendedForUser(pkg, uid);
+
+        return isPaused;
     }
 
     protected class PostNotificationRunnable implements Runnable {
@@ -5412,7 +5409,8 @@
                         return;
                     }
 
-                    final boolean isPackageSuspended = isPackageSuspendedLocked(r);
+                    final boolean isPackageSuspended =
+                            isPackagePausedOrSuspended(r.sbn.getPackageName(), r.getUid());
                     r.setHidden(isPackageSuspended);
                     if (isPackageSuspended) {
                         mUsageStats.registerSuspendedByAdmin(r);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b0f73c3..232bca8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -238,6 +238,7 @@
 import android.os.storage.StorageManagerInternal;
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
+import android.permission.IPermissionManager;
 import android.provider.DeviceConfig;
 import android.provider.MediaStore;
 import android.provider.Settings.Global;
@@ -961,7 +962,10 @@
 
     // TODO remove this and go through mPermissonManager directly
     final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
+    // Internal interface for permission manager
     private final PermissionManagerServiceInternal mPermissionManager;
+    // Public interface for permission manager
+    private final IPermissionManager mPermissionManagerService;
 
     private final ComponentResolver mComponentResolver;
     // List of packages names to keep cached, even if they are uninstalled for all users
@@ -2419,6 +2423,8 @@
                     mPackages);
             mPermissionManager = PermissionManagerService.create(context,
                     mPackages /*externalLock*/);
+            mPermissionManagerService =
+                    (IPermissionManager) ServiceManager.getService("permissionmgr");
             mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
             mSettings = new Settings(Environment.getDataDirectory(),
                     mPermissionManager.getPermissionSettings(), mPackages);
@@ -4624,30 +4630,15 @@
         return null;
     }
 
-    @Override
-    public PermissionInfo getPermissionInfo(String name, String packageName, int flags) {
-        return mPermissionManager.getPermissionInfo(name, packageName, flags, getCallingUid());
-    }
-
-    @Override
-    public @Nullable ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String groupName,
-            int flags) {
-        final List<PermissionInfo> permissionList =
-                mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid());
-        return (permissionList == null) ? null : new ParceledListSlice<>(permissionList);
-    }
-
+    // NOTE: Can't remove due to unsupported app usage
     @Override
     public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) {
-        return mPermissionManager.getPermissionGroupInfo(groupName, flags, getCallingUid());
-    }
-
-    @Override
-    public @NonNull ParceledListSlice<PermissionGroupInfo> getAllPermissionGroups(int flags) {
-        final List<PermissionGroupInfo> permissionList =
-                mPermissionManager.getAllPermissionGroups(flags, getCallingUid());
-        return (permissionList == null)
-                ? ParceledListSlice.emptyList() : new ParceledListSlice<>(permissionList);
+        try {
+            // Because this is accessed via the package manager service AIDL,
+            // go through the permission manager service AIDL
+            return mPermissionManagerService.getPermissionGroupInfo(groupName, flags);
+        } catch (RemoteException ignore) { }
+        return null;
     }
 
     @GuardedBy("mPackages")
@@ -5704,37 +5695,36 @@
         }
     }
 
-    private boolean addDynamicPermission(PermissionInfo info, final boolean async) {
-        return mPermissionManager.addDynamicPermission(
-                info, async, getCallingUid(), new PermissionCallback() {
-                    @Override
-                    public void onPermissionChanged() {
-                        if (!async) {
-                            mSettings.writeLPr();
-                        } else {
-                            scheduleWriteSettingsLocked();
-                        }
-                    }
-                });
-    }
-
+    // NOTE: Can't remove due to unsupported app usage
     @Override
     public boolean addPermission(PermissionInfo info) {
-        synchronized (mPackages) {
-            return addDynamicPermission(info, false);
-        }
+        try {
+            // Because this is accessed via the package manager service AIDL,
+            // go through the permission manager service AIDL
+            return mPermissionManagerService.addPermission(info, false);
+        } catch (RemoteException ignore) { }
+        return false;
     }
 
+    // NOTE: Can't remove due to unsupported app usage
     @Override
     public boolean addPermissionAsync(PermissionInfo info) {
-        synchronized (mPackages) {
-            return addDynamicPermission(info, true);
-        }
+        try {
+            // Because this is accessed via the package manager service AIDL,
+            // go through the permission manager service AIDL
+            return mPermissionManagerService.addPermission(info, true);
+        } catch (RemoteException ignore) { }
+        return false;
     }
 
+    // NOTE: Can't remove due to unsupported app usage
     @Override
     public void removePermission(String permName) {
-        mPermissionManager.removeDynamicPermission(permName, getCallingUid(), mPermissionCallback);
+        try {
+            // Because this is accessed via the package manager service AIDL,
+            // go through the permission manager service AIDL
+            mPermissionManagerService.removePermission(permName);
+        } catch (RemoteException ignore) { }
     }
 
     @Override
@@ -6565,9 +6555,15 @@
         return false;
     }
 
+    // NOTE: Can't remove due to unsupported app usage
     @Override
     public String[] getAppOpPermissionPackages(String permName) {
-        return mPermissionManager.getAppOpPermissionPackages(permName);
+        try {
+            // Because this is accessed via the package manager service AIDL,
+            // go through the permission manager service AIDL
+            return mPermissionManagerService.getAppOpPermissionPackages(permName);
+        } catch (RemoteException ignore) { }
+        return null;
     }
 
     @Override
@@ -21747,7 +21743,7 @@
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
             FileDescriptor err, String[] args, ShellCallback callback,
             ResultReceiver resultReceiver) {
-        (new PackageManagerShellCommand(this)).exec(
+        (new PackageManagerShellCommand(this, mPermissionManagerService)).exec(
                 this, in, out, err, args, callback, resultReceiver);
     }
 
@@ -25009,6 +25005,17 @@
                 Slog.wtf(TAG, e);
             }
         }
+
+        @Override
+        public void writeSettings(boolean async) {
+            synchronized (mPackages) {
+                if (async) {
+                    scheduleWriteSettingsLocked();
+                } else {
+                    mSettings.writeLPr();
+                }
+            }
+        }
     }
 
     @GuardedBy("mPackages")
@@ -25286,7 +25293,8 @@
             return false;
         }
         String appOpPermission = Manifest.permission.REQUEST_INSTALL_PACKAGES;
-        String[] packagesDeclaringPermission = getAppOpPermissionPackages(appOpPermission);
+        String[] packagesDeclaringPermission =
+                mPermissionManager.getAppOpPermissionPackages(appOpPermission, callingUid);
         if (!ArrayUtils.contains(packagesDeclaringPermission, packageName)) {
             if (throwIfPermNotDeclared) {
                 throw new SecurityException("Need to declare " + appOpPermission
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 5b80556..a25c68f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -87,6 +87,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
+import android.permission.IPermissionManager;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
@@ -132,6 +133,7 @@
     private static final int DEFAULT_WAIT_MS = 60 * 1000;
 
     final IPackageManager mInterface;
+    final IPermissionManager mPermissionManager;
     final private WeakHashMap<String, Resources> mResourceCache =
             new WeakHashMap<String, Resources>();
     int mTargetUser;
@@ -139,8 +141,10 @@
     boolean mComponents;
     int mQueryFlags;
 
-    PackageManagerShellCommand(PackageManagerService service) {
+    PackageManagerShellCommand(
+            PackageManagerService service, IPermissionManager permissionManager) {
         mInterface = service;
+        mPermissionManager = permissionManager;
     }
 
     @Override
@@ -786,7 +790,8 @@
 
     private int runListPermissionGroups() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
-        final List<PermissionGroupInfo> pgs = mInterface.getAllPermissionGroups(0).getList();
+        final List<PermissionGroupInfo> pgs =
+                mPermissionManager.getAllPermissionGroups(0).getList();
 
         final int count = pgs.size();
         for (int p = 0; p < count ; p++) {
@@ -833,7 +838,7 @@
         final ArrayList<String> groupList = new ArrayList<String>();
         if (groups) {
             final List<PermissionGroupInfo> infos =
-                    mInterface.getAllPermissionGroups(0 /*flags*/).getList();
+                    mPermissionManager.getAllPermissionGroups(0 /*flags*/).getList();
             final int count = infos.size();
             for (int i = 0; i < count; i++) {
                 groupList.add(infos.get(i).name);
@@ -2933,8 +2938,8 @@
                 }
                 prefix = "  ";
             }
-            List<PermissionInfo> ps =
-                    mInterface.queryPermissionsByGroup(groupList.get(i), 0 /*flags*/).getList();
+            List<PermissionInfo> ps = mPermissionManager
+                    .queryPermissionsByGroup(groupList.get(i), 0 /*flags*/).getList();
             final int count = ps.size();
             boolean first = true;
             for (int p = 0 ; p < count ; p++) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 1287085..43a8373 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -51,10 +51,13 @@
 import android.app.ApplicationPackageManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.PermissionGroupInfoFlags;
+import android.content.pm.PackageManager.PermissionInfoFlags;
 import android.content.pm.PackageManager.PermissionWhitelistFlags;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.Package;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.metrics.LogMaker;
@@ -63,12 +66,14 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Process;
+import android.os.ServiceManager;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
+import android.permission.IPermissionManager;
 import android.permission.PermissionControllerManager;
 import android.permission.PermissionManager;
 import android.permission.PermissionManagerInternal;
@@ -122,7 +127,7 @@
 /**
  * Manages all permissions and handles permissions related tasks.
  */
-public class PermissionManagerService {
+public class PermissionManagerService extends IPermissionManager.Stub {
     private static final String TAG = "PackageManager";
 
     /** Permission grant: not grant the permission. */
@@ -283,7 +288,13 @@
         if (permMgrInt != null) {
             return permMgrInt;
         }
-        new PermissionManagerService(context, externalLock);
+        PermissionManagerService permissionService =
+                (PermissionManagerService) ServiceManager.getService("permissionmgr");
+        if (permissionService == null) {
+            permissionService =
+                    new PermissionManagerService(context, externalLock);
+            ServiceManager.addService("permissionmgr", permissionService);
+        }
         return LocalServices.getService(PermissionManagerServiceInternal.class);
     }
 
@@ -293,6 +304,158 @@
         }
     }
 
+    @Override
+    public String[] getAppOpPermissionPackages(String permName) {
+        return getAppOpPermissionPackagesInternal(permName, getCallingUid());
+    }
+
+    private String[] getAppOpPermissionPackagesInternal(String permName, int callingUid) {
+        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+            return null;
+        }
+        synchronized (mLock) {
+            final ArraySet<String> pkgs = mSettings.mAppOpPermissionPackages.get(permName);
+            if (pkgs == null) {
+                return null;
+            }
+            return pkgs.toArray(new String[pkgs.size()]);
+        }
+    }
+
+    @Override
+    @NonNull
+    public ParceledListSlice<PermissionGroupInfo> getAllPermissionGroups(
+            @PermissionGroupInfoFlags int flags) {
+        final int callingUid = getCallingUid();
+        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+            return ParceledListSlice.emptyList();
+        }
+        synchronized (mLock) {
+            final int n = mSettings.mPermissionGroups.size();
+            final ArrayList<PermissionGroupInfo> out =
+                    new ArrayList<PermissionGroupInfo>(n);
+            for (PackageParser.PermissionGroup pg : mSettings.mPermissionGroups.values()) {
+                out.add(PackageParser.generatePermissionGroupInfo(pg, flags));
+            }
+            return new ParceledListSlice<>(out);
+        }
+    }
+
+
+    @Override
+    @Nullable
+    public PermissionGroupInfo getPermissionGroupInfo(String groupName,
+            @PermissionGroupInfoFlags int flags) {
+        final int callingUid = getCallingUid();
+        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+            return null;
+        }
+        synchronized (mLock) {
+            return PackageParser.generatePermissionGroupInfo(
+                    mSettings.mPermissionGroups.get(groupName), flags);
+        }
+    }
+
+
+    @Override
+    @Nullable
+    public PermissionInfo getPermissionInfo(String permName, String packageName,
+            @PermissionInfoFlags int flags) {
+        final int callingUid = getCallingUid();
+        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+            return null;
+        }
+        synchronized (mLock) {
+            final BasePermission bp = mSettings.getPermissionLocked(permName);
+            if (bp == null) {
+                return null;
+            }
+            final int adjustedProtectionLevel = adjustPermissionProtectionFlagsLocked(
+                    bp.getProtectionLevel(), packageName, callingUid);
+            return bp.generatePermissionInfo(adjustedProtectionLevel, flags);
+        }
+    }
+
+    @Override
+    @Nullable
+    public ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String groupName,
+            @PermissionInfoFlags int flags) {
+        final int callingUid = getCallingUid();
+        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+            return null;
+        }
+        synchronized (mLock) {
+            if (groupName != null && !mSettings.mPermissionGroups.containsKey(groupName)) {
+                return null;
+            }
+            final 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);
+        }
+    }
+
+    @Override
+    public boolean addPermission(PermissionInfo info, boolean async) {
+        final int callingUid = getCallingUid();
+        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 = mSettings.enforcePermissionTree(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) {
+            mPackageManagerInt.writeSettings(async);
+        }
+        return added;
+    }
+
+    @Override
+    public void removePermission(String permName) {
+        final int callingUid = getCallingUid();
+        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
+            throw new SecurityException("Instant applications don't have access to this method");
+        }
+        final BasePermission tree = mSettings.enforcePermissionTree(permName, callingUid);
+        synchronized (mLock) {
+            final BasePermission bp = mSettings.getPermissionLocked(permName);
+            if (bp == null) {
+                return;
+            }
+            if (bp.isDynamic()) {
+                // TODO: switch this back to SecurityException
+                Slog.wtf(TAG, "Not allowed to modify non-dynamic permission "
+                        + permName);
+            }
+            mSettings.removePermissionLocked(permName);
+            mPackageManagerInt.writeSettings(false);
+        }
+    }
+
     private int checkPermission(String permName, String pkgName, int callingUid, int userId) {
         if (!mUserManagerInt.exists(userId)) {
             return PackageManager.PERMISSION_DENIED;
@@ -491,69 +654,6 @@
                 && permissionsState.hasPermission(FULLER_PERMISSION_MAP.get(permName), userId);
     }
 
-    private PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags,
-            int callingUid) {
-        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
-            return null;
-        }
-        synchronized (mLock) {
-            return PackageParser.generatePermissionGroupInfo(
-                    mSettings.mPermissionGroups.get(groupName), flags);
-        }
-    }
-
-    private List<PermissionGroupInfo> getAllPermissionGroups(int flags, int callingUid) {
-        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
-            return null;
-        }
-        synchronized (mLock) {
-            final int N = mSettings.mPermissionGroups.size();
-            final ArrayList<PermissionGroupInfo> out
-                    = new ArrayList<PermissionGroupInfo>(N);
-            for (PackageParser.PermissionGroup pg : mSettings.mPermissionGroups.values()) {
-                out.add(PackageParser.generatePermissionGroupInfo(pg, flags));
-            }
-            return out;
-        }
-    }
-
-    private PermissionInfo getPermissionInfo(String permName, String packageName, int flags,
-            int callingUid) {
-        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
-            return null;
-        }
-        // reader
-        synchronized (mLock) {
-            final BasePermission bp = mSettings.getPermissionLocked(permName);
-            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;
-        }
-        synchronized (mLock) {
-            if (groupName != null && !mSettings.mPermissionGroups.containsKey(groupName)) {
-                return null;
-            }
-            final 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 out;
-        }
-    }
-
     private int adjustPermissionProtectionFlagsLocked(
             int protectionLevel, String packageName, int uid) {
         // Signature permission flags area always reported
@@ -799,63 +899,6 @@
         }
     }
 
-    private boolean addDynamicPermission(
-            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 = mSettings.enforcePermissionTree(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 removeDynamicPermission(
-            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 = mSettings.enforcePermissionTree(permName, callingUid);
-        synchronized (mLock) {
-            final BasePermission bp = mSettings.getPermissionLocked(permName);
-            if (bp == null) {
-                return;
-            }
-            if (bp.isDynamic()) {
-                // TODO: switch this back to SecurityException
-                Slog.wtf(TAG, "Not allowed to modify non-dynamic permission "
-                        + permName);
-            }
-            mSettings.removePermissionLocked(permName);
-            if (callback != null) {
-                callback.onPermissionRemoved();
-            }
-        }
-    }
-
     /**
      * Restore the permission state for a package.
      *
@@ -2327,12 +2370,8 @@
             final String permissionName = pkg.requestedPermissions.get(i);
 
             final BasePermission bp = mSettings.getPermissionLocked(permissionName);
-            if (bp == null) {
-                Slog.w(TAG, "Cannot whitelist unknown permission: " + permissionName);
-                continue;
-            }
 
-            if (!bp.isHardOrSoftRestricted()) {
+            if (bp == null || !bp.isHardOrSoftRestricted()) {
                 continue;
             }
 
@@ -2497,19 +2536,6 @@
         return runtimePermissionChangedUserIds;
     }
 
-    private String[] getAppOpPermissionPackages(String permName) {
-        if (mPackageManagerInt.getInstantAppPackageName(Binder.getCallingUid()) != null) {
-            return null;
-        }
-        synchronized (mLock) {
-            final ArraySet<String> pkgs = mSettings.mAppOpPermissionPackages.get(permName);
-            if (pkgs == null) {
-                return null;
-            }
-            return pkgs.toArray(new String[pkgs.size()]);
-        }
-    }
-
     private int getPermissionFlags(
             String permName, String packageName, int callingUid, int userId) {
         if (!mUserManagerInt.exists(userId)) {
@@ -3137,16 +3163,6 @@
             PermissionManagerService.this.removeAllPermissions(pkg, chatty);
         }
         @Override
-        public boolean addDynamicPermission(PermissionInfo info, boolean async, int callingUid,
-                PermissionCallback callback) {
-            return PermissionManagerService.this.addDynamicPermission(info, callingUid, callback);
-        }
-        @Override
-        public void removeDynamicPermission(String permName, int callingUid,
-                PermissionCallback callback) {
-            PermissionManagerService.this.removeDynamicPermission(permName, callingUid, callback);
-        }
-        @Override
         public void grantRuntimePermission(String permName, String packageName,
                 boolean overridePolicy, int callingUid, int userId,
                 PermissionCallback callback) {
@@ -3200,8 +3216,9 @@
                     volumeUuid, sdkUpdated, allPackages, callback);
         }
         @Override
-        public String[] getAppOpPermissionPackages(String permName) {
-            return PermissionManagerService.this.getAppOpPermissionPackages(permName);
+        public String[] getAppOpPermissionPackages(String permName, int callingUid) {
+            return PermissionManagerService.this
+                    .getAppOpPermissionPackagesInternal(permName, callingUid);
         }
         @Override
         public int getPermissionFlags(String permName, String packageName, int callingUid,
@@ -3252,27 +3269,6 @@
             return PermissionManagerService.this.checkUidPermission(permName, pkg, uid, callingUid);
         }
         @Override
-        public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags,
-                int callingUid) {
-            return PermissionManagerService.this.getPermissionGroupInfo(
-                    groupName, flags, callingUid);
-        }
-        @Override
-        public List<PermissionGroupInfo> getAllPermissionGroups(int flags, int callingUid) {
-            return PermissionManagerService.this.getAllPermissionGroups(flags, callingUid);
-        }
-        @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 PermissionSettings getPermissionSettings() {
             return mSettings;
         }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 0f7d07a..23d0114 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -20,9 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.PermissionInfoFlags;
 import android.content.pm.PackageParser;
-import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.permission.PermissionManagerInternal;
 
@@ -150,35 +148,13 @@
     public abstract void addAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
     public abstract void addAllPermissionGroups(@NonNull PackageParser.Package pkg, boolean chatty);
     public abstract void removeAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
-    public abstract boolean addDynamicPermission(@NonNull PermissionInfo info, boolean async,
-            int callingUid, @Nullable PermissionCallback callback);
-    public abstract void removeDynamicPermission(@NonNull String permName, int callingUid,
-            @Nullable PermissionCallback callback);
 
-    public abstract @Nullable String[] getAppOpPermissionPackages(@NonNull String permName);
+    /** Retrieve the packages that have requested the given app op permission */
+    public abstract @Nullable String[] getAppOpPermissionPackages(
+            @NonNull String permName, int callingUid);
 
     public abstract int getPermissionFlags(@NonNull String permName,
             @NonNull String packageName, int callingUid, int userId);
-    /**
-     * Retrieve all of the information we know about a particular group of permissions.
-     */
-    public abstract @Nullable PermissionGroupInfo getPermissionGroupInfo(
-            @NonNull String groupName, int flags, int callingUid);
-    /**
-     * Retrieve all of the known permission groups in the system.
-     */
-    public abstract @Nullable List<PermissionGroupInfo> getAllPermissionGroups(int flags,
-            int callingUid);
-    /**
-     * 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);
 
     /**
      * Updates the flags associated with a permission by replacing the flags in
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index de3e89f..eb2f648 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -75,6 +75,7 @@
 import java.util.List;
 import java.util.Random;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
@@ -114,9 +115,8 @@
     private final Set<NewRollback> mNewRollbacks = new ArraySet<>();
 
     // The list of all rollbacks, including available and committed rollbacks.
-    // This list is null until the rollback data has been loaded.
     @GuardedBy("mLock")
-    private List<RollbackData> mRollbacks;
+    private final List<RollbackData> mRollbacks;
 
     private final RollbackStore mRollbackStore;
 
@@ -138,25 +138,24 @@
         // SystemService#onStart.
         mInstaller = new Installer(mContext);
         mInstaller.onStart();
-        mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
-        mHandlerThread.start();
-
-        // Monitor the handler thread
-        Watchdog.getInstance().addThread(getHandler(), HANDLER_THREAD_TIMEOUT_DURATION_MILLIS);
 
         mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback"));
 
         mPackageHealthObserver = new RollbackPackageHealthObserver(mContext);
         mAppDataRollbackHelper = new AppDataRollbackHelper(mInstaller);
 
-        // Kick off loading of the rollback data from strorage in a background
-        // thread.
-        // TODO: Consider loading the rollback data directly here instead, to
-        // avoid the need to call ensureRollbackDataLoaded every time before
-        // accessing the rollback data?
-        // TODO: Test that this kicks off initial scheduling of rollback
-        // expiration.
-        getHandler().post(() -> ensureRollbackDataLoaded());
+        // Load rollback data from device storage.
+        synchronized (mLock) {
+            mRollbacks = mRollbackStore.loadAllRollbackData();
+            for (RollbackData data : mRollbacks) {
+                mAllocatedRollbackIds.put(data.info.getRollbackId(), true);
+            }
+        }
+
+        // Kick off and start monitoring the handler thread.
+        mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
+        mHandlerThread.start();
+        Watchdog.getInstance().addThread(getHandler(), HANDLER_THREAD_TIMEOUT_DURATION_MILLIS);
 
         // TODO: Make sure to register these call backs when a new user is
         // added too.
@@ -299,20 +298,15 @@
         // to get the most up-to-date results. This is intended to reduce test
         // flakiness when checking available rollbacks immediately after
         // installing a package with rollback enabled.
-        final LinkedBlockingQueue<Boolean> result = new LinkedBlockingQueue<>();
-        getHandler().post(() -> result.offer(true));
-
+        CountDownLatch latch = new CountDownLatch(1);
+        getHandler().post(() -> latch.countDown());
         try {
-            result.take();
+            latch.await();
         } catch (InterruptedException ie) {
-            // We may not get the most up-to-date information, but whatever we
-            // can get now is better than nothing, so log but otherwise ignore
-            // the exception.
-            Slog.w(TAG, "Interrupted while waiting for handler thread in getAvailableRollbacks");
+            throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
         }
 
         synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
             List<RollbackInfo> rollbacks = new ArrayList<>();
             for (int i = 0; i < mRollbacks.size(); ++i) {
                 RollbackData data = mRollbacks.get(i);
@@ -329,7 +323,6 @@
         enforceManageRollbacks("getRecentlyCommittedRollbacks");
 
         synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
             List<RollbackInfo> rollbacks = new ArrayList<>();
             for (int i = 0; i < mRollbacks.size(); ++i) {
                 RollbackData data = mRollbacks.get(i);
@@ -364,8 +357,6 @@
                 final long timeDifference = mRelativeBootTime - oldRelativeBootTime;
 
                 synchronized (mLock) {
-                    ensureRollbackDataLoadedLocked();
-
                     Iterator<RollbackData> iter = mRollbacks.iterator();
                     while (iter.hasNext()) {
                         RollbackData data = iter.next();
@@ -544,13 +535,21 @@
                 Manifest.permission.TEST_MANAGE_ROLLBACKS,
                 "reloadPersistedData");
 
-        synchronized (mLock) {
-            mRollbacks = null;
-        }
+        CountDownLatch latch = new CountDownLatch(1);
         getHandler().post(() -> {
             updateRollbackLifetimeDurationInMillis();
-            ensureRollbackDataLoaded();
+            synchronized (mLock) {
+                mRollbacks.clear();
+                mRollbacks.addAll(mRollbackStore.loadAllRollbackData());
+            }
+            latch.countDown();
         });
+
+        try {
+            latch.await();
+        } catch (InterruptedException ie) {
+            throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
+        }
     }
 
     @Override
@@ -559,7 +558,6 @@
                 Manifest.permission.TEST_MANAGE_ROLLBACKS,
                 "expireRollbackForPackage");
         synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
             Iterator<RollbackData> iter = mRollbacks.iterator();
             while (iter.hasNext()) {
                 RollbackData data = iter.next();
@@ -583,7 +581,7 @@
             try {
                 Thread.sleep(millis);
             } catch (InterruptedException e) {
-                // ignored.
+                throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
             }
         });
     }
@@ -626,7 +624,6 @@
             List<RollbackData> restoreInProgress = new ArrayList<>();
             Set<String> apexPackageNames = new HashSet<>();
             synchronized (mLock) {
-                ensureRollbackDataLoadedLocked();
                 for (RollbackData data : mRollbacks) {
                     if (data.isStaged()) {
                         if (data.state == RollbackData.ROLLBACK_STATE_ENABLING) {
@@ -648,17 +645,14 @@
                 PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
                 PackageInstaller.SessionInfo session = installer.getSessionInfo(
                         data.stagedSessionId);
-                // TODO: What if session is null?
-                if (session != null) {
-                    if (session.isStagedSessionApplied()) {
-                        makeRollbackAvailable(data);
-                    } else if (session.isStagedSessionFailed()) {
-                        // TODO: Do we need to remove this from
-                        // mRollbacks, or is it okay to leave as
-                        // unavailable until the next reboot when it will go
-                        // away on its own?
-                        deleteRollback(data);
-                    }
+                if (session == null || session.isStagedSessionFailed()) {
+                    // TODO: Do we need to remove this from
+                    // mRollbacks, or is it okay to leave as
+                    // unavailable until the next reboot when it will go
+                    // away on its own?
+                    deleteRollback(data);
+                } else if (session.isStagedSessionApplied()) {
+                    makeRollbackAvailable(data);
                 }
             }
 
@@ -689,41 +683,6 @@
     }
 
     /**
-     * Load rollback data from storage if it has not already been loaded.
-     * After calling this function, mRollbacks will be non-null.
-     */
-    private void ensureRollbackDataLoaded() {
-        synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-        }
-    }
-
-    /**
-     * Load rollback data from storage if it has not already been loaded.
-     * After calling this function, mRollbacks will be non-null.
-     */
-    @GuardedBy("mLock")
-    private void ensureRollbackDataLoadedLocked() {
-        if (mRollbacks == null) {
-            loadAllRollbackDataLocked();
-        }
-    }
-
-    /**
-     * Load all rollback data from storage.
-     * Note: We do potentially heavy IO here while holding mLock, because we
-     * have to have the rollback data loaded before we can do anything else
-     * meaningful.
-     */
-    @GuardedBy("mLock")
-    private void loadAllRollbackDataLocked() {
-        mRollbacks = mRollbackStore.loadAllRollbackData();
-        for (RollbackData data : mRollbacks) {
-            mAllocatedRollbackIds.put(data.info.getRollbackId(), true);
-        }
-    }
-
-    /**
      * Called when a package has been replaced with a different version.
      * Removes all backups for the package not matching the currently
      * installed package version.
@@ -734,7 +693,6 @@
         VersionedPackage installedVersion = getInstalledPackageVersion(packageName);
 
         synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
             Iterator<RollbackData> iter = mRollbacks.iterator();
             while (iter.hasNext()) {
                 RollbackData data = iter.next();
@@ -803,8 +761,6 @@
         Instant now = Instant.now();
         Instant oldest = null;
         synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
-
             Iterator<RollbackData> iter = mRollbacks.iterator();
             while (iter.hasNext()) {
                 RollbackData data = iter.next();
@@ -924,7 +880,6 @@
         // TODO: This check could be made more efficient.
         RollbackData rd = null;
         synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
             for (int i = 0; i < mRollbacks.size(); ++i) {
                 RollbackData data = mRollbacks.get(i);
                 if (data.apkSessionId == parentSession.getSessionId()) {
@@ -1080,7 +1035,6 @@
     private void snapshotUserDataInternal(String packageName) {
         synchronized (mLock) {
             // staged installs
-            ensureRollbackDataLoadedLocked();
             for (int i = 0; i < mRollbacks.size(); i++) {
                 RollbackData data = mRollbacks.get(i);
                 if (data.state != RollbackData.ROLLBACK_STATE_ENABLING) {
@@ -1113,7 +1067,6 @@
         PackageRollbackInfo info = null;
         RollbackData rollbackData = null;
         synchronized (mLock) {
-            ensureRollbackDataLoadedLocked();
             for (int i = 0; i < mRollbacks.size(); ++i) {
                 RollbackData data = mRollbacks.get(i);
                 if (data.restoreUserDataInProgress) {
@@ -1209,7 +1162,6 @@
         getHandler().post(() -> {
             RollbackData rd = null;
             synchronized (mLock) {
-                ensureRollbackDataLoadedLocked();
                 for (int i = 0; i < mRollbacks.size(); ++i) {
                     RollbackData data = mRollbacks.get(i);
                     if (data.stagedSessionId == originalSessionId) {
@@ -1380,7 +1332,6 @@
             // device reboots between when the session is
             // committed and this point. Revisit this after
             // adding support for rollback of staged installs.
-            ensureRollbackDataLoadedLocked();
             mRollbacks.add(data);
         }
 
@@ -1417,9 +1368,6 @@
      */
     private RollbackData getRollbackForId(int rollbackId) {
         synchronized (mLock) {
-            // TODO: Have ensureRollbackDataLoadedLocked return the list of
-            // available rollbacks, to hopefully avoid forgetting to call it?
-            ensureRollbackDataLoadedLocked();
             for (int i = 0; i < mRollbacks.size(); ++i) {
                 RollbackData data = mRollbacks.get(i);
                 if (data.info.getRollbackId() == rollbackId) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6665381..f6eb8ea 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -108,6 +108,7 @@
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESTARTING_PROCESS;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
 import static com.android.server.wm.ActivityStack.LAUNCH_TICK;
@@ -1922,6 +1923,15 @@
         return state1 == mState || state2 == mState || state3 == mState || state4 == mState;
     }
 
+    /**
+     * Returns {@code true} if the Activity is in one of the specified states.
+     */
+    boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
+            ActivityState state4, ActivityState state5) {
+        return state1 == mState || state2 == mState || state3 == mState || state4 == mState
+                || state5 == mState;
+    }
+
     void notifyAppResumed(boolean wasStopped) {
         if (mAppWindowToken == null) {
             Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: "
@@ -2037,12 +2047,10 @@
             mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     WindowVisibilityItem.obtain(true /* showWindow */));
             makeActiveIfNeeded(null /* activeActivity*/);
-            if (isState(STOPPING, STOPPED) && isFocusable()) {
-                // #shouldMakeActive() only evaluates the topmost activities in task, so
-                // activities that are not the topmost in task are not being resumed or paused.
-                // For activities that are still in STOPPING or STOPPED state, updates the state
-                // to PAUSE at least when making it visible.
-                setState(PAUSED, "makeClientVisible");
+            if (isState(STOPPING, STOPPED)) {
+                // Set state to STARTED in order to have consistent state with client while
+                // making an non-active activity visible from stopped.
+                setState(STARTED, "makeClientVisible");
             }
         } catch (Exception e) {
             Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e);
@@ -2118,7 +2126,7 @@
         // calls will lead to noticeable jank. A later call to
         // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to a proper
         // active state.
-        if (!isState(RESUMED, PAUSED, STOPPED, STOPPING)
+        if (!isState(STARTED, RESUMED, PAUSED, STOPPED, STOPPING)
                 || getActivityStack().mTranslucentActivityWaiting != null) {
             return false;
         }
@@ -2646,7 +2654,7 @@
                 compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                 prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
                 allowTaskSnapshot(),
-                mState.ordinal() >= RESUMED.ordinal() && mState.ordinal() <= STOPPED.ordinal(),
+                mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal(),
                 fromRecents);
         if (shown) {
             mStartingWindowState = STARTING_WINDOW_SHOWN;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index df3712f..6d902fc 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -62,6 +62,7 @@
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
 import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
@@ -164,6 +165,8 @@
 import com.android.server.am.EventLogTags;
 import com.android.server.am.PendingIntentRecord;
 
+import com.google.android.collect.Sets;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -292,6 +295,7 @@
 
     enum ActivityState {
         INITIALIZING,
+        STARTED,
         RESUMED,
         PAUSING,
         PAUSED,
@@ -1608,7 +1612,7 @@
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
-                if (r.isState(STOPPING, STOPPED, PAUSED, PAUSING)) {
+                if (r.isState(STARTED, STOPPING, STOPPED, PAUSED, PAUSING)) {
                     r.setSleeping(true);
                 }
             }
@@ -2435,6 +2439,7 @@
                 case RESUMED:
                 case PAUSING:
                 case PAUSED:
+                case STARTED:
                     addToStopping(r, true /* scheduleIdle */,
                             canEnterPictureInPicture /* idleDelayed */, "makeInvisible");
                     break;
@@ -3882,7 +3887,7 @@
         }
         if (activityNdx >= 0) {
             r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
-            if (r.isState(RESUMED, PAUSING, PAUSED)) {
+            if (r.isState(STARTED, RESUMED, PAUSING, PAUSED)) {
                 if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
                     Slog.w(TAG, "  Force finishing activity "
                             + r.intent.getComponent().flattenToShortString());
@@ -4031,6 +4036,14 @@
                 }
                 getDisplay().mDisplayContent.prepareAppTransition(transit, false);
 
+                // When finishing the activity pre-emptively take the snapshot before the app window
+                // is marked as hidden and any configuration changes take place
+                if (mWindowManager.mTaskSnapshotController != null) {
+                    final ArraySet<Task> tasks = Sets.newArraySet(task.mTask);
+                    mWindowManager.mTaskSnapshotController.snapshotTasks(tasks);
+                    mWindowManager.mTaskSnapshotController.addSkipClosingAppSnapshotTasks(tasks);
+                }
+
                 // Tell window manager to prepare for this one to be removed.
                 r.setVisibility(false);
 
@@ -4146,6 +4159,7 @@
                 || (prevState == PAUSED
                     && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
                 || finishingInNonFocusedStackOrNoRunning
+                || prevState == STARTED
                 || prevState == STOPPING
                 || prevState == STOPPED
                 || prevState == ActivityState.INITIALIZING) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index bc5e328..932e44e 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -27,6 +27,7 @@
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
@@ -731,10 +732,10 @@
                 if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r);
                 return null;
             }
-            // Don't consider any activies that are currently not in a state where they
+            // Don't consider any activities that are currently not in a state where they
             // can be destroyed.
             if (r.visible || !r.stopped || !r.haveState
-                    || r.isState(RESUMED, PAUSING, PAUSED, STOPPING)) {
+                    || r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING)) {
                 if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
                 continue;
             }
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index da17579..21bdc43 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -357,6 +357,33 @@
     return value ? JNI_TRUE : JNI_FALSE;
 }
 
+template<class T>
+static inline void logHidlError(Return<T>& result, const char* errorMessage) {
+    ALOGE("%s HIDL transport error: %s", errorMessage, result.description().c_str());
+}
+
+template<class T>
+static jboolean checkHidlReturn(Return<T>& result, const char* errorMessage) {
+    if (!result.isOk()) {
+        logHidlError(result, errorMessage);
+        return JNI_FALSE;
+    } else {
+        return JNI_TRUE;
+    }
+}
+
+static jboolean checkHidlReturn(Return<bool>& result, const char* errorMessage) {
+    if (!result.isOk()) {
+        logHidlError(result, errorMessage);
+        return JNI_FALSE;
+    } else if (!result) {
+        ALOGE("%s", errorMessage);
+        return JNI_FALSE;
+    } else {
+        return JNI_TRUE;
+    }
+}
+
 static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
     if (env->ExceptionCheck()) {
         ALOGE("An exception was thrown by callback '%s'.", methodName);
@@ -1913,8 +1940,7 @@
         result = gnssHal->setCallback(gnssCbIface);
     }
 
-    if (!result.isOk() || !result) {
-        ALOGE("SetCallback for IGnss interface failed.");
+    if (!checkHidlReturn(result, "IGnss setCallback() failed.")) {
         return JNI_FALSE;
     }
 
@@ -1924,35 +1950,29 @@
     } else {
         sp<IGnssXtraCallback> gnssXtraCbIface = new GnssXtraCallback();
         result = gnssXtraIface->setCallback(gnssXtraCbIface);
-        if (!result.isOk() || !result) {
+        if (!checkHidlReturn(result, "IGnssXtra setCallback() failed.")) {
             gnssXtraIface = nullptr;
-            ALOGI("SetCallback for IGnssXtra interface failed.");
         }
     }
 
     // Set IAGnss.hal callback.
-    Return<void> agnssStatus;
     if (agnssIface_V2_0 != nullptr) {
         sp<IAGnssCallback_V2_0> aGnssCbIface = new AGnssCallback_V2_0();
-        agnssStatus = agnssIface_V2_0->setCallback(aGnssCbIface);
+        auto agnssStatus = agnssIface_V2_0->setCallback(aGnssCbIface);
+        checkHidlReturn(agnssStatus, "IAGnss 2.0 setCallback() failed.");
     } else if (agnssIface != nullptr) {
         sp<IAGnssCallback_V1_0> aGnssCbIface = new AGnssCallback_V1_0();
-        agnssStatus = agnssIface->setCallback(aGnssCbIface);
+        auto agnssStatus = agnssIface->setCallback(aGnssCbIface);
+        checkHidlReturn(agnssStatus, "IAGnss setCallback() failed.");
     } else {
         ALOGI("Unable to initialize IAGnss interface.");
     }
 
-    if (!agnssStatus.isOk()) {
-        ALOGI("SetCallback for IAGnss interface failed.");
-    }
-
     // Set IGnssGeofencing.hal callback.
     sp<IGnssGeofenceCallback> gnssGeofencingCbIface = new GnssGeofenceCallback();
     if (gnssGeofencingIface != nullptr) {
         auto status = gnssGeofencingIface->setCallback(gnssGeofencingCbIface);
-        if (!status.isOk()) {
-            ALOGI("SetCallback for IGnssGeofencing interface failed.");
-        }
+        checkHidlReturn(status, "IGnssGeofencing setCallback() failed.");
     } else {
         ALOGI("Unable to initialize IGnssGeofencing interface.");
     }
@@ -1961,9 +1981,7 @@
     sp<IGnssNiCallback> gnssNiCbIface = new GnssNiCallback();
     if (gnssNiIface != nullptr) {
         auto status = gnssNiIface->setCallback(gnssNiCbIface);
-        if (!status.isOk()) {
-            ALOGI("SetCallback for IGnssNi interface failed.");
-        }
+        checkHidlReturn(status, "IGnssNi setCallback() failed.");
     } else {
         ALOGI("Unable to initialize IGnssNi interface.");
     }
@@ -1972,9 +1990,7 @@
     sp<IAGnssRilCallback> aGnssRilCbIface = new AGnssRilCallback();
     if (agnssRilIface != nullptr) {
         auto status = agnssRilIface->setCallback(aGnssRilCbIface);
-        if (!status.isOk()) {
-            ALOGI("SetCallback for IAGnssRil interface failed.");
-        }
+        checkHidlReturn(status, "IAGnssRil setCallback() failed.");
     } else {
         ALOGI("Unable to initialize IAGnssRil interface.");
     }
@@ -1984,9 +2000,7 @@
         sp<IGnssVisibilityControlCallback> gnssVisibilityControlCbIface =
                 new GnssVisibilityControlCallback();
         result = gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface);
-        if (!result.isOk() || !result) {
-            ALOGI("SetCallback for IGnssVisibilityControl interface failed.");
-        }
+        checkHidlReturn(result, "IGnssVisibilityControl setCallback() failed.");
     }
 
     // Set IMeasurementCorrections.hal callback.
@@ -1994,18 +2008,19 @@
         sp<IMeasurementCorrectionsCallback> gnssCorrectionsIfaceCbIface =
                 new MeasurementCorrectionsCallback();
         result = gnssCorrectionsIface->setCallback(gnssCorrectionsIfaceCbIface);
-        if (!result.isOk() || !result) {
-            ALOGI("SetCallback for IMeasurementCorrections interface failed.");
-        }
+        checkHidlReturn(result, "IMeasurementCorrections setCallback() failed.");
     }
 
     return JNI_TRUE;
 }
 
 static void android_location_GnssLocationProvider_cleanup(JNIEnv* /* env */, jobject /* obj */) {
-    if (gnssHal != nullptr) {
-        gnssHal->cleanup();
+    if (gnssHal == nullptr) {
+        return;
     }
+
+    auto result = gnssHal->cleanup();
+    checkHidlReturn(result, "IGnss cleanup() failed.");
 }
 
 static jboolean android_location_GnssLocationProvider_set_position_mode(JNIEnv* /* env */,
@@ -2026,48 +2041,37 @@
                  preferred_accuracy,
                  preferred_time);
     }
-    if (!result.isOk()) {
-       ALOGE("%s: GNSS setPositionMode failed\n", __func__);
-       return JNI_FALSE;
-    } else {
-       return result;
-    }
+
+    return checkHidlReturn(result, "IGnss setPositionMode() failed.");
 }
 
 static jboolean android_location_GnssLocationProvider_start(JNIEnv* /* env */, jobject /* obj */) {
-    if (gnssHal != nullptr) {
-        auto result = gnssHal->start();
-        if (!result.isOk()) {
-            return JNI_FALSE;
-        } else {
-            return result;
-        }
-    } else {
+    if (gnssHal == nullptr) {
         return JNI_FALSE;
     }
+
+    auto result = gnssHal->start();
+    return checkHidlReturn(result, "IGnss start() failed.");
 }
 
 static jboolean android_location_GnssLocationProvider_stop(JNIEnv* /* env */, jobject /* obj */) {
-    if (gnssHal != nullptr) {
-        auto result = gnssHal->stop();
-        if (!result.isOk()) {
-            return JNI_FALSE;
-        } else {
-            return result;
-        }
-    } else {
+    if (gnssHal == nullptr) {
         return JNI_FALSE;
     }
+
+    auto result = gnssHal->stop();
+    return checkHidlReturn(result, "IGnss stop() failed.");
 }
+
 static void android_location_GnssLocationProvider_delete_aiding_data(JNIEnv* /* env */,
                                                                     jobject /* obj */,
                                                                     jint flags) {
-    if (gnssHal != nullptr) {
-        auto result = gnssHal->deleteAidingData(static_cast<IGnss_V1_0::GnssAidingData>(flags));
-        if (!result.isOk()) {
-            ALOGE("Error in deleting aiding data");
-        }
+    if (gnssHal == nullptr) {
+        return;
     }
+
+    auto result = gnssHal->deleteAidingData(static_cast<IGnss_V1_0::GnssAidingData>(flags));
+    checkHidlReturn(result, "IGnss deleteAidingData() failed.");
 }
 
 static void android_location_GnssLocationProvider_agps_set_reference_location_cellid(
@@ -2075,7 +2079,7 @@
     IAGnssRil_V1_0::AGnssRefLocation location;
 
     if (agnssRilIface == nullptr) {
-        ALOGE("No AGPS RIL interface in agps_set_reference_location_cellid");
+        ALOGE("%s: IAGnssRil interface not available.", __func__);
         return;
     }
 
@@ -2094,18 +2098,20 @@
             break;
     }
 
-    agnssRilIface->setRefLocation(location);
+    auto result = agnssRilIface->setRefLocation(location);
+    checkHidlReturn(result, "IAGnssRil setRefLocation() failed.");
 }
 
 static void android_location_GnssLocationProvider_agps_set_id(JNIEnv* env, jobject /* obj */,
                                                              jint type, jstring  setid_string) {
     if (agnssRilIface == nullptr) {
-        ALOGE("no AGPS RIL interface in agps_set_id");
+        ALOGE("%s: IAGnssRil interface not available.", __func__);
         return;
     }
 
     ScopedJniString jniSetId{env, setid_string};
-    agnssRilIface->setSetId((IAGnssRil_V1_0::SetIDType)type, jniSetId);
+    auto result = agnssRilIface->setSetId((IAGnssRil_V1_0::SetIDType)type, jniSetId);
+    checkHidlReturn(result, "IAGnssRil setSetId() failed.");
 }
 
 static jint android_location_GnssLocationProvider_read_nmea(JNIEnv* env, jobject /* obj */,
@@ -2122,12 +2128,12 @@
 
 static void android_location_GnssLocationProvider_inject_time(JNIEnv* /* env */, jobject /* obj */,
         jlong time, jlong timeReference, jint uncertainty) {
-    if (gnssHal != nullptr) {
-        auto result = gnssHal->injectTime(time, timeReference, uncertainty);
-        if (!result.isOk() || !result) {
-            ALOGE("%s: Gnss injectTime() failed", __func__);
-        }
+    if (gnssHal == nullptr) {
+        return;
     }
+
+    auto result = gnssHal->injectTime(time, timeReference, uncertainty);
+    checkHidlReturn(result, "IGnss injectTime() failed.");
 }
 
 static void android_location_GnssLocationProvider_inject_best_location(
@@ -2164,10 +2170,7 @@
                 elapsedRealtimeNanos,
                 elapsedRealtimeUncertaintyNanos);
         auto result = gnssHal_V2_0->injectBestLocation_2_0(location);
-
-        if (!result.isOk() || !result) {
-            ALOGE("%s: Gnss injectBestLocation() failed.", __func__);
-        }
+        checkHidlReturn(result, "IGnss injectBestLocation_2_0() failed.");
         return;
     }
 
@@ -2185,24 +2188,20 @@
                 bearingAccuracyDegrees,
                 timestamp);
         auto result = gnssHal_V1_1->injectBestLocation(location);
-
-        if (!result.isOk() || !result) {
-            ALOGE("%s: Gnss injectBestLocation() failed.", __func__);
-        }
-        return;
+        checkHidlReturn(result, "IGnss injectBestLocation() failed.");
     }
 
-    ALOGE("%s: injectBestLocation() is called but gnssHal_V1_1 is not available.", __func__);
+    ALOGE("IGnss injectBestLocation() is called but gnssHal_V1_1 is not available.");
 }
 
 static void android_location_GnssLocationProvider_inject_location(JNIEnv* /* env */,
         jobject /* obj */, jdouble latitude, jdouble longitude, jfloat accuracy) {
-    if (gnssHal != nullptr) {
-        auto result = gnssHal->injectLocation(latitude, longitude, accuracy);
-        if (!result.isOk() || !result) {
-            ALOGE("%s: Gnss injectLocation() failed", __func__);
-        }
+    if (gnssHal == nullptr) {
+        return;
     }
+
+    auto result = gnssHal->injectLocation(latitude, longitude, accuracy);
+    checkHidlReturn(result, "IGnss injectLocation() failed.");
 }
 
 static jboolean android_location_GnssLocationProvider_supports_psds(
@@ -2213,12 +2212,13 @@
 static void android_location_GnssLocationProvider_inject_psds_data(JNIEnv* env, jobject /* obj */,
         jbyteArray data, jint length) {
     if (gnssXtraIface == nullptr) {
-        ALOGE("XTRA Interface not supported");
+        ALOGE("%s: IGnssXtra interface not available.", __func__);
         return;
     }
 
     jbyte* bytes = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(data, 0));
-    gnssXtraIface->injectXtraData(std::string((const char*)bytes, length));
+    auto result = gnssXtraIface->injectXtraData(std::string((const char*)bytes, length));
+    checkHidlReturn(result, "IGnssXtra injectXtraData() failed.");
     env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
 }
 
@@ -2245,9 +2245,7 @@
     ScopedJniString jniApn{env, apn};
     auto result = agnssIface->dataConnOpen(jniApn,
             static_cast<IAGnss_V1_0::ApnIpType>(apnIpType));
-    if (!result.isOk() || !result){
-        ALOGE("%s: Failed to set APN and its IP type", __func__);
-    }
+    checkHidlReturn(result, "IAGnss dataConnOpen() failed. APN and its IP type not set.");
 }
 
 void AGnssDispatcher::dataConnOpen(sp<IAGnss_V2_0> agnssIface_V2_0, JNIEnv* env,
@@ -2255,25 +2253,19 @@
     ScopedJniString jniApn{env, apn};
     auto result = agnssIface_V2_0->dataConnOpen(static_cast<uint64_t>(networkHandle), jniApn,
             static_cast<IAGnss_V2_0::ApnIpType>(apnIpType));
-    if (!result.isOk() || !result){
-        ALOGE("%s: Failed to set APN and its IP type", __func__);
-    }
+    checkHidlReturn(result, "IAGnss 2.0 dataConnOpen() failed. APN and its IP type not set.");
 }
 
 template<class T>
 void AGnssDispatcher::dataConnClosed(sp<T> agnssIface) {
     auto result = agnssIface->dataConnClosed();
-    if (!result.isOk() || !result) {
-        ALOGE("%s: Failed to close AGnss data connection", __func__);
-    }
+    checkHidlReturn(result, "IAGnss dataConnClosed() failed.");
 }
 
 template<class T>
 void AGnssDispatcher::dataConnFailed(sp<T> agnssIface) {
     auto result = agnssIface->dataConnFailed();
-    if (!result.isOk() || !result) {
-        ALOGE("%s: Failed to notify unavailability of AGnss data connection", __func__);
-    }
+    checkHidlReturn(result, "IAGnss dataConnFailed() failed.");
 }
 
 template <class T, class U>
@@ -2282,9 +2274,7 @@
     ScopedJniString jniHostName{env, hostname};
     auto result = agnssIface->setServer(static_cast<typename U::AGnssType>(type),
             jniHostName, port);
-    if (!result.isOk() || !result) {
-        ALOGE("%s: Failed to set AGnss host name and port", __func__);
-    }
+    checkHidlReturn(result, "IAGnss setServer() failed. Host name and port not set.");
 }
 
 static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_open(
@@ -2299,7 +2289,7 @@
     } else if (agnssIface != nullptr) {
         AGnssDispatcher::dataConnOpen(agnssIface, env, apn, apnIpType);
     } else {
-        ALOGE("%s: AGPS interface not supported", __func__);
+        ALOGE("%s: IAGnss interface not available.", __func__);
         return;
     }
 }
@@ -2311,7 +2301,7 @@
     } else if (agnssIface != nullptr) {
         AGnssDispatcher::dataConnClosed(agnssIface);
     } else {
-        ALOGE("%s: AGPS interface not supported", __func__);
+        ALOGE("%s: IAGnss interface not available.", __func__);
         return;
     }
 }
@@ -2323,7 +2313,7 @@
     } else if (agnssIface != nullptr) {
         AGnssDispatcher::dataConnFailed(agnssIface);
     } else {
-        ALOGE("%s: AGPS interface not supported", __func__);
+        ALOGE("%s: IAGnss interface not available.", __func__);
         return;
     }
 }
@@ -2337,26 +2327,30 @@
         AGnssDispatcher::setServer<IAGnss_V1_0, IAGnssCallback_V1_0>(agnssIface, env, type,
                 hostname, port);
     } else {
-        ALOGE("%s: AGPS interface not supported", __func__);
+        ALOGE("%s: IAGnss interface not available.", __func__);
         return;
     }
 }
 
 static void android_location_GnssLocationProvider_send_ni_response(JNIEnv* /* env */,
-      jobject /* obj */, jint notifId, jint response) {
+        jobject /* obj */, jint notifId, jint response) {
     if (gnssNiIface == nullptr) {
-        ALOGE("no NI interface in send_ni_response");
+        ALOGE("%s: IGnssNi interface not available.", __func__);
         return;
     }
 
-    gnssNiIface->respond(notifId, static_cast<IGnssNiCallback::GnssUserResponseType>(response));
+    auto result = gnssNiIface->respond(notifId,
+            static_cast<IGnssNiCallback::GnssUserResponseType>(response));
+    checkHidlReturn(result, "IGnssNi respond() failed.");
 }
 
-const IGnssDebug_V1_0::SatelliteData& getSatelliteData(const hidl_vec<IGnssDebug_V1_0::SatelliteData>& satelliteDataArray, size_t i) {
+const IGnssDebug_V1_0::SatelliteData& getSatelliteData(
+        const hidl_vec<IGnssDebug_V1_0::SatelliteData>& satelliteDataArray, size_t i) {
     return satelliteDataArray[i];
 }
 
-const IGnssDebug_V1_0::SatelliteData& getSatelliteData(const hidl_vec<IGnssDebug_V2_0::SatelliteData>& satelliteDataArray, size_t i) {
+const IGnssDebug_V1_0::SatelliteData& getSatelliteData(
+        const hidl_vec<IGnssDebug_V2_0::SatelliteData>& satelliteDataArray, size_t i) {
     return satelliteDataArray[i].v1_0;
 }
 
@@ -2424,7 +2418,7 @@
 
 static jstring android_location_GnssLocationProvider_get_internal_state(JNIEnv* env,
                                                                        jobject /* obj */) {
-    jstring result = nullptr;
+    jstring internalStateStr = nullptr;
     /*
      * TODO: Create a jobject to represent GnssDebug.
      */
@@ -2432,21 +2426,27 @@
     std::stringstream internalState;
 
     if (gnssDebugIface == nullptr) {
-        internalState << "Gnss Debug Interface not available"  << std::endl;
+        ALOGE("%s: IGnssDebug interface not available.", __func__);
     } else if (gnssDebugIface_V2_0 != nullptr) {
         IGnssDebug_V2_0::DebugData data;
-        gnssDebugIface_V2_0->getDebugData_2_0([&data](const IGnssDebug_V2_0::DebugData& debugData) {
-            data = debugData;
-        });
-        result = parseDebugData(env, internalState, data);
+        auto result = gnssDebugIface_V2_0->getDebugData_2_0(
+                [&data](const IGnssDebug_V2_0::DebugData& debugData) {
+                    data = debugData;
+                });
+        if (checkHidlReturn(result, "IGnssDebug getDebugData_2_0() failed.")) {
+            internalStateStr = parseDebugData(env, internalState, data);
+        }
     } else {
         IGnssDebug_V1_0::DebugData data;
-        gnssDebugIface->getDebugData([&data](const IGnssDebug_V1_0::DebugData& debugData) {
-            data = debugData;
-        });
-        result = parseDebugData(env, internalState, data);
+        auto result = gnssDebugIface->getDebugData(
+                [&data](const IGnssDebug_V1_0::DebugData& debugData) {
+                    data = debugData;
+                });
+        if (checkHidlReturn(result, "IGnssDebug getDebugData() failed.")) {
+            internalStateStr = parseDebugData(env, internalState, data);
+        }
     }
-    return result;
+    return internalStateStr;
 }
 
 static jboolean android_location_GnssLocationProvider_is_gnss_visibility_control_supported(
@@ -2473,26 +2473,20 @@
         };
 
         auto result = agnssRilIface_V2_0->updateNetworkState_2_0(networkAttributes);
-        if (!result.isOk() || !result) {
-            ALOGE("updateNetworkState_2_0 failed");
-        }
+        checkHidlReturn(result, "IAGnssRil updateNetworkState_2_0() failed.");
     } else if (agnssRilIface != nullptr) {
         ScopedJniString jniApn{env, apn};
         hidl_string hidlApn{jniApn};
         auto result = agnssRilIface->updateNetworkState(connected,
                 static_cast<IAGnssRil_V1_0::NetworkType>(type), roaming);
-        if (!result.isOk() || !result) {
-            ALOGE("updateNetworkState failed");
-        }
+        checkHidlReturn(result, "IAGnssRil updateNetworkState() failed.");
 
         if (!hidlApn.empty()) {
             result = agnssRilIface->updateNetworkAvailability(available, hidlApn);
-            if (!result.isOk() || !result) {
-                ALOGE("updateNetworkAvailability failed");
-            }
+            checkHidlReturn(result, "IAGnssRil updateNetworkAvailability() failed.");
         }
     } else {
-        ALOGE("AGnssRilInterface does not exist");
+        ALOGE("%s: IAGnssRil interface not available.", __func__);
     }
 }
 
@@ -2505,49 +2499,49 @@
         jobject /* obj */, jint geofenceId, jdouble latitude, jdouble longitude, jdouble radius,
         jint last_transition, jint monitor_transition, jint notification_responsiveness,
         jint unknown_timer) {
-    if (gnssGeofencingIface != nullptr) {
-        auto result = gnssGeofencingIface->addGeofence(
-                geofenceId, latitude, longitude, radius,
-                static_cast<IGnssGeofenceCallback::GeofenceTransition>(last_transition),
-                monitor_transition, notification_responsiveness, unknown_timer);
-        return boolToJbool(result.isOk());
-    } else {
-        ALOGE("Geofence Interface not available");
+    if (gnssGeofencingIface == nullptr) {
+        ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+        return JNI_FALSE;
     }
-    return JNI_FALSE;
+
+    auto result = gnssGeofencingIface->addGeofence(
+            geofenceId, latitude, longitude, radius,
+            static_cast<IGnssGeofenceCallback::GeofenceTransition>(last_transition),
+            monitor_transition, notification_responsiveness, unknown_timer);
+    return checkHidlReturn(result, "IGnssGeofencing addGeofence() failed.");
 }
 
 static jboolean android_location_GnssGeofenceProvider_remove_geofence(JNIEnv* /* env */,
         jobject /* obj */, jint geofenceId) {
-    if (gnssGeofencingIface != nullptr) {
-        auto result = gnssGeofencingIface->removeGeofence(geofenceId);
-        return boolToJbool(result.isOk());
-    } else {
-        ALOGE("Geofence interface not available");
+    if (gnssGeofencingIface == nullptr) {
+        ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+        return JNI_FALSE;
     }
-    return JNI_FALSE;
+
+    auto result = gnssGeofencingIface->removeGeofence(geofenceId);
+    return checkHidlReturn(result, "IGnssGeofencing removeGeofence() failed.");
 }
 
 static jboolean android_location_GnssGeofenceProvider_pause_geofence(JNIEnv* /* env */,
         jobject /* obj */, jint geofenceId) {
-    if (gnssGeofencingIface != nullptr) {
-        auto result = gnssGeofencingIface->pauseGeofence(geofenceId);
-        return boolToJbool(result.isOk());
-    } else {
-        ALOGE("Geofence interface not available");
+    if (gnssGeofencingIface == nullptr) {
+        ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+        return JNI_FALSE;
     }
-    return JNI_FALSE;
+
+    auto result = gnssGeofencingIface->pauseGeofence(geofenceId);
+    return checkHidlReturn(result, "IGnssGeofencing pauseGeofence() failed.");
 }
 
 static jboolean android_location_GnssGeofenceProvider_resume_geofence(JNIEnv* /* env */,
         jobject /* obj */, jint geofenceId, jint monitor_transition) {
-    if (gnssGeofencingIface != nullptr) {
-        auto result = gnssGeofencingIface->resumeGeofence(geofenceId, monitor_transition);
-        return boolToJbool(result.isOk());
-    } else {
-        ALOGE("Geofence interface not available");
+    if (gnssGeofencingIface == nullptr) {
+        ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+        return JNI_FALSE;
     }
-    return JNI_FALSE;
+
+    auto result = gnssGeofencingIface->resumeGeofence(geofenceId, monitor_transition);
+    return checkHidlReturn(result, "IGnssGeofencing resumeGeofence() failed.");
 }
 
 static jboolean android_location_GnssMeasurementsProvider_is_measurement_supported(
@@ -2564,12 +2558,12 @@
         jobject /* obj */,
         jboolean enableFullTracking) {
     if (gnssMeasurementIface == nullptr) {
-        ALOGE("GNSS Measurement interface is not available.");
+        ALOGE("%s: IGnssMeasurement interface not available.", __func__);
         return JNI_FALSE;
     }
 
     sp<GnssMeasurementCallback> cbIface = new GnssMeasurementCallback();
-    IGnssMeasurement_V1_0::GnssMeasurementStatus result =
+    Return<IGnssMeasurement_V1_0::GnssMeasurementStatus> result =
             IGnssMeasurement_V1_0::GnssMeasurementStatus::ERROR_GENERIC;
     if (gnssMeasurementIface_V2_0 != nullptr) {
         result = gnssMeasurementIface_V2_0->setCallback_2_0(cbIface, enableFullTracking);
@@ -2578,14 +2572,20 @@
     } else {
         if (enableFullTracking == JNI_TRUE) {
             // full tracking mode not supported in 1.0 HAL
+            result.assertOk(); // isOk() must be called before result destructor is invoked.
             return JNI_FALSE;
         }
         result = gnssMeasurementIface->setCallback(cbIface);
     }
 
-    if (result != IGnssMeasurement_V1_0::GnssMeasurementStatus::SUCCESS) {
+    if (!checkHidlReturn(result, "IGnssMeasurement setCallback() failed.")) {
+        return JNI_FALSE;
+    }
+
+    IGnssMeasurement_V1_0::GnssMeasurementStatus initRet = result;
+    if (initRet != IGnssMeasurement_V1_0::GnssMeasurementStatus::SUCCESS) {
         ALOGE("An error has been found on GnssMeasurementInterface::init, status=%d",
-              static_cast<int32_t>(result));
+              static_cast<int32_t>(initRet));
         return JNI_FALSE;
     } else {
         ALOGD("gnss measurement infc has been enabled");
@@ -2598,12 +2598,12 @@
         JNIEnv* env,
         jobject obj) {
     if (gnssMeasurementIface == nullptr) {
-        ALOGE("Measurement interface not available");
+        ALOGE("%s: IGnssMeasurement interface not available.", __func__);
         return JNI_FALSE;
     }
 
     auto result = gnssMeasurementIface->close();
-    return boolToJbool(result.isOk());
+    return checkHidlReturn(result, "IGnssMeasurement close() failed.");
 }
 
 static jboolean
@@ -2718,8 +2718,8 @@
         .satCorrections = list,
     };
 
-    gnssCorrectionsIface->setCorrections(measurementCorrections);
-    return JNI_TRUE;
+    auto result = gnssCorrectionsIface->setCorrections(measurementCorrections);
+    return checkHidlReturn(result, "IMeasurementCorrections setCorrections() failed.");
 }
 
 static jboolean android_location_GnssNavigationMessageProvider_is_navigation_message_supported(
@@ -2735,17 +2735,20 @@
         JNIEnv* env,
         jobject obj) {
     if (gnssNavigationMessageIface == nullptr) {
-        ALOGE("Navigation Message interface is not available.");
+        ALOGE("%s: IGnssNavigationMessage interface not available.", __func__);
         return JNI_FALSE;
     }
 
     sp<IGnssNavigationMessageCallback> gnssNavigationMessageCbIface =
             new GnssNavigationMessageCallback();
-    IGnssNavigationMessage::GnssNavigationMessageStatus result =
-            gnssNavigationMessageIface->setCallback(gnssNavigationMessageCbIface);
+    auto result = gnssNavigationMessageIface->setCallback(gnssNavigationMessageCbIface);
+    if (!checkHidlReturn(result, "IGnssNavigationMessage setCallback() failed.")) {
+        return JNI_FALSE;
+    }
 
-    if (result != IGnssNavigationMessage::GnssNavigationMessageStatus::SUCCESS) {
-        ALOGE("An error has been found in %s: %d", __FUNCTION__, static_cast<int32_t>(result));
+    IGnssNavigationMessage::GnssNavigationMessageStatus initRet = result;
+    if (initRet != IGnssNavigationMessage::GnssNavigationMessageStatus::SUCCESS) {
+        ALOGE("An error has been found in %s: %d", __FUNCTION__, static_cast<int32_t>(initRet));
         return JNI_FALSE;
     }
 
@@ -2756,43 +2759,35 @@
         JNIEnv* env,
         jobject obj) {
     if (gnssNavigationMessageIface == nullptr) {
-        ALOGE("Navigation Message interface is not available.");
+        ALOGE("%s: IGnssNavigationMessage interface not available.", __func__);
         return JNI_FALSE;
     }
 
     auto result = gnssNavigationMessageIface->close();
-    return boolToJbool(result.isOk());
+    return checkHidlReturn(result, "IGnssNavigationMessage close() failed.");
 }
 
 static jboolean android_location_GnssConfiguration_set_emergency_supl_pdn(JNIEnv*,
                                                                           jobject,
                                                                           jint emergencySuplPdn) {
     if (gnssConfigurationIface == nullptr) {
-        ALOGE("no GNSS configuration interface available");
+        ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
 
     auto result = gnssConfigurationIface->setEmergencySuplPdn(emergencySuplPdn);
-    if (result.isOk()) {
-        return result;
-    } else {
-        return JNI_FALSE;
-    }
+    return checkHidlReturn(result, "IGnssConfiguration setEmergencySuplPdn() failed.");
 }
 
 static jboolean android_location_GnssConfiguration_set_supl_version(JNIEnv*,
                                                                     jobject,
                                                                     jint version) {
     if (gnssConfigurationIface == nullptr) {
-        ALOGE("no GNSS configuration interface available");
+        ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
     auto result = gnssConfigurationIface->setSuplVersion(version);
-    if (result.isOk()) {
-        return result;
-    } else {
-        return JNI_FALSE;
-    }
+    return checkHidlReturn(result, "IGnssConfiguration setSuplVersion() failed.");
 }
 
 static jboolean android_location_GnssConfiguration_set_supl_es(JNIEnv*,
@@ -2804,32 +2799,24 @@
     }
 
     if (gnssConfigurationIface == nullptr) {
-        ALOGE("no GNSS configuration interface available");
+        ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
 
     auto result = gnssConfigurationIface->setSuplEs(suplEs);
-    if (result.isOk()) {
-        return result;
-    } else {
-        return JNI_FALSE;
-    }
+    return checkHidlReturn(result, "IGnssConfiguration setSuplEs() failed.");
 }
 
 static jboolean android_location_GnssConfiguration_set_supl_mode(JNIEnv*,
                                                                  jobject,
                                                                  jint mode) {
     if (gnssConfigurationIface == nullptr) {
-        ALOGE("no GNSS configuration interface available");
+        ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
 
     auto result = gnssConfigurationIface->setSuplMode(mode);
-    if (result.isOk()) {
-        return result;
-    } else {
-        return JNI_FALSE;
-    }
+    return checkHidlReturn(result, "IGnssConfiguration setSuplMode() failed.");
 }
 
 static jboolean android_location_GnssConfiguration_set_gps_lock(JNIEnv*,
@@ -2841,55 +2828,42 @@
     }
 
     if (gnssConfigurationIface == nullptr) {
-        ALOGE("no GNSS configuration interface available");
+        ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
 
     auto result = gnssConfigurationIface->setGpsLock(gpsLock);
-    if (result.isOk()) {
-        return result;
-    } else {
-        return JNI_FALSE;
-    }
+    return checkHidlReturn(result, "IGnssConfiguration setGpsLock() failed.");
 }
 
 static jboolean android_location_GnssConfiguration_set_lpp_profile(JNIEnv*,
                                                                    jobject,
                                                                    jint lppProfile) {
     if (gnssConfigurationIface == nullptr) {
-        ALOGE("no GNSS configuration interface available");
+        ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
 
     auto result = gnssConfigurationIface->setLppProfile(lppProfile);
-
-    if (result.isOk()) {
-        return result;
-    } else {
-        return JNI_FALSE;
-    }
+    return checkHidlReturn(result, "IGnssConfiguration setLppProfile() failed.");
 }
 
 static jboolean android_location_GnssConfiguration_set_gnss_pos_protocol_select(JNIEnv*,
                                                                             jobject,
                                                                             jint gnssPosProtocol) {
     if (gnssConfigurationIface == nullptr) {
-        ALOGE("no GNSS configuration interface available");
+        ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
 
     auto result = gnssConfigurationIface->setGlonassPositioningProtocol(gnssPosProtocol);
-    if (result.isOk()) {
-        return result;
-    } else {
-        return JNI_FALSE;
-    }
+    return checkHidlReturn(result, "IGnssConfiguration setGlonassPositioningProtocol() failed.");
 }
 
 static jboolean android_location_GnssConfiguration_set_satellite_blacklist(
         JNIEnv* env, jobject, jintArray constellations, jintArray sv_ids) {
     if (gnssConfigurationIface_V1_1 == nullptr) {
-        ALOGI("No GNSS Satellite Blacklist interface available");
+        ALOGI("IGnssConfiguration interface does not support satellite blacklist.");
         return JNI_FALSE;
     }
 
@@ -2920,17 +2894,13 @@
     }
 
     auto result = gnssConfigurationIface_V1_1->setBlacklist(sources);
-    if (result.isOk()) {
-        return result;
-    } else {
-        return JNI_FALSE;
-    }
+    return checkHidlReturn(result, "IGnssConfiguration setBlacklist() failed.");
 }
 
 static jboolean android_location_GnssConfiguration_set_es_extension_sec(
         JNIEnv*, jobject, jint emergencyExtensionSeconds) {
     if (gnssConfigurationIface == nullptr) {
-        ALOGE("no GNSS configuration interface available");
+        ALOGE("%s: IGnssConfiguration interface not available.", __func__);
         return JNI_FALSE;
     }
 
@@ -2941,11 +2911,7 @@
     }
 
     auto result = gnssConfigurationIface_V2_0->setEsExtensionSec(emergencyExtensionSeconds);
-    if (result.isOk()) {
-        return result;
-    } else {
-        return JNI_FALSE;
-    }
+    return checkHidlReturn(result, "IGnssConfiguration setEsExtensionSec() failed.");
 }
 
 static jint android_location_GnssBatchingProvider_get_batch_size(JNIEnv*, jclass) {
@@ -2953,20 +2919,22 @@
         return 0; // batching not supported, size = 0
     }
     auto result = gnssBatchingIface->getBatchSize();
-    if (result.isOk()) {
-        return static_cast<jint>(result);
-    } else {
+    if (!checkHidlReturn(result, "IGnssBatching getBatchSize() failed.")) {
         return 0; // failure in binder, don't support batching
     }
+
+    return static_cast<jint>(result);
 }
 
 static jboolean android_location_GnssBatchingProvider_init_batching(JNIEnv*, jclass) {
     if (gnssBatchingIface_V2_0 != nullptr) {
         sp<IGnssBatchingCallback_V2_0> gnssBatchingCbIface_V2_0 = new GnssBatchingCallback_V2_0();
-        return static_cast<jboolean>(gnssBatchingIface_V2_0->init_2_0(gnssBatchingCbIface_V2_0));
+        auto result = gnssBatchingIface_V2_0->init_2_0(gnssBatchingCbIface_V2_0);
+        return checkHidlReturn(result, "IGnssBatching init_2_0() failed.");
     } else if (gnssBatchingIface != nullptr) {
         sp<IGnssBatchingCallback_V1_0> gnssBatchingCbIface_V1_0 = new GnssBatchingCallback_V1_0();
-        return static_cast<jboolean>(gnssBatchingIface->init(gnssBatchingCbIface_V1_0));
+        auto result = gnssBatchingIface->init(gnssBatchingCbIface_V1_0);
+        return checkHidlReturn(result, "IGnssBatching init() failed.");
     } else {
         return JNI_FALSE; // batching not supported
     }
@@ -2976,7 +2944,8 @@
     if (gnssBatchingIface == nullptr) {
         return; // batching not supported
     }
-    gnssBatchingIface->cleanup();
+    auto result = gnssBatchingIface->cleanup();
+    checkHidlReturn(result, "IGnssBatching cleanup() failed.");
 }
 
 static jboolean android_location_GnssBatchingProvider_start_batch(JNIEnv*, jclass,
@@ -2993,29 +2962,30 @@
         options.flags = 0;
     }
 
-    return static_cast<jboolean>(gnssBatchingIface->start(options));
+    auto result = gnssBatchingIface->start(options);
+    return checkHidlReturn(result, "IGnssBatching start() failed.");
 }
 
 static void android_location_GnssBatchingProvider_flush_batch(JNIEnv*, jclass) {
     if (gnssBatchingIface == nullptr) {
         return; // batching not supported
     }
-
-    gnssBatchingIface->flush();
+    auto result = gnssBatchingIface->flush();
+    checkHidlReturn(result, "IGnssBatching flush() failed.");
 }
 
 static jboolean android_location_GnssBatchingProvider_stop_batch(JNIEnv*, jclass) {
     if (gnssBatchingIface == nullptr) {
         return JNI_FALSE; // batching not supported
     }
-
-    return gnssBatchingIface->stop();
+    auto result = gnssBatchingIface->stop();
+    return checkHidlReturn(result, "IGnssBatching stop() failed.");
 }
 
 static jboolean android_location_GnssVisibilityControl_enable_nfw_location_access(
         JNIEnv* env, jobject, jobjectArray proxyApps) {
     if (gnssVisibilityControlIface == nullptr) {
-        ALOGI("No GNSS Visibility Control interface available");
+        ALOGI("IGnssVisibilityControl interface not available.");
         return JNI_FALSE;
     }
 
@@ -3028,11 +2998,7 @@
     }
 
     auto result = gnssVisibilityControlIface->enableNfwLocationAccess(hidlProxyApps);
-    if (result.isOk()) {
-        return result;
-    } else {
-        return JNI_FALSE;
-    }
+    return checkHidlReturn(result, "IGnssVisibilityControl enableNfwLocationAccess() failed.");
 }
 
 static const JNINativeMethod sMethods[] = {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c7e1518..48921ea 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4119,9 +4119,8 @@
 
     @Override
     public boolean isSeparateProfileChallengeAllowed(int userHandle) {
-        if (!isCallerWithSystemUid()) {
-            throw new SecurityException("Caller must be system");
-        }
+        enforceSystemCaller("query separate challenge support");
+
         ComponentName profileOwner = getProfileOwner(userHandle);
         // Profile challenge is supported on N or newer release.
         return profileOwner != null &&
@@ -5943,10 +5942,7 @@
     @Override
     public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
             final IBinder response) {
-        // Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers.
-        if (!isCallerWithSystemUid()) {
-            return;
-        }
+        enforceSystemCaller("choose private key alias");
 
         final UserHandle caller = mInjector.binderGetCallingUserHandle();
         // If there is a profile owner, redirect to that; otherwise query the device owner.
@@ -6044,7 +6040,7 @@
      *
      * @param who the device owner or profile owner.
      * @param delegatePackage the name of the delegate package.
-     * @param scopes the list of delegation scopes to be given to the delegate package.
+     * @param scopeList the list of delegation scopes to be given to the delegate package.
      */
     @Override
     public void setDelegatedScopes(ComponentName who, String delegatePackage,
@@ -6677,36 +6673,28 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
-        enforceFullCrossUsersPermission(userId);
+        enforceSystemCaller("report password change");
 
         // Managed Profile password can only be changed when it has a separate challenge.
         if (!isSeparateProfileChallengeEnabled(userId)) {
             enforceNotManagedProfile(userId, "set the active password");
         }
 
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-
         DevicePolicyData policy = getUserData(userId);
 
-        long ident = mInjector.binderClearCallingIdentity();
-        try {
-            synchronized (getLockObject()) {
-                policy.mFailedPasswordAttempts = 0;
-                updatePasswordValidityCheckpointLocked(userId, /* parent */ false);
-                saveSettingsLocked(userId);
-                updatePasswordExpirationsLocked(userId);
-                setExpirationAlarmCheckLocked(mContext, userId, /* parent */ false);
+        synchronized (getLockObject()) {
+            policy.mFailedPasswordAttempts = 0;
+            updatePasswordValidityCheckpointLocked(userId, /* parent */ false);
+            saveSettingsLocked(userId);
+            updatePasswordExpirationsLocked(userId);
+            setExpirationAlarmCheckLocked(mContext, userId, /* parent */ false);
 
-                // Send a broadcast to each profile using this password as its primary unlock.
-                sendAdminCommandForLockscreenPoliciesLocked(
-                        DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
-                        DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userId);
-            }
-            removeCaApprovalsIfNeeded(userId);
-        } finally {
-            mInjector.binderRestoreCallingIdentity(ident);
+            // Send a broadcast to each profile using this password as its primary unlock.
+            sendAdminCommandForLockscreenPoliciesLocked(
+                    DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
+                    DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userId);
         }
+        removeCaApprovalsIfNeeded(userId);
     }
 
     /**
@@ -8787,8 +8775,7 @@
 
     private void ensureCallerPackage(@Nullable String packageName) {
         if (packageName == null) {
-            Preconditions.checkState(isCallerWithSystemUid(),
-                    "Only caller can omit package name");
+            enforceSystemCaller("omit package name");
         } else {
             final int callingUid = mInjector.binderGetCallingUid();
             final int userId = mInjector.userHandleGetCallingUserId();
@@ -9100,10 +9087,8 @@
 
     @Override
     public ComponentName getRestrictionsProvider(int userHandle) {
+        enforceSystemCaller("query the permission provider");
         synchronized (getLockObject()) {
-            if (!isCallerWithSystemUid()) {
-                throw new SecurityException("Only the system can query the permission provider");
-            }
             DevicePolicyData userData = getUserData(userHandle);
             return userData != null ? userData.mRestrictionsProvider : null;
         }
@@ -9368,10 +9353,8 @@
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
         Preconditions.checkStringNotEmpty(packageName, "packageName is null");
-        if (!isCallerWithSystemUid()){
-            throw new SecurityException(
-                    "Only the system can query if an accessibility service is disabled by admin");
-        }
+        enforceSystemCaller("query if an accessibility service is disabled by admin");
+
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
             if (admin == null) {
@@ -9531,10 +9514,8 @@
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
         Preconditions.checkStringNotEmpty(packageName, "packageName is null");
-        if (!isCallerWithSystemUid()) {
-            throw new SecurityException(
-                    "Only the system can query if an input method is disabled by admin");
-        }
+        enforceSystemCaller("query if an input method is disabled by admin");
+
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
             if (admin == null) {
@@ -9591,10 +9572,8 @@
         }
 
         Preconditions.checkStringNotEmpty(packageName, "packageName is null or empty");
-        if (!isCallerWithSystemUid()) {
-            throw new SecurityException(
-                    "Only the system can query if a notification listener service is permitted");
-        }
+        enforceSystemCaller("query if a notification listener service is permitted");
+
         synchronized (getLockObject()) {
             ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
             if (profileOwner == null || profileOwner.permittedNotificationListeners == null) {
@@ -9606,6 +9585,12 @@
         }
     }
 
+    private void enforceSystemCaller(String action) {
+        if (!isCallerWithSystemUid()) {
+            throw new SecurityException("Only the system can " + action);
+        }
+    }
+
     private void maybeSendAdminEnabledBroadcastLocked(int userHandle) {
         DevicePolicyData policyData = getUserData(userHandle);
         if (policyData.mAdminBroadcastPending) {
@@ -10760,9 +10745,7 @@
 
     @Override
     public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) {
-        if (!isCallerWithSystemUid()) {
-            throw new SecurityException("notifyLockTaskModeChanged can only be called by system");
-        }
+        enforceSystemCaller("call notifyLockTaskModeChanged");
         synchronized (getLockObject()) {
             final DevicePolicyData policy = getUserData(userHandle);
 
@@ -12119,8 +12102,7 @@
         final ApplicationInfo ai;
         try {
             ai = mIPackageManager.getApplicationInfo(packageName, 0, userId);
-            final int targetSdkVersion = ai == null ? 0 : ai.targetSdkVersion;
-            return targetSdkVersion;
+            return ai == null ? 0 : ai.targetSdkVersion;
         } catch (RemoteException e) {
             // Shouldn't happen
             return 0;
@@ -12169,8 +12151,7 @@
         Preconditions.checkNotNull(who, "ComponentName is null");
         final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getActiveAdminForUidLocked(who,
-                    mInjector.binderGetCallingUid());
+            ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid());
             if (!TextUtils.equals(admin.shortSupportMessage, message)) {
                 admin.shortSupportMessage = message;
                 saveSettingsLocked(userHandle);
@@ -12189,8 +12170,7 @@
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getActiveAdminForUidLocked(who,
-                    mInjector.binderGetCallingUid());
+            ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid());
             return admin.shortSupportMessage;
         }
     }
@@ -12203,8 +12183,7 @@
         Preconditions.checkNotNull(who, "ComponentName is null");
         final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getActiveAdminForUidLocked(who,
-                    mInjector.binderGetCallingUid());
+            ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid());
             if (!TextUtils.equals(admin.longSupportMessage, message)) {
                 admin.longSupportMessage = message;
                 saveSettingsLocked(userHandle);
@@ -12223,8 +12202,7 @@
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getActiveAdminForUidLocked(who,
-                    mInjector.binderGetCallingUid());
+            ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid());
             return admin.longSupportMessage;
         }
     }
@@ -12235,9 +12213,8 @@
             return null;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
-        if (!isCallerWithSystemUid()) {
-            throw new SecurityException("Only the system can query support message for user");
-        }
+        enforceSystemCaller("query support message for user");
+
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
             if (admin != null) {
@@ -12253,9 +12230,8 @@
             return null;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
-        if (!isCallerWithSystemUid()) {
-            throw new SecurityException("Only the system can query support message for user");
-        }
+        enforceSystemCaller("query support message for user");
+
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
             if (admin != null) {
@@ -12462,10 +12438,8 @@
         if (!mHasFeature) {
             return false;
         }
-        if (!isCallerWithSystemUid()) {
-            throw new SecurityException(
-                    "Only the system can query restricted pkgs for a specific user");
-        }
+        enforceSystemCaller("query restricted pkgs for a specific user");
+
         synchronized (getLockObject()) {
             final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId);
             if (admin != null && admin.meteredDisabledPackages != null) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 47539d3..a04875f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1449,16 +1449,13 @@
             }
             t.traceEnd();
 
-            final boolean useNewTimeServices = true;
-            if (useNewTimeServices) {
-                t.traceBegin("StartTimeDetectorService");
-                try {
-                    mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS);
-                } catch (Throwable e) {
-                    reportWtf("starting StartTimeDetectorService service", e);
-                }
-                t.traceEnd();
+            t.traceBegin("StartTimeDetectorService");
+            try {
+                mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS);
+            } catch (Throwable e) {
+                reportWtf("starting StartTimeDetectorService service", e);
             }
+            t.traceEnd();
 
             if (!isWatch) {
                 t.traceBegin("StartSearchManagerService");
@@ -1656,12 +1653,7 @@
             if (!isWatch && !disableNetworkTime) {
                 t.traceBegin("StartNetworkTimeUpdateService");
                 try {
-                    if (useNewTimeServices) {
-                        networkTimeUpdater = new NewNetworkTimeUpdateService(context);
-                    } else {
-                        networkTimeUpdater = new OldNetworkTimeUpdateService(context);
-                    }
-                    Slog.d(TAG, "Using networkTimeUpdater class=" + networkTimeUpdater.getClass());
+                    networkTimeUpdater = new NetworkTimeUpdateServiceImpl(context);
                     ServiceManager.addService("network_time_update_service", networkTimeUpdater);
                 } catch (Throwable e) {
                     reportWtf("starting NetworkTimeUpdate service", e);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 748c23a..22cd3d3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -25,6 +25,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.RARE_INDEX;
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
 import static org.junit.Assert.assertEquals;
@@ -662,4 +664,86 @@
         assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
         assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
     }
+
+    /** Tests that rare job batching works as expected. */
+    @Test
+    public void testRareJobBatching() {
+        spyOn(mService);
+        doNothing().when(mService).evaluateControllerStatesLocked(any());
+        doNothing().when(mService).noteJobsPending(any());
+        doReturn(true).when(mService).isReadyToBeExecutedLocked(any());
+        advanceElapsedClock(24 * HOUR_IN_MILLIS);
+
+        JobSchedulerService.MaybeReadyJobQueueFunctor maybeQueueFunctor =
+                mService.new MaybeReadyJobQueueFunctor();
+        mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
+        mService.mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = HOUR_IN_MILLIS;
+        mService.mConstants.MIN_CONNECTIVITY_COUNT = 2;
+        mService.mConstants.MIN_READY_JOBS_COUNT = 1;
+
+        JobStatus job = createJobStatus(
+                "testRareJobBatching",
+                createJobInfo().setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY));
+        job.setStandbyBucket(RARE_INDEX);
+
+        // Not enough RARE jobs to run.
+        mService.mPendingJobs.clear();
+        maybeQueueFunctor.reset();
+        for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) {
+            maybeQueueFunctor.accept(job);
+            assertEquals(i + 1, maybeQueueFunctor.forceBatchedCount);
+            assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size());
+            assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed());
+        }
+        maybeQueueFunctor.postProcess();
+        assertEquals(0, mService.mPendingJobs.size());
+
+        // Enough RARE jobs to run.
+        mService.mPendingJobs.clear();
+        maybeQueueFunctor.reset();
+        for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT; ++i) {
+            maybeQueueFunctor.accept(job);
+            assertEquals(i + 1, maybeQueueFunctor.forceBatchedCount);
+            assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size());
+            assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed());
+        }
+        maybeQueueFunctor.postProcess();
+        assertEquals(5, mService.mPendingJobs.size());
+
+        // Not enough RARE jobs to run, but a non-batched job saves the day.
+        mService.mPendingJobs.clear();
+        maybeQueueFunctor.reset();
+        JobStatus activeJob = createJobStatus(
+                "testRareJobBatching",
+                createJobInfo().setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY));
+        activeJob.setStandbyBucket(ACTIVE_INDEX);
+        for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) {
+            maybeQueueFunctor.accept(job);
+            assertEquals(i + 1, maybeQueueFunctor.forceBatchedCount);
+            assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size());
+            assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed());
+        }
+        maybeQueueFunctor.accept(activeJob);
+        maybeQueueFunctor.postProcess();
+        assertEquals(3, mService.mPendingJobs.size());
+
+        // Not enough RARE jobs to run, but an old RARE job saves the day.
+        mService.mPendingJobs.clear();
+        maybeQueueFunctor.reset();
+        JobStatus oldRareJob = createJobStatus("testRareJobBatching", createJobInfo());
+        oldRareJob.setStandbyBucket(RARE_INDEX);
+        final long oldBatchTime = sElapsedRealtimeClock.millis()
+                - 2 * mService.mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS;
+        oldRareJob.setFirstForceBatchedTimeElapsed(oldBatchTime);
+        for (int i = 0; i < mService.mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT / 2; ++i) {
+            maybeQueueFunctor.accept(job);
+            assertEquals(i + 1, maybeQueueFunctor.forceBatchedCount);
+            assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size());
+            assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed());
+        }
+        maybeQueueFunctor.accept(oldRareJob);
+        assertEquals(oldBatchTime, oldRareJob.getFirstForceBatchedTimeElapsed());
+        maybeQueueFunctor.postProcess();
+        assertEquals(3, mService.mPendingJobs.size());
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 823cc66..328e8f4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -59,6 +59,7 @@
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobSchedulerService.Constants;
+import com.android.server.job.JobServiceContext;
 import com.android.server.net.NetworkPolicyManagerInternal;
 
 import org.junit.Before;
@@ -138,22 +139,53 @@
                         DataUnit.MEBIBYTES.toBytes(1))
                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
 
+        final ConnectivityController controller = new ConnectivityController(mService);
+        when(mService.getMaxJobExecutionTimeMs(any()))
+                .thenReturn(JobServiceContext.EXECUTING_TIMESLICE_MILLIS);
+
         // Slow network is too slow
-        assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net,
+        assertFalse(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilities().setLinkUpstreamBandwidthKbps(1)
                         .setLinkDownstreamBandwidthKbps(1), mConstants));
         // Slow downstream
-        assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net,
+        assertFalse(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilities().setLinkUpstreamBandwidthKbps(1024)
                         .setLinkDownstreamBandwidthKbps(1), mConstants));
         // Slow upstream
-        assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net,
+        assertFalse(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilities().setLinkUpstreamBandwidthKbps(1)
                         .setLinkDownstreamBandwidthKbps(1024), mConstants));
         // Fast network looks great
-        assertTrue(ConnectivityController.isSatisfied(createJobStatus(job), net,
+        assertTrue(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilities().setLinkUpstreamBandwidthKbps(1024)
                         .setLinkDownstreamBandwidthKbps(1024), mConstants));
+        // Slow network still good given time
+        assertTrue(controller.isSatisfied(createJobStatus(job), net,
+                createCapabilities().setLinkUpstreamBandwidthKbps(130)
+                        .setLinkDownstreamBandwidthKbps(130), mConstants));
+
+        when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(60_000L);
+
+        // Slow network is too slow
+        assertFalse(controller.isSatisfied(createJobStatus(job), net,
+                createCapabilities().setLinkUpstreamBandwidthKbps(1)
+                        .setLinkDownstreamBandwidthKbps(1), mConstants));
+        // Slow downstream
+        assertFalse(controller.isSatisfied(createJobStatus(job), net,
+                createCapabilities().setLinkUpstreamBandwidthKbps(137)
+                        .setLinkDownstreamBandwidthKbps(1), mConstants));
+        // Slow upstream
+        assertFalse(controller.isSatisfied(createJobStatus(job), net,
+                createCapabilities().setLinkUpstreamBandwidthKbps(1)
+                        .setLinkDownstreamBandwidthKbps(137), mConstants));
+        // Network good enough
+        assertTrue(controller.isSatisfied(createJobStatus(job), net,
+                createCapabilities().setLinkUpstreamBandwidthKbps(137)
+                        .setLinkDownstreamBandwidthKbps(137), mConstants));
+        // Network slightly too slow given reduced time
+        assertFalse(controller.isSatisfied(createJobStatus(job), net,
+                createCapabilities().setLinkUpstreamBandwidthKbps(130)
+                        .setLinkDownstreamBandwidthKbps(130), mConstants));
     }
 
     @Test
@@ -166,21 +198,23 @@
         final JobStatus early = createJobStatus(job, now - 1000, now + 2000);
         final JobStatus late = createJobStatus(job, now - 2000, now + 1000);
 
+        final ConnectivityController controller = new ConnectivityController(mService);
+
         // Uncongested network is whenever
         {
             final Network net = new Network(101);
             final NetworkCapabilities caps = createCapabilities()
                     .addCapability(NET_CAPABILITY_NOT_CONGESTED);
-            assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants));
-            assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
+            assertTrue(controller.isSatisfied(early, net, caps, mConstants));
+            assertTrue(controller.isSatisfied(late, net, caps, mConstants));
         }
 
         // Congested network is more selective
         {
             final Network net = new Network(101);
             final NetworkCapabilities caps = createCapabilities();
-            assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants));
-            assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
+            assertFalse(controller.isSatisfied(early, net, caps, mConstants));
+            assertTrue(controller.isSatisfied(late, net, caps, mConstants));
         }
     }
 
@@ -198,16 +232,18 @@
         final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000);
         final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000);
 
+        final ConnectivityController controller = new ConnectivityController(mService);
+
         // Unmetered network is whenever
         {
             final Network net = new Network(101);
             final NetworkCapabilities caps = createCapabilities()
                     .addCapability(NET_CAPABILITY_NOT_CONGESTED)
                     .addCapability(NET_CAPABILITY_NOT_METERED);
-            assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants));
-            assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
-            assertTrue(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants));
-            assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants));
+            assertTrue(controller.isSatisfied(early, net, caps, mConstants));
+            assertTrue(controller.isSatisfied(late, net, caps, mConstants));
+            assertTrue(controller.isSatisfied(earlyPrefetch, net, caps, mConstants));
+            assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants));
         }
 
         // Metered network is only when prefetching and late
@@ -215,10 +251,10 @@
             final Network net = new Network(101);
             final NetworkCapabilities caps = createCapabilities()
                     .addCapability(NET_CAPABILITY_NOT_CONGESTED);
-            assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants));
-            assertFalse(ConnectivityController.isSatisfied(late, net, caps, mConstants));
-            assertFalse(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants));
-            assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants));
+            assertFalse(controller.isSatisfied(early, net, caps, mConstants));
+            assertFalse(controller.isSatisfied(late, net, caps, mConstants));
+            assertFalse(controller.isSatisfied(earlyPrefetch, net, caps, mConstants));
+            assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants));
         }
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 9f31289..2d70231 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -28,6 +28,7 @@
 import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
 import static com.android.server.job.JobSchedulerService.RARE_INDEX;
 import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -73,6 +74,7 @@
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.JobSchedulerService.Constants;
+import com.android.server.job.JobServiceContext;
 import com.android.server.job.JobStore;
 import com.android.server.job.controllers.QuotaController.ExecutionStats;
 import com.android.server.job.controllers.QuotaController.TimingSession;
@@ -975,6 +977,37 @@
         assertEquals(expectedStats, newStatsRare);
     }
 
+    @Test
+    public void testGetMaxJobExecutionTimeLocked() {
+        mQuotaController.saveTimingSession(0, SOURCE_PACKAGE,
+                createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS),
+                        3 * MINUTE_IN_MILLIS, 5));
+        JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked", 0);
+        job.setStandbyBucket(RARE_INDEX);
+
+        setCharging();
+        assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS,
+                mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+
+        setDischarging();
+        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS,
+                mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+
+        // Top-started job
+        setProcessState(ActivityManager.PROCESS_STATE_TOP);
+        mQuotaController.maybeStartTrackingJobLocked(job, null);
+        mQuotaController.prepareForExecutionLocked(job);
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+        assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS,
+                mQuotaController.getMaxJobExecutionTimeMsLocked((job)));
+        mQuotaController.maybeStopTrackingJobLocked(job, null, false);
+
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+        assertEquals(7 * MINUTE_IN_MILLIS,
+                mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+    }
+
     /**
      * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
      * window.
@@ -1892,6 +1925,16 @@
         assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[RARE_INDEX]);
         assertEquals(0, mQuotaController.getTimingSessionCoalescingDurationMs());
 
+        // Invalid configurations.
+        // In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD
+        mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 2 * MINUTE_IN_MILLIS;
+        mQcConstants.IN_QUOTA_BUFFER_MS = 5 * MINUTE_IN_MILLIS;
+
+        mQcConstants.updateConstants();
+
+        assertTrue(mQuotaController.getInQuotaBufferMs()
+                <= mQuotaController.getAllowedTimePerPeriodMs());
+
         // Test larger than a day. Controller should cap at one day.
         mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS;
         mQcConstants.IN_QUOTA_BUFFER_MS = 25 * HOUR_IN_MILLIS;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 8fbb7f5..efbae2b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -31,9 +31,9 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE;
@@ -166,7 +166,13 @@
         final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
         mStack.mTranslucentActivityWaiting = topActivity;
         mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
-        assertTrue(mActivity.isState(PAUSED));
+        assertTrue(mActivity.isState(STARTED));
+
+        mStack.mTranslucentActivityWaiting = null;
+        topActivity.changeWindowTranslucency(false);
+        mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind non-opaque");
+        mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
+        assertTrue(mActivity.isState(STARTED));
     }
 
     private void ensureActivityConfiguration() {
@@ -442,7 +448,7 @@
         topActivity.changeWindowTranslucency(false);
         mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
         mActivity.makeClientVisible();
-        assertEquals(PAUSED, mActivity.getState());
+        assertEquals(STARTED, mActivity.getState());
     }
 
     @Test
diff --git a/tests/JobSchedulerPerfTests/Android.bp b/tests/JobSchedulerPerfTests/Android.bp
new file mode 100644
index 0000000..2230807
--- /dev/null
+++ b/tests/JobSchedulerPerfTests/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2019 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.
+
+android_test {
+    name: "JobSchedulerPerfTests",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.rules",
+        "apct-perftests-utils",
+        "services",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/tests/JobSchedulerPerfTests/AndroidManifest.xml b/tests/JobSchedulerPerfTests/AndroidManifest.xml
new file mode 100644
index 0000000..39e751c
--- /dev/null
+++ b/tests/JobSchedulerPerfTests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.perftests.job">
+    <uses-sdk
+            android:minSdkVersion="21" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.frameworks.perftests.job"/>
+</manifest>
diff --git a/tests/JobSchedulerPerfTests/AndroidTest.xml b/tests/JobSchedulerPerfTests/AndroidTest.xml
new file mode 100644
index 0000000..ca4b6c8
--- /dev/null
+++ b/tests/JobSchedulerPerfTests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs JobScheduler Performance Tests">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="JobSchedulerPerfTests.apk"/>
+        <option name="cleanup-apks" value="true"/>
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="JobSchedulerPerfTests"/>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.frameworks.perftests.job"/>
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+    </test>
+</configuration>
diff --git a/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java b/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
new file mode 100644
index 0000000..e956be3
--- /dev/null
+++ b/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2019 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.frameworks.perftests.job;
+
+
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.SystemClock;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.PerfManualStatusReporter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.job.JobStore;
+import com.android.server.job.JobStore.JobSet;
+import com.android.server.job.controllers.JobStatus;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class JobStorePerfTests {
+    private static final String SOURCE_PACKAGE = "com.android.frameworks.perftests.job";
+    private static final int SOURCE_USER_ID = 0;
+    private static final int CALLING_UID = 10079;
+
+    private static Context sContext;
+    private static File sTestDir;
+    private static JobStore sJobStore;
+
+    private static List<JobStatus> sFewJobs = new ArrayList<>();
+    private static List<JobStatus> sManyJobs = new ArrayList<>();
+
+    @Rule
+    public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter();
+
+    @BeforeClass
+    public static void setUpOnce() {
+        sContext = InstrumentationRegistry.getTargetContext();
+        sTestDir = new File(sContext.getFilesDir(), "JobStorePerfTests");
+        sJobStore = JobStore.initAndGetForTesting(sContext, sTestDir);
+
+        for (int i = 0; i < 50; i++) {
+            sFewJobs.add(createJobStatus("fewJobs", i));
+        }
+        for (int i = 0; i < 500; i++) {
+            sManyJobs.add(createJobStatus("manyJobs", i));
+        }
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        sTestDir.deleteOnExit();
+    }
+
+    private void runPersistedJobWriting(List<JobStatus> jobList) {
+        final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+
+        long elapsedTimeNs = 0;
+        while (benchmarkState.keepRunning(elapsedTimeNs)) {
+            sJobStore.clear();
+            for (JobStatus job : jobList) {
+                sJobStore.add(job);
+            }
+            sJobStore.waitForWriteToCompleteForTesting(10_000);
+
+            final long startTime = SystemClock.elapsedRealtimeNanos();
+            sJobStore.writeStatusToDiskForTesting();
+            final long endTime = SystemClock.elapsedRealtimeNanos();
+            elapsedTimeNs = endTime - startTime;
+        }
+    }
+
+    @Test
+    public void testPersistedJobWriting_fewJobs() {
+        runPersistedJobWriting(sFewJobs);
+    }
+
+    @Test
+    public void testPersistedJobWriting_manyJobs() {
+        runPersistedJobWriting(sManyJobs);
+    }
+
+    private void runPersistedJobReading(List<JobStatus> jobList, boolean rtcIsGood) {
+        final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+
+        long elapsedTimeNs = 0;
+        while (benchmarkState.keepRunning(elapsedTimeNs)) {
+            sJobStore.clear();
+            for (JobStatus job : jobList) {
+                sJobStore.add(job);
+            }
+            sJobStore.waitForWriteToCompleteForTesting(10_000);
+
+            JobSet jobSet = new JobSet();
+
+            final long startTime = SystemClock.elapsedRealtimeNanos();
+            sJobStore.readJobMapFromDisk(jobSet, rtcIsGood);
+            final long endTime = SystemClock.elapsedRealtimeNanos();
+            elapsedTimeNs = endTime - startTime;
+        }
+    }
+
+    @Test
+    public void testPersistedJobReading_fewJobs_goodRTC() {
+        runPersistedJobReading(sFewJobs, true);
+    }
+
+    @Test
+    public void testPersistedJobReading_fewJobs_badRTC() {
+        runPersistedJobReading(sFewJobs, false);
+    }
+
+    @Test
+    public void testPersistedJobReading_manyJobs_goodRTC() {
+        runPersistedJobReading(sManyJobs, true);
+    }
+
+    @Test
+    public void testPersistedJobReading_manyJobs_badRTC() {
+        runPersistedJobReading(sManyJobs, false);
+    }
+
+    private static JobStatus createJobStatus(String testTag, int jobId) {
+        JobInfo jobInfo = new JobInfo.Builder(jobId,
+                new ComponentName(sContext, "JobStorePerfTestJobService"))
+                .setPersisted(true)
+                .build();
+        return JobStatus.createFromJobInfo(
+                jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+    }
+}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 1f1eac1..db81831 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -21,14 +21,19 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.fail;
+
 import android.Manifest;
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
+import android.text.TextUtils;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -39,8 +44,7 @@
 import com.android.cts.install.lib.Uninstall;
 import com.android.cts.rollback.lib.Rollback;
 import com.android.cts.rollback.lib.RollbackUtils;
-
-import static org.junit.Assert.fail;
+import com.android.internal.R;
 
 import org.junit.After;
 import org.junit.Before;
@@ -59,10 +63,11 @@
 @RunWith(JUnit4.class)
 public class StagedRollbackTest {
 
-    private static final String TAG = "RollbackTest";
     private static final String NETWORK_STACK_CONNECTOR_CLASS =
             "android.net.INetworkStackConnector";
 
+    private static final String MODULE_META_DATA_PACKAGE = getModuleMetadataPackageName();
+
     /**
      * Adopts common shell permissions needed for rollback tests.
      */
@@ -183,6 +188,21 @@
     }
 
     @Test
+    public void installModuleMetadataPackage() throws Exception {
+        resetModuleMetadataPackage();
+        Context context = InstrumentationRegistry.getContext();
+        PackageInfo metadataPackageInfo = context.getPackageManager().getPackageInfo(
+                MODULE_META_DATA_PACKAGE, 0);
+        String metadataApkPath = metadataPackageInfo.applicationInfo.sourceDir;
+        assertThat(metadataApkPath).isNotNull();
+        assertThat(metadataApkPath).isNotEqualTo("");
+
+        runShellCommand("pm install "
+                + "-r --enable-rollback --staged --wait "
+                + metadataApkPath);
+    }
+
+    @Test
     public void assertNetworkStackRollbackAvailable() throws Exception {
         RollbackManager rm = RollbackUtils.getRollbackManager();
         assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
@@ -203,6 +223,20 @@
                         getNetworkStackPackageName())).isNull();
     }
 
+    @Test
+    public void assertModuleMetadataRollbackAvailable() throws Exception {
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+                        MODULE_META_DATA_PACKAGE)).isNotNull();
+    }
+
+    @Test
+    public void assertModuleMetadataRollbackCommitted() throws Exception {
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+                        MODULE_META_DATA_PACKAGE)).isNotNull();
+    }
+
     private String getNetworkStackPackageName() {
         Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS);
         ComponentName comp = intent.resolveSystemService(
@@ -245,4 +279,29 @@
         InstallUtils.processUserData(TestApp.A);
         Uninstall.packages(TestApp.A);
     }
-}
\ No newline at end of file
+
+    @Nullable
+    private static String getModuleMetadataPackageName() {
+        String packageName = InstrumentationRegistry.getContext().getResources().getString(
+                R.string.config_defaultModuleMetadataProvider);
+        if (TextUtils.isEmpty(packageName)) {
+            return null;
+        }
+        return packageName;
+    }
+
+    private void resetModuleMetadataPackage() {
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+
+        assertThat(MODULE_META_DATA_PACKAGE).isNotNull();
+        rm.expireRollbackForPackage(MODULE_META_DATA_PACKAGE);
+
+        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+                MODULE_META_DATA_PACKAGE)).isNull();
+    }
+
+    private void runShellCommand(String cmd) {
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .executeShellCommand(cmd);
+    }
+}
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index ddef4b9..b2e5a62 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -33,6 +33,8 @@
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class StagedRollbackTest extends BaseHostJUnit4Test {
+    private static final int NATIVE_CRASHES_THRESHOLD = 5;
+
     /**
      * Runs the given phase of a test by calling into the device.
      * Throws an exception if the test phase fails.
@@ -83,6 +85,29 @@
         runPhase("testBadApkOnlyConfirmRollback");
     }
 
+    @Test
+    public void testNativeWatchdogTriggersRollback() throws Exception {
+        //Stage install ModuleMetadata package - this simulates a Mainline module update
+        runPhase("installModuleMetadataPackage");
+
+        // Reboot device to activate staged package
+        getDevice().reboot();
+        getDevice().waitForDeviceAvailable();
+
+        runPhase("assertModuleMetadataRollbackAvailable");
+
+        // crash system_server enough times to trigger a rollback
+        crashProcess("system_server", NATIVE_CRASHES_THRESHOLD);
+
+        // Rollback should be committed automatically now
+        // Give time for rollback to be committed
+        assertTrue(getDevice().waitForDeviceNotAvailable(60000));
+        getDevice().waitForDeviceAvailable();
+
+        // verify rollback committed
+        runPhase("assertModuleMetadataRollbackCommitted");
+    }
+
     /**
      * Tests failed network health check triggers watchdog staged rollbacks.
      */
@@ -177,4 +202,18 @@
         getDevice().reboot();
         runPhase("testPreviouslyAbandonedRollbacksCheckUserdataRollback");
     }
+
+    private void crashProcess(String processName, int numberOfCrashes) throws Exception {
+        String pid = "";
+        String lastPid = "invalid";
+        for (int i = 0; i < numberOfCrashes; ++i) {
+            // This condition makes sure before we kill the process, the process is running AND
+            // the last crash was finished.
+            while ("".equals(pid) || lastPid.equals(pid)) {
+                pid = getDevice().executeShellCommand("pidof " + processName);
+            }
+            getDevice().executeShellCommand("kill " + pid);
+            lastPid = pid;
+        }
+    }
 }
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 135bf90..5260b30 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -3,22 +3,6 @@
 //########################################################################
 java_defaults {
     name: "FrameworksNetTests-jni-defaults",
-    static_libs: [
-        "FrameworksNetCommonTests",
-        "frameworks-base-testutils",
-        "framework-protos",
-        "androidx.test.rules",
-        "mockito-target-minus-junit4",
-        "net-tests-utils",
-        "platform-test-annotations",
-        "services.core",
-        "services.net",
-    ],
-    libs: [
-        "android.test.runner",
-        "android.test.base",
-        "android.test.mock",
-    ],
     jni_libs: [
         "ld-android",
         "libartbase",
@@ -44,20 +28,20 @@
         "libnativehelper",
         "libnetdbpf",
         "libnetdutils",
+        "libnetworkstatsfactorytestjni",
         "libpackagelistparser",
         "libpcre2",
         "libprocessgroup",
         "libselinux",
-        "libui",
-        "libutils",
-        "libvndksupport",
         "libtinyxml2",
+        "libui",
         "libunwindstack",
+        "libutils",
         "libutilscallstack",
+        "libvndksupport",
         "libziparchive",
         "libz",
         "netd_aidl_interface-V2-cpp",
-        "libnetworkstatsfactorytestjni",
     ],
 }
 
@@ -68,4 +52,20 @@
     platform_apis: true,
     test_suites: ["device-tests"],
     certificate: "platform",
+    static_libs: [
+        "androidx.test.rules",
+        "FrameworksNetCommonTests",
+        "frameworks-base-testutils",
+        "framework-protos",
+        "mockito-target-minus-junit4",
+        "net-tests-utils",
+        "platform-test-annotations",
+        "services.core",
+        "services.net",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "android.test.mock",
+    ],
 }
diff --git a/tests/net/smoketest/Android.bp b/tests/net/smoketest/Android.bp
index ef1ad2c..84ae2b5 100644
--- a/tests/net/smoketest/Android.bp
+++ b/tests/net/smoketest/Android.bp
@@ -14,4 +14,9 @@
     defaults: ["FrameworksNetTests-jni-defaults"],
     srcs: ["java/SmokeTest.java"],
     test_suites: ["device-tests"],
-}
+    static_libs: [
+        "androidx.test.rules",
+        "mockito-target-minus-junit4",
+        "services.core",
+    ],
+}
\ No newline at end of file