Merge "Keep PluginManager reference to avoid NPE"
diff --git a/Android.bp b/Android.bp
index abeeb43..3095ca5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -536,6 +536,7 @@
         "telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
         "telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
         "telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl",
+        "telephony/java/android/telephony/ICellInfoCallback.aidl",
         "telephony/java/android/telephony/INetworkService.aidl",
         "telephony/java/android/telephony/INetworkServiceCallback.aidl",
         "telephony/java/com/android/ims/internal/IImsCallSession.aidl",
diff --git a/api/current.txt b/api/current.txt
index 99ea60f..b458fac 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -5370,6 +5370,7 @@
     field public static final android.os.Parcelable.Creator<android.app.Notification.Action> CREATOR;
     field public static final int SEMANTIC_ACTION_ARCHIVE = 5; // 0x5
     field public static final int SEMANTIC_ACTION_CALL = 10; // 0xa
+    field public static final int SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION = 11; // 0xb
     field public static final int SEMANTIC_ACTION_DELETE = 4; // 0x4
     field public static final int SEMANTIC_ACTION_MARK_AS_READ = 2; // 0x2
     field public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3; // 0x3
@@ -24464,6 +24465,7 @@
   }
 
   public static final class MediaExtractor.CasInfo {
+    method public byte[] getPrivateData();
     method public android.media.MediaCas.Session getSession();
     method public int getSystemId();
   }
@@ -43555,6 +43557,7 @@
     method public java.lang.String getCountryIso();
     method public int getDataRoaming();
     method public java.lang.CharSequence getDisplayName();
+    method public java.lang.String getGroupUuid();
     method public java.lang.String getIccId();
     method public int getIconTint();
     method public deprecated int getMcc();
@@ -43562,7 +43565,6 @@
     method public deprecated int getMnc();
     method public java.lang.String getMncString();
     method public java.lang.String getNumber();
-    method public int getParentSubId();
     method public int getSimSlotIndex();
     method public int getSubscriptionId();
     method public boolean isEmbedded();
@@ -43596,6 +43598,7 @@
     method public static boolean isValidSubscriptionId(int);
     method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
     method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
+    method public java.lang.String setSubscriptionGroup(int[]);
     method public void setSubscriptionOverrideCongested(int, boolean, long);
     method public void setSubscriptionOverrideUnmetered(int, boolean, long);
     method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
@@ -43727,6 +43730,7 @@
     method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
     method public void listen(android.telephony.PhoneStateListener, int);
+    method public void requestCellInfoUpdate(java.util.concurrent.Executor, android.telephony.TelephonyManager.CellInfoCallback);
     method public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
     method public void sendDialerSpecialCode(java.lang.String);
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
@@ -43829,6 +43833,11 @@
     field public static final java.lang.String VVM_TYPE_OMTP = "vvm_type_omtp";
   }
 
+  public static abstract class TelephonyManager.CellInfoCallback {
+    ctor public TelephonyManager.CellInfoCallback();
+    method public abstract void onCellInfo(java.util.List<android.telephony.CellInfo>);
+  }
+
   public static abstract class TelephonyManager.UssdResponseCallback {
     ctor public TelephonyManager.UssdResponseCallback();
     method public void onReceiveUssdResponse(android.telephony.TelephonyManager, java.lang.String, java.lang.CharSequence);
@@ -54358,6 +54367,9 @@
     method public void show(float, float);
     method public void show(float, float, float, float);
     method public void update();
+    field public static final int SOURCE_BOUND_MAX_IN_SURFACE = 0; // 0x0
+    field public static final int SOURCE_BOUND_MAX_IN_VIEW = 1; // 0x1
+    field public static final int SOURCE_BOUND_MAX_VISIBLE = 2; // 0x2
   }
 
   public static class Magnifier.Builder {
@@ -54368,6 +54380,7 @@
     method public android.widget.Magnifier.Builder setElevation(float);
     method public android.widget.Magnifier.Builder setForcePositionWithinWindowSystemInsetsBounds(boolean);
     method public android.widget.Magnifier.Builder setSize(int, int);
+    method public android.widget.Magnifier.Builder setSourceBounds(int, int, int, int);
     method public android.widget.Magnifier.Builder setZoom(float);
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index 8eb5507..cdd711f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1206,6 +1206,7 @@
   public abstract class PackageManager {
     method public abstract void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
     method public abstract boolean arePermissionsIndividuallyControlled();
+    method public boolean canSuspendPackage(java.lang.String);
     method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
     method public android.content.pm.dex.ArtManager getArtManager();
     method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
@@ -3640,17 +3641,16 @@
 
   public class WifiManager {
     method public void connect(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
+    method public java.util.List<android.net.wifi.WifiConfiguration> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>);
+    method public java.util.List<android.net.wifi.hotspot2.OsuProvider> getMatchingOsuProviders(java.util.List<android.net.wifi.ScanResult>);
     method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
     method public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method public int getWifiApState();
     method public boolean isDeviceToApRttSupported();
     method public boolean isDeviceToDeviceRttSupported();
-    method public boolean isOweSupported();
     method public boolean isPortableHotspotSupported();
     method public boolean isWifiApEnabled();
     method public boolean isWifiScannerSupported();
-    method public boolean isWpa3SaeSupported();
-    method public boolean isWpa3SuiteBSupported();
     method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler);
     method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
     method public boolean startScan(android.os.WorkSource);
@@ -4110,6 +4110,7 @@
   }
 
   public final class PowerManager {
+    method public void dream(long);
     method public int getPowerSaveMode();
     method public boolean setDynamicPowerSavings(boolean, int);
     method public boolean setPowerSaveMode(boolean);
@@ -4784,6 +4785,16 @@
 
 }
 
+package android.service.carrier {
+
+  public abstract class ApnService extends android.app.Service {
+    ctor public ApnService();
+    method public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
+    method public android.os.IBinder onBind(android.content.Intent);
+  }
+
+}
+
 package android.service.euicc {
 
   public final class EuiccProfileInfo implements android.os.Parcelable {
@@ -5697,6 +5708,7 @@
     method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
     method public boolean needsOtaServiceProvisioning();
     method public boolean rebootRadio();
+    method public void requestCellInfoUpdate(android.os.WorkSource, java.util.concurrent.Executor, android.telephony.TelephonyManager.CellInfoCallback);
     method public boolean resetRadioConfig();
     method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
     method public void setCarrierDataEnabled(boolean);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 244974d..5ca5aa3 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -27,6 +27,7 @@
 import "frameworks/base/core/proto/android/bluetooth/enums.proto";
 import "frameworks/base/core/proto/android/os/enums.proto";
 import "frameworks/base/core/proto/android/server/enums.proto";
+import "frameworks/base/core/proto/android/server/location/enums.proto";
 import "frameworks/base/core/proto/android/service/procstats_enum.proto";
 import "frameworks/base/core/proto/android/stats/enums.proto";
 import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
@@ -118,7 +119,7 @@
         ResourceConfigurationChanged resource_configuration_changed = 66;
         BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67;
         BluetoothConnectionStateChanged bluetooth_connection_state_changed = 68;
-        // 69 is blank but need not be.
+        GpsSignalQualityChanged gps_signal_quality_changed = 69;
         UsbConnectorStateChanged usb_connector_state_changed = 70;
         SpeakerImpedanceReported speaker_impedance_reported = 71;
         HardwareFailed hardware_failed = 72;
@@ -148,6 +149,11 @@
         UserRestrictionChanged user_restriction_changed = 96;
         SettingsUIChanged settings_ui_changed = 97;
         ConnectivityStateChanged connectivity_state_changed = 98;
+        // TODO: service state change is very noisy shortly after boot, as well
+        // as at other transitions - coming out of doze, device plugged in, etc.
+        // Consider removing this if it becomes a problem
+        ServiceStateChanged service_state_changed = 99;
+        ServiceLaunchReported service_launch_reported = 100;
     }
 
     // Pulled events will start at field 10000.
@@ -489,7 +495,6 @@
     optional State state = 3;
 }
 
-
 /**
  * Logs when GPS state changes.
  *
@@ -506,6 +511,16 @@
     optional State state = 2;
 }
 
+/**
+ * Logs when GPS signal quality.
+ *
+ * Logged from:
+ *   /frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+ */
+message GpsSignalQualityChanged {
+    optional android.server.location.GpsSignalQualityEnum level = 1;
+}
+
 
 /**
  * Logs when a sync manager sync state changes.
@@ -2138,16 +2153,51 @@
  * Logged in StatsCompanionService.java
  */
 message ConnectivityStateChanged {
-    // Id of the network.
-    optional int32 net_id = 1;
+  // Id of the network.
+  optional int32 net_id = 1;
+
+  enum State {
+    UNKNOWN = 0;
+    CONNECTED = 1;
+    DISCONNECTED = 2;
+  }
+  // Connected state of a network.
+  optional State state = 2;
+}
+
+/**
+ * Logs when a service starts and stops.
+ * Logged from:
+ *   services/core/java/com/android/server/am/ActiveServices.java
+ */
+message ServiceStateChanged {
+
+    optional int32 uid = 1 [(is_uid) = true];
+
+    optional string package_name = 2;
+
+    optional string service_name = 3;
 
     enum State {
-        UNKNOWN = 0;
-        CONNECTED = 1;
-        DISCONNECTED = 2;
+        START = 1;
+        STOP = 2;
     }
-    // Connected state of a network.
-    optional State state = 2;
+
+    optional State state = 4;
+}
+
+/**
+ * Logs when a service is launched.
+ * Logged from:
+ *   services/core/java/com/android/server/am/ActiveServices.java
+ */
+message ServiceLaunchReported {
+
+    optional int32 uid = 1 [(is_uid) = true];
+
+    optional string package_name = 2;
+
+    optional string service_name = 3;
 }
 
 //////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index fcd9a05..8bb704d 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2277,6 +2277,15 @@
     }
 
     @Override
+    public boolean canSuspendPackage(String packageName) {
+        try {
+            return mPM.canSuspendPackageForUser(packageName, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public Bundle getSuspendedPackageAppExtras() {
         final PersistableBundle extras;
         try {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index df37a02..450efdf 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -207,7 +207,8 @@
     private static final int MAX_REPLY_HISTORY = 5;
 
     /**
-     * Maximum numbers of action buttons in a notification.
+     * Maximum number of (generic) action buttons in a notification (contextual action buttons are
+     * handled separately).
      * @hide
      */
     public static final int MAX_ACTION_BUTTONS = 3;
@@ -1421,6 +1422,12 @@
          */
         public static final int SEMANTIC_ACTION_CALL = 10;
 
+        /**
+         * {@code SemanticAction}: Contextual action - dependent on the current notification. E.g.
+         * open a Map application with an address shown in the notification.
+         */
+        public static final int SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION = 11;
+
         private final Bundle mExtras;
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         private Icon mIcon;
@@ -2042,7 +2049,8 @@
                 SEMANTIC_ACTION_UNMUTE,
                 SEMANTIC_ACTION_THUMBS_UP,
                 SEMANTIC_ACTION_THUMBS_DOWN,
-                SEMANTIC_ACTION_CALL
+                SEMANTIC_ACTION_CALL,
+                SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface SemanticAction {}
@@ -4962,6 +4970,18 @@
                     result);
         }
 
+        private static List<Notification.Action> filterOutContextualActions(
+                List<Notification.Action> actions) {
+            List<Notification.Action> nonContextualActions = new ArrayList<>();
+            for (Notification.Action action : actions) {
+                if (action.getSemanticAction()
+                        != Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) {
+                    nonContextualActions.add(action);
+                }
+            }
+            return nonContextualActions;
+        }
+
         private RemoteViews applyStandardTemplateWithActions(int layoutId,
                 StandardTemplateParams p, TemplateBindResult result) {
             RemoteViews big = applyStandardTemplate(layoutId, p, result);
@@ -4970,7 +4990,11 @@
 
             boolean validRemoteInput = false;
 
-            int N = mActions.size();
+            // In the UI contextual actions appear separately from the standard actions, so we
+            // filter them out here.
+            List<Notification.Action> nonContextualActions = filterOutContextualActions(mActions);
+
+            int N = nonContextualActions.size();
             boolean emphazisedMode = mN.fullScreenIntent != null && !p.ambient;
             big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
             if (N > 0) {
@@ -4979,7 +5003,8 @@
                 big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
                 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
                 for (int i=0; i<N; i++) {
-                    Action action = mActions.get(i);
+                    Action action = nonContextualActions.get(i);
+
                     boolean actionHasValidInput = hasValidRemoteInput(action);
                     validRemoteInput |= actionHasValidInput;
 
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index a87ee57..d0eff2e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -276,6 +276,8 @@
             in PersistableBundle appExtras, in PersistableBundle launcherExtras,
             in SuspendDialogInfo dialogInfo, String callingPackage, int userId);
 
+    boolean canSuspendPackageForUser(String packageName, int userId);
+
     boolean isPackageSuspendedForUser(String packageName, int userId);
 
     PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d3e4045..e14b17e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5797,6 +5797,30 @@
     }
 
     /**
+     * Returns whether or not a given package can be suspended via a call to {@link
+     * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+     * SuspendDialogInfo) setPackagesSuspended}. The platform prevents suspending certain critical
+     * packages to keep the device in a functioning state, e.g. the default dialer.
+     * Apps need to hold {@link Manifest.permission#SUSPEND_APPS SUSPEND_APPS} to call this api.
+     *
+     * <p>
+     * Note that this set of critical packages can change with time, so <em>a value of {@code true}
+     * returned by this api does not guarantee that a following call to {@link
+     * #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+     * SuspendDialogInfo) setPackagesSuspended} for the same package will succeed</em>, especially
+     * if considerable time elapsed between the two calls.
+     *
+     * @param packageName The package to check.
+     * @return {@code true} if the given package can be suspended, {@code false} otherwise.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+    public boolean canSuspendPackage(@NonNull String packageName) {
+        throw new UnsupportedOperationException("canSuspendPackage not implemented");
+    }
+
+    /**
      * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
      * @param packageName The name of the package to get the suspended status of.
      * @param userId The user id.
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 2717c4e..a83a33e 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -19,7 +19,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.app.PendingIntent;
-import android.content.Intent;
 import android.os.RemoteException;
 
 import com.android.internal.util.Preconditions;
@@ -49,13 +48,25 @@
      */
     private final ContextHubInfo mAttachedHub;
 
-    private final CloseGuard mCloseGuard = CloseGuard.get();
+    private final CloseGuard mCloseGuard;
 
     private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
 
-    /* package */ ContextHubClient(ContextHubInfo hubInfo) {
+    /*
+     * True if this is a persistent client (i.e. does not have to close the connection when the
+     * resource is freed from the system).
+     */
+    private final boolean mPersistent;
+
+    /* package */ ContextHubClient(ContextHubInfo hubInfo, boolean persistent) {
         mAttachedHub = hubInfo;
-        mCloseGuard.open("close");
+        mPersistent = persistent;
+        if (mPersistent) {
+            mCloseGuard = null;
+        } else {
+            mCloseGuard = CloseGuard.get();
+            mCloseGuard.open("close");
+        }
     }
 
     /**
@@ -88,11 +99,18 @@
      * Closes the connection for this client and the Context Hub Service.
      *
      * When this function is invoked, the messaging associated with this client is invalidated.
-     * All futures messages targeted for this client are dropped at the service.
+     * All futures messages targeted for this client are dropped at the service, and the
+     * ContextHubClient is unregistered from the service.
+     *
+     * If this object has a PendingIntent, i.e. the object was generated via
+     * {@link ContextHubManager.createClient(PendingIntent, ContextHubInfo, long)}, then the
+     * Intent events corresponding to the PendingIntent will no longer be triggered.
      */
     public void close() {
         if (!mIsClosed.getAndSet(true)) {
-            mCloseGuard.close();
+            if (mCloseGuard != null) {
+                mCloseGuard.close();
+            }
             try {
                 mClientProxy.close();
             } catch (RemoteException e) {
@@ -102,72 +120,6 @@
     }
 
     /**
-     * Registers to receive persistent intents for a given nanoapp.
-     *
-     * This method should be used if the caller wants to receive notifications even after the
-     * process exits. The client must have an open connection with the Context Hub Service (i.e. it
-     * cannot have been closed through the {@link #close()} method). Only one PendingIntent can be
-     * registered at a time for a single ContextHubClient, and the PendingIntent cannot be
-     * registered if already registered by a ContextHubClient. If registered successfully, intents
-     * will be delivered regarding events for the specified nanoapp from the attached Context Hub.
-     * Any unicast messages for this client will also be delivered. The intent will have an extra
-     * {@link ContextHubManager.EXTRA_CONTEXT_HUB_INFO} of type {@link ContextHubInfo}, which
-     * describes the Context Hub the intent event was for. The intent will also have an extra
-     * {@link ContextHubManager.EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which
-     * will contain the type of the event. See {@link ContextHubManager.Event} for description of
-     * each event type, along with event-specific extra fields. A client can use
-     * {@link ContextHubIntentEvent.fromIntent(Intent)} to parse the Intent generated by the event.
-     *
-     * When the intent is received, this client can be recreated through
-     * {@link ContextHubManager.createClient(PendingIntent, ContextHubInfo,
-     * ContextHubClientCallback, Exectutor)}. When recreated, the client can be treated as the
-     * same endpoint entity from a nanoapp's perspective, and can be continued to be used to send
-     * messages even if the original process has exited.
-     *
-     * Intents will be delivered until it is unregistered through
-     * {@link #unregisterIntent(PendingIntent)}. Note that the registration of this client will
-     * continued to be maintained at the Context Hub Service until
-     * {@link #unregisterIntent(PendingIntent)} is called for registered intents.
-     *
-     * @param pendingIntent the PendingIntent to register for this client
-     * @param nanoAppId     the unique ID of the nanoapp to receive events for
-     * @return true on success, false otherwise
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public boolean registerIntent(@NonNull PendingIntent pendingIntent, long nanoAppId) {
-        Preconditions.checkNotNull(pendingIntent, "PendingIntent cannot be null");
-
-        try {
-            return mClientProxy.registerIntent(pendingIntent, nanoAppId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Unregisters an intent previously registered via {@link #registerIntent(PendingIntent, long)}.
-     * If this intent has not been registered for this client, this method returns false.
-     *
-     * @param pendingIntent the PendingIntent to unregister
-     *
-     * @return true on success, false otherwise
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public boolean unregisterIntent(@NonNull PendingIntent pendingIntent) {
-        Preconditions.checkNotNull(pendingIntent, "PendingIntent cannot be null");
-
-        try {
-            return mClientProxy.unregisterIntent(pendingIntent);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Sends a message to a nanoapp through the Context Hub Service.
      *
      * This function returns RESULT_SUCCESS if the message has reached the HAL, but
@@ -200,7 +152,9 @@
             if (mCloseGuard != null) {
                 mCloseGuard.warnIfOpen();
             }
-            close();
+            if (!mPersistent) {
+                close();
+            }
         } finally {
             super.finalize();
         }
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
index 36123e3..51daa92 100644
--- a/core/java/android/hardware/location/ContextHubInfo.java
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -15,6 +15,7 @@
  */
 package android.hardware.location;
 
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.hardware.contexthub.V1_0.ContextHub;
 import android.os.Parcel;
@@ -267,6 +268,34 @@
         return retVal;
     }
 
+    @Override
+    public boolean equals(@Nullable Object object) {
+        if (object == this) {
+            return true;
+        }
+
+        boolean isEqual = false;
+        if (object instanceof ContextHubInfo) {
+            ContextHubInfo other = (ContextHubInfo) object;
+            isEqual = (other.getId() == mId)
+                    && other.getName().equals(mName)
+                    && other.getVendor().equals(mVendor)
+                    && other.getToolchain().equals(mToolchain)
+                    && (other.getToolchainVersion() == mToolchainVersion)
+                    && (other.getStaticSwVersion() == getStaticSwVersion())
+                    && (other.getChrePlatformId() == mChrePlatformId)
+                    && (other.getPeakMips() == mPeakMips)
+                    && (other.getStoppedPowerDrawMw() == mStoppedPowerDrawMw)
+                    && (other.getSleepPowerDrawMw() == mSleepPowerDrawMw)
+                    && (other.getPeakPowerDrawMw() == mPeakPowerDrawMw)
+                    && (other.getMaxPacketLengthBytes() == mMaxPacketLengthBytes)
+                    && Arrays.equals(other.getSupportedSensors(), mSupportedSensors)
+                    && Arrays.equals(other.getMemoryRegions(), mMemoryRegions);
+        }
+
+        return isEqual;
+    }
+
     private ContextHubInfo(Parcel in) {
         mId = in.readInt();
         mName = in.readString();
diff --git a/core/java/android/hardware/location/ContextHubIntentEvent.java b/core/java/android/hardware/location/ContextHubIntentEvent.java
index 96e7496..b2aa268 100644
--- a/core/java/android/hardware/location/ContextHubIntentEvent.java
+++ b/core/java/android/hardware/location/ContextHubIntentEvent.java
@@ -16,6 +16,7 @@
 package android.hardware.location;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.Intent;
 
@@ -206,6 +207,37 @@
         return out + "]";
     }
 
+    @Override
+    public boolean equals(@Nullable Object object) {
+        if (object == this) {
+            return true;
+        }
+
+        boolean isEqual = false;
+        if (object instanceof ContextHubIntentEvent) {
+            ContextHubIntentEvent other = (ContextHubIntentEvent) object;
+            if (other.getEventType() == mEventType
+                    && other.getContextHubInfo().equals(mContextHubInfo)) {
+                isEqual = true;
+                try {
+                    if (mEventType != ContextHubManager.EVENT_HUB_RESET) {
+                        isEqual &= (other.getNanoAppId() == mNanoAppId);
+                    }
+                    if (mEventType == ContextHubManager.EVENT_NANOAPP_ABORTED) {
+                        isEqual &= (other.getNanoAppAbortCode() == mNanoAppAbortCode);
+                    }
+                    if (mEventType == ContextHubManager.EVENT_NANOAPP_MESSAGE) {
+                        isEqual &= other.getNanoAppMessage().equals(mNanoAppMessage);
+                    }
+                } catch (UnsupportedOperationException e) {
+                    isEqual = false;
+                }
+            }
+        }
+
+        return isEqual;
+    }
+
     private static void hasExtraOrThrow(Intent intent, String extra) {
         if (!intent.hasExtra(extra)) {
             throw new IllegalArgumentException("Intent did not have extra: " + extra);
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 9acefa5..843db66 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -25,6 +25,7 @@
 import android.annotation.SystemService;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.Looper;
@@ -757,7 +758,7 @@
         Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null");
         Preconditions.checkNotNull(executor, "Executor cannot be null");
 
-        ContextHubClient client = new ContextHubClient(hubInfo);
+        ContextHubClient client = new ContextHubClient(hubInfo, false /* persistent */);
         IContextHubClientCallback clientInterface = createClientCallback(
                 client, callback, executor);
 
@@ -793,43 +794,55 @@
     }
 
     /**
-     * Creates a ContextHubClient based on an Intent received by the Context Hub Service.
+     * Creates a ContextHubClient that will receive notifications based on Intent events.
      *
-     * This method is intended to be used after receiving an Intent received as a result of
-     * {@link ContextHubClient.registerIntent(PendingIntent, long)}, and must have been created
-     * through {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)} or
-     * equivalent at an earlier time.
+     * This method should be used instead of {@link #createClient(ContextHubInfo,
+     * ContextHubClientCallback)} and the equivalent API if the caller wants to preserve the
+     * messaging endpoint of a ContextHubClient, even after a process exits. If the PendingIntent
+     * with the provided nanoapp has already been registered at the service previously, then the
+     * same ContextHubClient will be regenerated without creating a new client connection at the
+     * service. Note that the PendingIntent, nanoapp, and Context Hub must all match in identifying
+     * a previously registered ContextHubClient. If a client is regenerated, it can be treated as
+     * the same endpoint entity from a nanoapp's perspective, and can be continued to be
+     * used to send messages even if the original process has exited.
      *
-     * @param pendingIntent the PendingIntent that has been registered with a client
+     * If registered successfully, intents will be delivered regarding events or messages from the
+     * specified nanoapp from the attached Context Hub. The intent will have an extra
+     * {@link ContextHubManager.EXTRA_CONTEXT_HUB_INFO} of type {@link ContextHubInfo}, which
+     * describes the Context Hub the intent event was for. The intent will also have an extra
+     * {@link ContextHubManager.EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which
+     * will contain the type of the event. See {@link ContextHubManager.Event} for description of
+     * each event type, along with event-specific extra fields. The client can also use
+     * {@link ContextHubIntentEvent.fromIntent(Intent)} to parse the Intent generated by the event.
+     *
+     * Intent events will be delivered until it is unregistered through
+     * {@link ContextHubClient.close()}. Note that the registration of this
+     * ContextHubClient at the Context Hub Service will continued to be maintained until
+     * {@link ContextHubClient.close()} is called.
+     *
      * @param hubInfo       the hub to attach this client to
-     * @param callback      the notification callback to register
-     * @param executor      the executor to invoke the callback
+     * @param pendingIntent the PendingIntent to register to the client
+     * @param nanoAppId     the ID of the nanoapp that Intent events will be generated for
      * @return the registered client object
      *
-     * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or pendingIntent
-     *                                  was not associated with a client
-     * @throws IllegalStateException    if the client is already registered to a valid callback
-     * @throws NullPointerException     if pendingIntent, hubInfo, callback, or executor is null
+     * @throws IllegalArgumentException if hubInfo does not represent a valid hub
+     * @throws IllegalStateException    if there were too many registered clients at the service
+     * @throws NullPointerException     if pendingIntent or hubInfo is null
      *
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     @NonNull public ContextHubClient createClient(
-            @NonNull PendingIntent pendingIntent, @NonNull ContextHubInfo hubInfo,
-            @NonNull ContextHubClientCallback callback,
-            @NonNull @CallbackExecutor Executor executor) {
-        Preconditions.checkNotNull(pendingIntent, "PendingIntent cannot be null");
-        Preconditions.checkNotNull(callback, "Callback cannot be null");
-        Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null");
-        Preconditions.checkNotNull(executor, "Executor cannot be null");
+            @NonNull ContextHubInfo hubInfo, @NonNull PendingIntent pendingIntent, long nanoAppId) {
+        Preconditions.checkNotNull(pendingIntent);
+        Preconditions.checkNotNull(hubInfo);
 
-        ContextHubClient client = new ContextHubClient(hubInfo);
-        IContextHubClientCallback clientInterface = createClientCallback(
-                client, callback, executor);
+        ContextHubClient client = new ContextHubClient(hubInfo, true /* persistent */);
 
         IContextHubClient clientProxy;
         try {
-            clientProxy = mService.bindClient(pendingIntent, clientInterface, hubInfo.getId());
+            clientProxy = mService.createPendingIntentClient(
+                    hubInfo.getId(), pendingIntent, nanoAppId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -839,30 +852,6 @@
     }
 
     /**
-     * Equivalent to {@link #createClient(PendingIntent, ContextHubInfo, ContextHubClientCallback,
-     * Executor)} with the executor using the main thread's Looper.
-     *
-     * @param pendingIntent the PendingIntent that has been registered with a client
-     * @param hubInfo       the hub to attach this client to
-     * @param callback      the notification callback to register
-     * @return the registered client object
-     *
-     * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or pendingIntent
-     *                                  was not associated with a client
-     * @throws IllegalStateException    if the client is already registered to a valid callback
-     * @throws NullPointerException     if pendingIntent, hubInfo, or callback is null
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    @NonNull public ContextHubClient createClient(
-            @NonNull PendingIntent pendingIntent, @NonNull ContextHubInfo hubInfo,
-            @NonNull ContextHubClientCallback callback) {
-        return createClient(
-                pendingIntent, hubInfo, callback, new HandlerExecutor(Handler.getMain()));
-    }
-
-    /**
      * Unregister a callback for receive messages from the context hub.
      *
      * @see Callback
diff --git a/core/java/android/hardware/location/IContextHubClient.aidl b/core/java/android/hardware/location/IContextHubClient.aidl
index b539414..e33545c 100644
--- a/core/java/android/hardware/location/IContextHubClient.aidl
+++ b/core/java/android/hardware/location/IContextHubClient.aidl
@@ -29,10 +29,4 @@
 
     // Closes the connection with the Context Hub
     void close();
-
-    // Registers a PendingIntent with the client
-    boolean registerIntent(in PendingIntent pendingIntent, long nanoAppId);
-
-    // Unregisters a PendingIntent from the client
-    boolean unregisterIntent(in PendingIntent pendingIntent);
 }
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index 9b0acdf..19ed694 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -61,10 +61,9 @@
     // Creates a client to send and receive messages
     IContextHubClient createClient(in IContextHubClientCallback client, int contextHubId);
 
-    // Binds an existing client to a new callback interface, provided a previously registered
-    // PendingIntent
-    IContextHubClient bindClient(
-            in PendingIntent pendingIntent, in IContextHubClientCallback client, int contextHubId);
+    // Creates a PendingIntent-based client to send and receive messages
+    IContextHubClient createPendingIntentClient(
+            int contextHubId, in PendingIntent pendingIntent, long nanoAppId);
 
     // Returns a list of ContextHub objects of available hubs
     List<ContextHubInfo> getContextHubs();
diff --git a/core/java/android/hardware/location/MemoryRegion.java b/core/java/android/hardware/location/MemoryRegion.java
index 857434e..3d9e859 100644
--- a/core/java/android/hardware/location/MemoryRegion.java
+++ b/core/java/android/hardware/location/MemoryRegion.java
@@ -16,6 +16,7 @@
 
 package android.hardware.location;
 
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -106,6 +107,25 @@
     }
 
     @Override
+    public boolean equals(@Nullable Object object) {
+        if (object == this) {
+            return true;
+        }
+
+        boolean isEqual = false;
+        if (object instanceof MemoryRegion) {
+            MemoryRegion other = (MemoryRegion) object;
+            isEqual = (other.getCapacityBytes() == mSizeBytes)
+                    && (other.getFreeCapacityBytes() == mSizeBytesFree)
+                    && (other.isReadable() == mIsReadable)
+                    && (other.isWritable() == mIsWritable)
+                    && (other.isExecutable() == mIsExecutable);
+        }
+
+        return isEqual;
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/hardware/location/NanoAppMessage.java b/core/java/android/hardware/location/NanoAppMessage.java
index 6635258..9f90d59 100644
--- a/core/java/android/hardware/location/NanoAppMessage.java
+++ b/core/java/android/hardware/location/NanoAppMessage.java
@@ -15,10 +15,13 @@
  */
 package android.hardware.location;
 
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Arrays;
+
 /**
  * A class describing messages send to or from nanoapps through the Context Hub Service.
  *
@@ -168,4 +171,22 @@
 
         return ret;
     }
+
+    @Override
+    public boolean equals(@Nullable Object object) {
+        if (object == this) {
+            return true;
+        }
+
+        boolean isEqual = false;
+        if (object instanceof NanoAppMessage) {
+            NanoAppMessage other = (NanoAppMessage) object;
+            isEqual = (other.getNanoAppId() == mNanoAppId)
+                    && (other.getMessageType() == mMessageType)
+                    && (other.isBroadcastMessage() == mIsBroadcasted)
+                    && Arrays.equals(other.getMessageBody(), mMessageBody);
+        }
+
+        return isEqual;
+    }
 }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index a307cd8..1c1db68 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -24,6 +24,7 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.content.Context;
+import android.service.dreams.Sandman;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
@@ -1001,6 +1002,29 @@
     }
 
     /**
+     * Requests the device to start dreaming.
+     * <p>
+     * If dream can not be started, for example if another {@link PowerManager} transition is in
+     * progress, does nothing. Unlike {@link #nap(long)}, this does not put device to sleep when
+     * dream ends.
+     * </p><p>
+     * Requires the {@link android.Manifest.permission#WRITE_DREAM_STATE} permission.
+     * </p>
+     *
+     * @param time The time when the request to nap was issued, in the
+     * {@link SystemClock#uptimeMillis()} time base.  This timestamp may be used to correctly
+     * order the dream request with other power management functions.  It should be set
+     * to the timestamp of the input event that caused the request to dream.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+    public void dream(long time) {
+        Sandman.startDreamByUserRequest(mContext);
+    }
+
+    /**
      * Boosts the brightness of the screen to maximum for a predetermined
      * period of time.  This is used to make the screen more readable in bright
      * daylight for a short duration.
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index f4dcce1..15ded8d 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -154,7 +154,7 @@
 
     // User authenticators.
     public static final int HW_AUTH_PASSWORD = 1 << 0;
-    public static final int HW_AUTH_FINGERPRINT = 1 << 1;
+    public static final int HW_AUTH_BIOMETRIC = 1 << 1;
 
     // Error codes.
     public static final int KM_ERROR_OK = 0;
diff --git a/core/java/android/service/carrier/ApnService.java b/core/java/android/service/carrier/ApnService.java
new file mode 100644
index 0000000..d53eb37
--- /dev/null
+++ b/core/java/android/service/carrier/ApnService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.carrier;
+
+import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
+import android.app.Service;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.telephony.IApnSourceService;
+
+import java.util.List;
+
+/**
+ * A service that the system can call to restore default APNs.
+ * <p>
+ * To extend this class, specify the full name of your implementation in the resource file
+ * {@code packages/providers/TelephonyProvider/res/values/config.xml} as the
+ * {@code apn_source_service}.
+ * </p>
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class ApnService extends Service {
+
+    private static final String LOG_TAG = "ApnService";
+
+    private final IApnSourceService.Stub mBinder = new IApnSourceService.Stub() {
+        /**
+         * Retreive APNs for the default slot index.
+         */
+        @Override
+        public ContentValues[] getApns(int subId) {
+            try {
+                List<ContentValues> apns = ApnService.this.onRestoreApns(subId);
+                return apns.toArray(new ContentValues[apns.size()]);
+            } catch (Exception e) {
+                Log.e(LOG_TAG, "Error in getApns for subId=" + subId + ": " + e.getMessage(), e);
+                return null;
+            }
+        }
+    };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    /**
+     * Override this method to restore default user APNs with a carrier service instead of the
+     * built in platform xml APNs list.
+     * <p>
+     * This method is called by the TelephonyProvider when the user requests restoring the default
+     * APNs. It should return a list of ContentValues representing the default APNs for the given
+     * subId.
+     */
+    @WorkerThread
+    public abstract List<ContentValues> onRestoreApns(int subId);
+}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 2d5f3bf..5504427 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -2372,52 +2372,6 @@
         public Directions(int[] dirs) {
             mDirections = dirs;
         }
-
-        /**
-         * Returns number of BiDi runs.
-         *
-         * @hide
-         */
-        public @IntRange(from = 0) int getRunCount() {
-            return mDirections.length / 2;
-        }
-
-        /**
-         * Returns the start offset of the BiDi run.
-         *
-         * @param runIndex the index of the BiDi run
-         * @return the start offset of the BiDi run.
-         * @hide
-         */
-        public @IntRange(from = 0) int getRunStart(@IntRange(from = 0) int runIndex) {
-            return mDirections[runIndex * 2];
-        }
-
-        /**
-         * Returns the length of the BiDi run.
-         *
-         * Note that this method may return too large number due to reducing the number of object
-         * allocations. The too large number means the remaining part is assigned to this run. The
-         * caller must clamp the returned value.
-         *
-         * @param runIndex the index of the BiDi run
-         * @return the length of the BiDi run.
-         * @hide
-         */
-        public @IntRange(from = 0) int getRunLength(@IntRange(from = 0) int runIndex) {
-            return mDirections[runIndex * 2 + 1] & RUN_LENGTH_MASK;
-        }
-
-        /**
-         * Returns true if the BiDi run is RTL.
-         *
-         * @param runIndex the index of the BiDi run
-         * @return true if the BiDi run is RTL.
-         * @hide
-         */
-        public boolean isRunRtl(int runIndex) {
-            return (mDirections[runIndex * 2 + 1] & RUN_RTL_FLAG) != 0;
-        }
     }
 
     /**
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 6eb433a..44dfd11 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -16,7 +16,6 @@
 
 package android.text;
 
-import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
@@ -52,8 +51,6 @@
 public class TextLine {
     private static final boolean DEBUG = false;
 
-    private static final char TAB_CHAR = '\t';
-
     private TextPaint mPaint;
     @UnsupportedAppUsage
     private CharSequence mText;
@@ -201,7 +198,7 @@
             }
         }
 
-        mCharsValid = hasReplacement;
+        mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
 
         if (mCharsValid) {
             if (mChars == null || mChars.length < mLen) {
@@ -235,10 +232,6 @@
         mEllipsisEnd = ellipsisStart != ellipsisEnd ? ellipsisEnd : 0;
     }
 
-    private char charAt(int i) {
-        return mCharsValid ? mChars[i] : mText.charAt(i + mStart);
-    }
-
     /**
      * Justify the line to the given width.
      */
@@ -268,23 +261,51 @@
      * @param bottom the bottom of the line
      */
     void draw(Canvas c, float x, int top, int y, int bottom) {
+        if (!mHasTabs) {
+            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
+                drawRun(c, 0, mLen, false, x, top, y, bottom, false);
+                return;
+            }
+            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
+                drawRun(c, 0, mLen, true, x, top, y, bottom, false);
+                return;
+            }
+        }
+
         float h = 0;
-        final int runCount = mDirections.getRunCount();
-        for (int runIndex = 0; runIndex < runCount; runIndex++) {
-            final int runStart = mDirections.getRunStart(runIndex);
-            final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen);
-            final boolean runIsRtl = mDirections.isRunRtl(runIndex);
+        int[] runs = mDirections.mDirections;
 
-            int segStart = runStart;
+        int lastRunIndex = runs.length - 2;
+        for (int i = 0; i < runs.length; i += 2) {
+            int runStart = runs[i];
+            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
+            if (runLimit > mLen) {
+                runLimit = mLen;
+            }
+            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
+
+            int segstart = runStart;
             for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
-                if (j == runLimit || charAt(j) == TAB_CHAR) {
-                    h += drawRun(c, segStart, j, runIsRtl, x + h, top, y, bottom,
-                            runIndex != (runCount - 1) || j != mLen);
+                int codept = 0;
+                if (mHasTabs && j < runLimit) {
+                    codept = mChars[j];
+                    if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) {
+                        codept = Character.codePointAt(mChars, j);
+                        if (codept > 0xFFFF) {
+                            ++j;
+                            continue;
+                        }
+                    }
+                }
 
-                    if (j != runLimit) {  // charAt(j) == TAB_CHAR
+                if (j == runLimit || codept == '\t') {
+                    h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom,
+                            i != lastRunIndex || j != mLen);
+
+                    if (codept == '\t') {
                         h = mDir * nextTab(h * mDir);
                     }
-                    segStart = j + 1;
+                    segstart = j + 1;
                 }
             }
         }
@@ -302,81 +323,75 @@
     }
 
     /**
-     * Returns the signed graphical offset from the leading margin.
+     * Returns information about a position on the line.
      *
-     * Following examples are all for measuring offset=3. LX(e.g. L0, L1, ...) denotes a
-     * character which has LTR BiDi property. On the other hand, RX(e.g. R0, R1, ...) denotes a
-     * character which has RTL BiDi property. Assuming all character has 1em width.
-     *
-     * Example 1: All LTR chars within LTR context
-     *   Input Text (logical)  :   L0 L1 L2 L3 L4 L5 L6 L7 L8
-     *   Input Text (visual)   :   L0 L1 L2 L3 L4 L5 L6 L7 L8
-     *   Output(trailing=true) :  |--------| (Returns 3em)
-     *   Output(trailing=false):  |--------| (Returns 3em)
-     *
-     * Example 2: All RTL chars within RTL context.
-     *   Input Text (logical)  :   R0 R1 R2 R3 R4 R5 R6 R7 R8
-     *   Input Text (visual)   :   R8 R7 R6 R5 R4 R3 R2 R1 R0
-     *   Output(trailing=true) :                    |--------| (Returns -3em)
-     *   Output(trailing=false):                    |--------| (Returns -3em)
-     *
-     * Example 3: BiDi chars within LTR context.
-     *   Input Text (logical)  :   L0 L1 L2 R3 R4 R5 L6 L7 L8
-     *   Input Text (visual)   :   L0 L1 L2 R5 R4 R3 L6 L7 L8
-     *   Output(trailing=true) :  |-----------------| (Returns 6em)
-     *   Output(trailing=false):  |--------| (Returns 3em)
-     *
-     * Example 4: BiDi chars within RTL context.
-     *   Input Text (logical)  :   L0 L1 L2 R3 R4 R5 L6 L7 L8
-     *   Input Text (visual)   :   L6 L7 L8 R5 R4 R3 L0 L1 L2
-     *   Output(trailing=true) :           |-----------------| (Returns -6em)
-     *   Output(trailing=false):                    |--------| (Returns -3em)
-     *
-     * @param offset the line-relative character offset, between 0 and the line length, inclusive
-     * @param trailing no effect if the offset is not on the BiDi transition offset. If the offset
-     *                 is on the BiDi transition offset and true is passed, the offset is regarded
-     *                 as the edge of the trailing run's edge. If false, the offset is regarded as
-     *                 the edge of the preceding run's edge. See example above.
-     * @param fmi receives metrics information about the requested character, can be null
-     * @return the signed graphical offset from the leading margin to the requested character edge.
-     *         The positive value means the offset is right from the leading edge. The negative
-     *         value means the offset is left from the leading edge.
+     * @param offset the line-relative character offset, between 0 and the
+     * line length, inclusive
+     * @param trailing true to measure the trailing edge of the character
+     * before offset, false to measure the leading edge of the character
+     * at offset.
+     * @param fmi receives metrics information about the requested
+     * character, can be null.
+     * @return the signed offset from the leading margin to the requested
+     * character edge.
      */
-    public float measure(@IntRange(from = 0) int offset, boolean trailing,
-            @NonNull FontMetricsInt fmi) {
-        if (offset > mLen) {
-            throw new IndexOutOfBoundsException(
-                    "offset(" + offset + ") should be less than line limit(" + mLen + ")");
-        }
-        final int target = trailing ? offset - 1 : offset;
+    public float measure(int offset, boolean trailing, FontMetricsInt fmi) {
+        int target = trailing ? offset - 1 : offset;
         if (target < 0) {
             return 0;
         }
 
         float h = 0;
-        for (int runIndex = 0; runIndex < mDirections.getRunCount(); runIndex++) {
-            final int runStart = mDirections.getRunStart(runIndex);
-            final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen);
-            final boolean runIsRtl = mDirections.isRunRtl(runIndex);
 
-            int segStart = runStart;
+        if (!mHasTabs) {
+            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
+                return measureRun(0, offset, mLen, false, fmi);
+            }
+            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
+                return measureRun(0, offset, mLen, true, fmi);
+            }
+        }
+
+        char[] chars = mChars;
+        int[] runs = mDirections.mDirections;
+        for (int i = 0; i < runs.length; i += 2) {
+            int runStart = runs[i];
+            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
+            if (runLimit > mLen) {
+                runLimit = mLen;
+            }
+            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
+
+            int segstart = runStart;
             for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
-                if (j == runLimit || charAt(j) == TAB_CHAR) {
-                    final boolean targetIsInThisSegment = target >= segStart && target < j;
-                    final boolean sameDirection = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
+                int codept = 0;
+                if (mHasTabs && j < runLimit) {
+                    codept = chars[j];
+                    if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) {
+                        codept = Character.codePointAt(chars, j);
+                        if (codept > 0xFFFF) {
+                            ++j;
+                            continue;
+                        }
+                    }
+                }
 
-                    if (targetIsInThisSegment && sameDirection) {
-                        return h + measureRun(segStart, offset, j, runIsRtl, fmi);
+                if (j == runLimit || codept == '\t') {
+                    boolean inSegment = target >= segstart && target < j;
+
+                    boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
+                    if (inSegment && advance) {
+                        return h + measureRun(segstart, offset, j, runIsRtl, fmi);
                     }
 
-                    final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi);
-                    h += sameDirection ? segmentWidth : -segmentWidth;
+                    float w = measureRun(segstart, j, j, runIsRtl, fmi);
+                    h += advance ? w : -w;
 
-                    if (targetIsInThisSegment) {
-                        return h + measureRun(segStart, offset, j, runIsRtl, null);
+                    if (inSegment) {
+                        return h + measureRun(segstart, offset, j, runIsRtl, null);
                     }
 
-                    if (j != runLimit) {  // charAt(j) == TAB_CHAR
+                    if (codept == '\t') {
                         if (offset == j) {
                             return h;
                         }
@@ -386,7 +401,7 @@
                         }
                     }
 
-                    segStart = j + 1;
+                    segstart = j + 1;
                 }
             }
         }
@@ -411,29 +426,62 @@
         }
 
         float h = 0;
-        for (int runIndex = 0; runIndex < mDirections.getRunCount(); runIndex++) {
-            final int runStart = mDirections.getRunStart(runIndex);
-            final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen);
-            final boolean runIsRtl = mDirections.isRunRtl(runIndex);
 
-            int segStart = runStart;
+        if (!mHasTabs) {
+            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
+                for (int offset = 0; offset <= mLen; ++offset) {
+                    measurement[offset] = measureRun(0, offset, mLen, false, fmi);
+                }
+                return measurement;
+            }
+            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
+                for (int offset = 0; offset <= mLen; ++offset) {
+                    measurement[offset] = measureRun(0, offset, mLen, true, fmi);
+                }
+                return measurement;
+            }
+        }
+
+        char[] chars = mChars;
+        int[] runs = mDirections.mDirections;
+        for (int i = 0; i < runs.length; i += 2) {
+            int runStart = runs[i];
+            int runLimit = runStart + (runs[i + 1] & Layout.RUN_LENGTH_MASK);
+            if (runLimit > mLen) {
+                runLimit = mLen;
+            }
+            boolean runIsRtl = (runs[i + 1] & Layout.RUN_RTL_FLAG) != 0;
+
+            int segstart = runStart;
             for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; ++j) {
-                if (j == runLimit || charAt(j) == TAB_CHAR) {
-                    final  float oldh = h;
-                    final boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
-                    final float w = measureRun(segStart, j, j, runIsRtl, fmi);
+                int codept = 0;
+                if (mHasTabs && j < runLimit) {
+                    codept = chars[j];
+                    if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) {
+                        codept = Character.codePointAt(chars, j);
+                        if (codept > 0xFFFF) {
+                            ++j;
+                            continue;
+                        }
+                    }
+                }
+
+                if (j == runLimit || codept == '\t') {
+                    float oldh = h;
+                    boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
+                    float w = measureRun(segstart, j, j, runIsRtl, fmi);
                     h += advance ? w : -w;
 
-                    final float baseh = advance ? oldh : h;
+                    float baseh = advance ? oldh : h;
                     FontMetricsInt crtfmi = advance ? fmi : null;
-                    for (int offset = segStart; offset <= j && offset <= mLen; ++offset) {
-                        if (target[offset] >= segStart && target[offset] < j) {
+                    for (int offset = segstart; offset <= j && offset <= mLen; ++offset) {
+                        if (target[offset] >= segstart && target[offset] < j) {
                             measurement[offset] =
-                                    baseh + measureRun(segStart, offset, j, runIsRtl, crtfmi);
+                                    baseh + measureRun(segstart, offset, j, runIsRtl, crtfmi);
                         }
                     }
 
-                    if (j != runLimit) {  // charAt(j) == TAB_CHAR
+                    if (codept == '\t') {
                         if (target[j] == j) {
                             measurement[j] = h;
                         }
@@ -443,7 +491,7 @@
                         }
                     }
 
-                    segStart = j + 1;
+                    segstart = j + 1;
                 }
             }
         }
@@ -815,6 +863,7 @@
         } else {
             final int delta = mStart;
             if (mComputed == null) {
+                // TODO: Enable measured getRunAdvance for ReplacementSpan and RTL text.
                 return wp.getRunAdvance(mText, delta + start, delta + end,
                         delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
             } else {
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 043b31d..03d9955 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -20,6 +20,7 @@
 import android.graphics.BaseRecordingCanvas;
 import android.graphics.CanvasProperty;
 import android.graphics.Paint;
+import android.os.Build;
 
 /**
  * This class exists temporarily to workaround broken apps
@@ -36,13 +37,13 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public abstract void drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top,
             CanvasProperty<Float> right, CanvasProperty<Float> bottom, CanvasProperty<Float> rx,
             CanvasProperty<Float> ry, CanvasProperty<Paint> paint);
 
     /** @hide */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
             CanvasProperty<Float> radius, CanvasProperty<Paint> paint);
 }
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index fa30221..4a5ccdf 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -218,11 +218,6 @@
                 layoutRight = end - paddingEnd;
                 end = layoutLeft = layoutRight - child.getMeasuredWidth();
             }
-            if (child == mAudiblyAlertedIcon) {
-                int paddingEnd = mContentEndMargin;
-                layoutRight = end - paddingEnd;
-                end = layoutLeft = layoutRight - child.getMeasuredWidth();
-            }
             if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
                 int ltrLeft = layoutLeft;
                 layoutLeft = getWidth() - layoutRight;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c7f25d7..5f1336f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4732,6 +4732,16 @@
     private TouchDelegate mTouchDelegate = null;
 
     /**
+     * While touch exploration is in use, set to true when hovering across boundaries and
+     * inside the touch area of the delegate at receiving {@link MotionEvent#ACTION_HOVER_ENTER}
+     * or {@link MotionEvent#ACTION_HOVER_MOVE}. False when leaving boundaries or receiving a
+     * {@link MotionEvent#ACTION_HOVER_EXIT}.
+     * Note that children of view group are excluded in the touch area.
+     * @see #dispatchTouchExplorationHoverEvent
+     */
+    private boolean mHoveringTouchDelegate = false;
+
+    /**
      * Solid color to use as a background when creating the drawing cache. Enables
      * the cache to use 16 bit bitmaps instead of 32 bit.
      */
@@ -13965,6 +13975,96 @@
     }
 
     /**
+     * Dispatching hover events to {@link TouchDelegate} to improve accessibility.
+     * <p>
+     * This method is dispatching hover events to the delegate target to support explore by touch.
+     * Similar to {@link ViewGroup#dispatchTouchEvent}, this method send proper hover events to
+     * the delegate target according to the pointer and the touch area of the delegate while touch
+     * exploration enabled.
+     * </p>
+     *
+     * @param event The motion event dispatch to the delegate target.
+     * @return True if the event was handled, false otherwise.
+     *
+     * @see #onHoverEvent
+     */
+    private boolean dispatchTouchExplorationHoverEvent(MotionEvent event) {
+        final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
+        if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) {
+            return false;
+        }
+
+        final boolean oldHoveringTouchDelegate = mHoveringTouchDelegate;
+        final int action = event.getActionMasked();
+        boolean pointInDelegateRegion = false;
+        boolean handled = false;
+
+        final AccessibilityNodeInfo.TouchDelegateInfo info = mTouchDelegate.getTouchDelegateInfo();
+        for (int i = 0; i < info.getRegionCount(); i++) {
+            Region r = info.getRegionAt(i);
+            if (r.contains((int) event.getX(), (int) event.getY())) {
+                pointInDelegateRegion = true;
+            }
+        }
+
+        // Explore by touch should dispatch events to children under the pointer first if any
+        // before dispatching to TouchDelegate. For non-hoverable views that do not consume
+        // hover events but receive accessibility focus, it should also not delegate to these
+        // views when hovered.
+        if (!oldHoveringTouchDelegate) {
+            if ((action == MotionEvent.ACTION_HOVER_ENTER
+                    || action == MotionEvent.ACTION_HOVER_MOVE)
+                    && !pointInHoveredChild(event)
+                    && pointInDelegateRegion) {
+                mHoveringTouchDelegate = true;
+            }
+        } else {
+            if (action == MotionEvent.ACTION_HOVER_EXIT
+                    || (action == MotionEvent.ACTION_HOVER_MOVE
+                        && (pointInHoveredChild(event) || !pointInDelegateRegion))) {
+                mHoveringTouchDelegate = false;
+            }
+        }
+        switch (action) {
+            case MotionEvent.ACTION_HOVER_MOVE:
+                if (oldHoveringTouchDelegate && mHoveringTouchDelegate) {
+                    // Inside bounds, dispatch as is.
+                    handled = mTouchDelegate.onTouchExplorationHoverEvent(event);
+                } else if (!oldHoveringTouchDelegate && mHoveringTouchDelegate) {
+                    // Moving inbound, synthesize hover enter.
+                    MotionEvent eventNoHistory = (event.getHistorySize() == 0)
+                            ? event : MotionEvent.obtainNoHistory(event);
+                    eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
+                    handled = mTouchDelegate.onTouchExplorationHoverEvent(eventNoHistory);
+                    eventNoHistory.setAction(action);
+                    handled |= mTouchDelegate.onTouchExplorationHoverEvent(eventNoHistory);
+                } else if (oldHoveringTouchDelegate && !mHoveringTouchDelegate) {
+                    // Moving outbound, synthesize hover exit.
+                    final boolean hoverExitPending = event.isHoverExitPending();
+                    event.setHoverExitPending(true);
+                    mTouchDelegate.onTouchExplorationHoverEvent(event);
+                    MotionEvent eventNoHistory = (event.getHistorySize() == 0)
+                            ? event : MotionEvent.obtainNoHistory(event);
+                    eventNoHistory.setHoverExitPending(hoverExitPending);
+                    eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
+                    mTouchDelegate.onTouchExplorationHoverEvent(eventNoHistory);
+                }  // else: outside bounds, do nothing.
+                break;
+            case MotionEvent.ACTION_HOVER_ENTER:
+                if (!oldHoveringTouchDelegate && mHoveringTouchDelegate) {
+                    handled = mTouchDelegate.onTouchExplorationHoverEvent(event);
+                }
+                break;
+            case MotionEvent.ACTION_HOVER_EXIT:
+                if (oldHoveringTouchDelegate) {
+                    mTouchDelegate.onTouchExplorationHoverEvent(event);
+                }
+                break;
+        }
+        return handled;
+    }
+
+    /**
      * Implement this method to handle hover events.
      * <p>
      * This method is called whenever a pointer is hovering into, over, or out of the
@@ -14001,15 +14101,8 @@
      * @see #onHoverChanged
      */
     public boolean onHoverEvent(MotionEvent event) {
-        // Explore by touch should dispatch events to children under pointer first if any before
-        // dispatching to TouchDelegate. For children non-hoverable that will not consume events,
-        // it should also not delegate when they got the pointer hovered.
-        if (mTouchDelegate != null && !pointInHoveredChild(event)) {
-            final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
-            if (manager.isEnabled() && manager.isTouchExplorationEnabled()
-                    && mTouchDelegate.onTouchExplorationHoverEvent(event)) {
-                return true;
-            }
+        if (mTouchDelegate != null && dispatchTouchExplorationHoverEvent(event)) {
+            return true;
         }
 
         // The root view may receive hover (or touch) events that are outside the bounds of
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 9f509b1..7756a19 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import android.annotation.FloatRange;
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -40,6 +41,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.PixelCopy;
@@ -55,11 +57,15 @@
 import com.android.internal.R;
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Android magnifier widget. Can be used by any view which is attached to a window.
  */
 @UiThread
 public final class Magnifier {
+    private static final String TAG = "Magnifier";
     // Use this to specify that a previous configuration value does not exist.
     private static final int NONEXISTENT_PREVIOUS_CONFIG_VALUE = -1;
     // The callbacks of the pixel copy requests will be invoked on
@@ -83,8 +89,8 @@
     private int mSourceWidth;
     // The height of the content that will be copied to the magnifier.
     private int mSourceHeight;
-    // Whether the zoom of the magnifier has changed since last content copy.
-    private boolean mDirtyZoom;
+    // Whether the zoom of the magnifier or the view position have changed since last content copy.
+    private boolean mDirtyState;
     // The elevation of the window containing the magnifier.
     private final float mWindowElevation;
     // The corner radius of the window containing the magnifier.
@@ -95,6 +101,14 @@
     private final int mDefaultVerticalSourceToMagnifierOffset;
     // Whether the magnifier will be clamped inside the main surface and not overlap system insets.
     private final boolean mForcePositionWithinWindowSystemInsetsBounds;
+    // The behavior of the left bound of the rectangle where the content can be copied from.
+    private @SourceBound int mLeftContentBound;
+    // The behavior of the top bound of the rectangle where the content can be copied from.
+    private @SourceBound int mTopContentBound;
+    // The behavior of the right bound of the rectangle where the content can be copied from.
+    private @SourceBound int mRightContentBound;
+    // The behavior of the bottom bound of the rectangle where the content can be copied from.
+    private @SourceBound int mBottomContentBound;
     // The parent surface for the magnifier surface.
     private SurfaceInfo mParentSurface;
     // The surface where the content will be copied from.
@@ -145,6 +159,10 @@
                 params.mVerticalDefaultSourceToMagnifierOffset;
         mForcePositionWithinWindowSystemInsetsBounds =
                 params.mForcePositionWithinWindowSystemInsetsBounds;
+        mLeftContentBound = params.mLeftContentBound;
+        mTopContentBound = params.mTopContentBound;
+        mRightContentBound = params.mRightContentBound;
+        mBottomContentBound = params.mBottomContentBound;
         // The view's surface coordinates will not be updated until the magnifier is first shown.
         mViewCoordinatesInSurface = new int[2];
     }
@@ -195,8 +213,6 @@
     public void show(@FloatRange(from = 0) float sourceCenterX,
             @FloatRange(from = 0) float sourceCenterY,
             float magnifierCenterX, float magnifierCenterY) {
-        sourceCenterX = Math.max(0, Math.min(sourceCenterX, mView.getWidth()));
-        sourceCenterY = Math.max(0, Math.min(sourceCenterY, mView.getHeight()));
 
         obtainSurfaces();
         obtainContentCoordinates(sourceCenterX, sourceCenterY);
@@ -205,7 +221,7 @@
         final int startX = mClampedCenterZoomCoords.x - mSourceWidth / 2;
         final int startY = mClampedCenterZoomCoords.y - mSourceHeight / 2;
         if (sourceCenterX != mPrevShowSourceCoords.x || sourceCenterY != mPrevShowSourceCoords.y
-                || mDirtyZoom) {
+                || mDirtyState) {
             if (mWindow == null) {
                 synchronized (mLock) {
                     mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(),
@@ -262,13 +278,13 @@
     public void update() {
         if (mWindow != null) {
             obtainSurfaces();
-            if (!mDirtyZoom) {
+            if (!mDirtyState) {
                 // Update the content shown in the magnifier.
                 performPixelCopy(mPrevStartCoordsInSurface.x, mPrevStartCoordsInSurface.y,
                         false /* update window position */);
             } else {
-                // If the zoom has changed, we cannot use the same top left coordinates
-                // as before, so just #show again to have them recomputed.
+                // If for example the zoom has changed, we cannot use the same top left
+                // coordinates as before, so just #show again to have them recomputed.
                 show(mPrevShowSourceCoords.x, mPrevShowSourceCoords.y,
                         mPrevShowWindowCoords.x, mPrevShowWindowCoords.y);
             }
@@ -315,6 +331,7 @@
 
     /**
      * Sets the zoom to be applied to the chosen content before being copied to the magnifier popup.
+     * The change will become effective at the next #show or #update call.
      * @param zoom the zoom to be set
      */
     public void setZoom(@FloatRange(from = 0f) float zoom) {
@@ -322,7 +339,7 @@
         mZoom = zoom;
         mSourceWidth = Math.round(mWindowWidth / mZoom);
         mSourceHeight = Math.round(mWindowHeight / mZoom);
-        mDirtyZoom = true;
+        mDirtyState = true;
     }
 
     /**
@@ -480,7 +497,14 @@
      * magnifier. These are relative to the surface the content is copied from.
      */
     private void obtainContentCoordinates(final float xPosInView, final float yPosInView) {
+        final int prevViewXInSurface = mViewCoordinatesInSurface[0];
+        final int prevViewYInSurface = mViewCoordinatesInSurface[1];
         mView.getLocationInSurface(mViewCoordinatesInSurface);
+        if (mViewCoordinatesInSurface[0] != prevViewXInSurface
+                || mViewCoordinatesInSurface[1] != prevViewYInSurface) {
+            mDirtyState = true;
+        }
+
         final int zoomCenterX;
         final int zoomCenterY;
         if (mView instanceof SurfaceView) {
@@ -492,8 +516,25 @@
             zoomCenterY = Math.round(yPosInView + mViewCoordinatesInSurface[1]);
         }
 
-        // Clamp the x location to avoid magnifying content which does not belong
-        // to the magnified view. This will not take into account overlapping views.
+        final Rect[] bounds = new Rect[3]; // [MAX_IN_SURFACE, MAX_IN_VIEW, MAX_VISIBLE]
+        // Obtain the surface bounds rectangle.
+        final Rect surfaceBounds = new Rect(0, 0,
+                mContentCopySurface.mWidth, mContentCopySurface.mHeight);
+        bounds[0] = surfaceBounds;
+        // Obtain the view bounds rectangle.
+        final Rect viewBounds;
+        if (mView instanceof SurfaceView) {
+            viewBounds = new Rect(0, 0, mContentCopySurface.mWidth, mContentCopySurface.mHeight);
+        } else {
+            viewBounds = new Rect(
+                    mViewCoordinatesInSurface[0],
+                    mViewCoordinatesInSurface[1],
+                    mViewCoordinatesInSurface[0] + mView.getWidth(),
+                    mViewCoordinatesInSurface[1] + mView.getHeight()
+            );
+        }
+        bounds[1] = viewBounds;
+        // Obtain the visible view region rectangle.
         final Rect viewVisibleRegion = new Rect();
         mView.getGlobalVisibleRect(viewVisibleRegion);
         if (mView.getViewRootImpl() != null) {
@@ -505,9 +546,40 @@
             // If we copy content from a SurfaceView, clamp coordinates relative to it.
             viewVisibleRegion.offset(-mViewCoordinatesInSurface[0], -mViewCoordinatesInSurface[1]);
         }
-        mClampedCenterZoomCoords.x = Math.max(viewVisibleRegion.left + mSourceWidth / 2, Math.min(
-                zoomCenterX, viewVisibleRegion.right - mSourceWidth / 2));
-        mClampedCenterZoomCoords.y = zoomCenterY;
+        bounds[2] = viewVisibleRegion;
+
+        // Aggregate the above to obtain the bounds where the content copy will be restricted.
+        int resolvedLeft = Integer.MIN_VALUE;
+        for (int i = mLeftContentBound; i >= 0; --i) {
+            resolvedLeft = Math.max(resolvedLeft, bounds[i].left);
+        }
+        int resolvedTop = Integer.MIN_VALUE;
+        for (int i = mTopContentBound; i >= 0; --i) {
+            resolvedTop = Math.max(resolvedTop, bounds[i].top);
+        }
+        int resolvedRight = Integer.MAX_VALUE;
+        for (int i = mRightContentBound; i >= 0; --i) {
+            resolvedRight = Math.min(resolvedRight, bounds[i].right);
+        }
+        int resolvedBottom = Integer.MAX_VALUE;
+        for (int i = mBottomContentBound; i >= 0; --i) {
+            resolvedBottom = Math.min(resolvedBottom, bounds[i].bottom);
+        }
+        // Adjust <left-right> and <top-bottom> pairs of bounds to make sense.
+        resolvedLeft = Math.min(resolvedLeft, mContentCopySurface.mWidth - mSourceWidth);
+        resolvedTop = Math.min(resolvedTop, mContentCopySurface.mHeight - mSourceHeight);
+        if (resolvedLeft < 0 || resolvedTop < 0) {
+            Log.e(TAG, "Magnifier's content is copied from a surface smaller than"
+                    + "the content requested size. This will probably lead to distorted content.");
+        }
+        resolvedRight = Math.max(resolvedRight, resolvedLeft + mSourceWidth);
+        resolvedBottom = Math.max(resolvedBottom, resolvedTop + mSourceHeight);
+
+        // Finally compute the coordinates of the source center.
+        mClampedCenterZoomCoords.x = Math.max(resolvedLeft + mSourceWidth / 2, Math.min(
+                zoomCenterX, resolvedRight - mSourceWidth / 2));
+        mClampedCenterZoomCoords.y = Math.max(resolvedTop + mSourceHeight / 2, Math.min(
+                zoomCenterY, resolvedBottom - mSourceHeight / 2));
     }
 
     /**
@@ -539,20 +611,16 @@
         if (mContentCopySurface.mSurface == null || !mContentCopySurface.mSurface.isValid()) {
             return;
         }
-        // Clamp copy coordinates inside the surface to avoid displaying distorted content.
-        final int clampedStartXInSurface = Math.max(0,
-                Math.min(startXInSurface, mContentCopySurface.mWidth - mSourceWidth));
-        final int clampedStartYInSurface = Math.max(0,
-                Math.min(startYInSurface, mContentCopySurface.mHeight - mSourceHeight));
+
         // Clamp window coordinates inside the parent surface, to avoid displaying
         // the magnifier out of screen or overlapping with system insets.
         final Point windowCoords = getCurrentClampedWindowCoordinates();
 
         // Perform the pixel copy.
-        mPixelCopyRequestRect.set(clampedStartXInSurface,
-                clampedStartYInSurface,
-                clampedStartXInSurface + mSourceWidth,
-                clampedStartYInSurface + mSourceHeight);
+        mPixelCopyRequestRect.set(startXInSurface,
+                startYInSurface,
+                startXInSurface + mSourceWidth,
+                startYInSurface + mSourceHeight);
         final InternalPopupWindow currentWindowInstance = mWindow;
         final Bitmap bitmap =
                 Bitmap.createBitmap(mSourceWidth, mSourceHeight, Bitmap.Config.ARGB_8888);
@@ -573,7 +641,7 @@
                 sPixelCopyHandlerThread.getThreadHandler());
         mPrevStartCoordsInSurface.x = startXInSurface;
         mPrevStartCoordsInSurface.y = startYInSurface;
-        mDirtyZoom = false;
+        mDirtyState = false;
     }
 
     /**
@@ -912,6 +980,10 @@
         private int mHorizontalDefaultSourceToMagnifierOffset;
         private int mVerticalDefaultSourceToMagnifierOffset;
         private boolean mForcePositionWithinWindowSystemInsetsBounds;
+        private @SourceBound int mLeftContentBound;
+        private @SourceBound int mTopContentBound;
+        private @SourceBound int mRightContentBound;
+        private @SourceBound int  mBottomContentBound;
 
         /**
          * Construct a new builder for {@link Magnifier} objects.
@@ -937,6 +1009,10 @@
                     a.getDimensionPixelSize(R.styleable.Magnifier_magnifierVerticalOffset, 0);
             a.recycle();
             mForcePositionWithinWindowSystemInsetsBounds = true;
+            mLeftContentBound = SOURCE_BOUND_MAX_VISIBLE;
+            mTopContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
+            mRightContentBound = SOURCE_BOUND_MAX_VISIBLE;
+            mBottomContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
         }
 
         /**
@@ -1044,6 +1120,52 @@
         }
 
         /**
+         * Defines the bounds of the rectangle where the magnifier will be able to copy its content
+         * from. The content will always be copied from the {@link Surface} of the main application
+         * window unless the magnified view is a {@link SurfaceView}, in which case its backing
+         * surface will be used. Each bound can have a different behavior, with the options being:
+         * <ul>
+         *   <li>{@link #SOURCE_BOUND_MAX_VISIBLE}, which extends the bound as much as possible
+         *   while remaining in the visible region of the magnified view, as given by
+         *   {@link android.view.View#getGlobalVisibleRect(Rect)}. For example, this will take into
+         *   account the case when the view is contained in a scrollable container, and the
+         *   magnifier will refuse to copy content outside of the visible view region</li>
+         *   <li>{@link #SOURCE_BOUND_MAX_IN_VIEW}, which extends the bound as much as possible
+         *   while remaining in the bounds of the view. Note that, although this option is
+         *   used, the magnifier will always only display content visible on the screen: if the
+         *   view lays outside the screen or is covered by a different view either partially or
+         *   totally, the magnifier will not show any view region not visible on the screen.</li>
+         *   <li>{@link #SOURCE_BOUND_MAX_IN_SURFACE}, which extends the bound as much
+         *   as possible while remaining inside the surface the content is copied from.</li>
+         * </ul>
+         * Note that if either of the first three options is used, the bound will be compared to
+         * the bound of the surface (i.e. as if {@link #SOURCE_BOUND_MAX_IN_SURFACE} was used),
+         * and the more restrictive one will be chosen. In other words, no attempt to copy content
+         * from outside the surface will be permitted. If two opposite bounds are not well-behaved
+         * (i.e. left + sourceWidth > right or top + sourceHeight > bottom), the left and top
+         * bounds will have priority and the others will be extended accordingly. If the pairs
+         * obtained this way still remain out of bounds, the smallest possible offset will be added
+         * to the pairs to bring them inside the surface bounds. If this is impossible
+         * (i.e. the surface is too small for the size of the content we try to copy on either
+         * dimension), an error will be logged and the magnifier content will look distorted.
+         * The default values assumed by the builder for the source bounds are
+         * left: {@link #SOURCE_BOUND_MAX_VISIBLE}, top: {@link #SOURCE_BOUND_MAX_IN_SURFACE},
+         * right: {@link #SOURCE_BOUND_MAX_VISIBLE}, bottom: {@link #SOURCE_BOUND_MAX_IN_SURFACE}.
+         * @param left the left bound for content copy
+         * @param top the top bound for content copy
+         * @param right the right bound for content copy
+         * @param bottom the bottom bound for content copy
+         */
+        public Builder setSourceBounds(@SourceBound int left, @SourceBound int top,
+                @SourceBound int right, @SourceBound int bottom) {
+            mLeftContentBound = left;
+            mTopContentBound = top;
+            mRightContentBound = right;
+            mBottomContentBound = bottom;
+            return this;
+        }
+
+        /**
          * Builds a {@link Magnifier} instance based on the configuration of this {@link Builder}.
          */
         public @NonNull Magnifier build() {
@@ -1051,6 +1173,38 @@
         }
     }
 
+    /**
+     * A source bound that will extend as much as possible, while remaining within the surface
+     * the content is copied from.
+     */
+
+    public static final int SOURCE_BOUND_MAX_IN_SURFACE = 0;
+    /**
+     * A source bound that will extend as much as possible, while remaining within the
+     * magnified view.
+     */
+
+    public static final int SOURCE_BOUND_MAX_IN_VIEW = 1;
+
+    /**
+     * A source bound that will extend as much as possible, while remaining within the
+     * visible region of the magnified view, as determined by
+     * {@link View#getGlobalVisibleRect(Rect)}.
+     */
+    public static final int SOURCE_BOUND_MAX_VISIBLE = 2;
+
+
+    /**
+     * Used to describe the {@link Surface} rectangle where the magnifier's content is allowed
+     * to be copied from. For more details, see method
+     * {@link Magnifier.Builder#setSourceBounds(int, int, int, int)}
+     *
+     * @hide
+     */
+    @IntDef({SOURCE_BOUND_MAX_IN_SURFACE, SOURCE_BOUND_MAX_IN_VIEW, SOURCE_BOUND_MAX_VISIBLE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SourceBound {}
+
     // The rest of the file consists of test APIs.
 
     /**
diff --git a/core/proto/android/server/location/enums.proto b/core/proto/android/server/location/enums.proto
new file mode 100644
index 0000000..b6dc589
--- /dev/null
+++ b/core/proto/android/server/location/enums.proto
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.server.location;
+
+option java_outer_classname = "ServerLocationProtoEnums";
+option java_multiple_files = true;
+
+// GPS Signal Quality levels,
+// primarily used by location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+enum GpsSignalQualityEnum {
+    GPS_SIGNAL_QUALITY_UNKNOWN = -1;
+    GPS_SIGNAL_QUALITY_POOR = 0;
+    GPS_SIGNAL_QUALITY_GOOD = 1;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 62896be..ecf8137 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -642,7 +642,6 @@
         <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.READ_CONTACTS"
-        android:permissionGroup="android.permission-group.CONTACTS"
         android:label="@string/permlab_readContacts"
         android:description="@string/permdesc_readContacts"
         android:protectionLevel="dangerous" />
@@ -651,7 +650,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.WRITE_CONTACTS"
-        android:permissionGroup="android.permission-group.CONTACTS"
         android:label="@string/permlab_writeContacts"
         android:description="@string/permdesc_writeContacts"
         android:protectionLevel="dangerous" />
@@ -673,7 +671,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.READ_CALENDAR"
-        android:permissionGroup="android.permission-group.CALENDAR"
         android:label="@string/permlab_readCalendar"
         android:description="@string/permdesc_readCalendar"
         android:protectionLevel="dangerous" />
@@ -682,7 +679,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.WRITE_CALENDAR"
-        android:permissionGroup="android.permission-group.CALENDAR"
         android:label="@string/permlab_writeCalendar"
         android:description="@string/permdesc_writeCalendar"
         android:protectionLevel="dangerous" />
@@ -704,7 +700,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.SEND_SMS"
-        android:permissionGroup="android.permission-group.SMS"
         android:label="@string/permlab_sendSms"
         android:description="@string/permdesc_sendSms"
         android:permissionFlags="costsMoney"
@@ -714,7 +709,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.RECEIVE_SMS"
-        android:permissionGroup="android.permission-group.SMS"
         android:label="@string/permlab_receiveSms"
         android:description="@string/permdesc_receiveSms"
         android:protectionLevel="dangerous"/>
@@ -723,7 +717,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.READ_SMS"
-        android:permissionGroup="android.permission-group.SMS"
         android:label="@string/permlab_readSms"
         android:description="@string/permdesc_readSms"
         android:protectionLevel="dangerous" />
@@ -732,7 +725,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.RECEIVE_WAP_PUSH"
-        android:permissionGroup="android.permission-group.SMS"
         android:label="@string/permlab_receiveWapPush"
         android:description="@string/permdesc_receiveWapPush"
         android:protectionLevel="dangerous" />
@@ -741,7 +733,6 @@
         <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.RECEIVE_MMS"
-        android:permissionGroup="android.permission-group.SMS"
         android:label="@string/permlab_receiveMms"
         android:description="@string/permdesc_receiveMms"
         android:protectionLevel="dangerous" />
@@ -759,7 +750,6 @@
          <p>Protection level: dangerous
          @hide Pending API council approval -->
     <permission android:name="android.permission.READ_CELL_BROADCASTS"
-        android:permissionGroup="android.permission-group.SMS"
         android:label="@string/permlab_readCellBroadcasts"
         android:description="@string/permdesc_readCellBroadcasts"
         android:protectionLevel="dangerous" />
@@ -801,7 +791,6 @@
      @deprecated replaced by new strongly-typed permission groups in Q.
      -->
     <permission android:name="android.permission.READ_EXTERNAL_STORAGE"
-        android:permissionGroup="android.permission-group.STORAGE"
         android:label="@string/permlab_sdcardRead"
         android:description="@string/permdesc_sdcardRead"
         android:protectionLevel="normal" />
@@ -822,7 +811,6 @@
          @deprecated replaced by new strongly-typed permission groups in Q.
     -->
     <permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
-        android:permissionGroup="android.permission-group.STORAGE"
         android:label="@string/permlab_sdcardWrite"
         android:description="@string/permdesc_sdcardWrite"
         android:protectionLevel="normal" />
@@ -838,14 +826,12 @@
 
     <!-- Allows an application to read the user's shared audio collection. -->
     <permission android:name="android.permission.READ_MEDIA_AUDIO"
-        android:permissionGroup="android.permission-group.MEDIA_AURAL"
         android:label="@string/permlab_audioRead"
         android:description="@string/permdesc_audioRead"
         android:protectionLevel="dangerous" />
 
     <!-- Allows an application to modify the user's shared audio collection. -->
     <permission android:name="android.permission.WRITE_MEDIA_AUDIO"
-        android:permissionGroup="android.permission-group.MEDIA_AURAL"
         android:label="@string/permlab_audioWrite"
         android:description="@string/permdesc_audioWrite"
         android:protectionLevel="dangerous" />
@@ -861,28 +847,24 @@
 
     <!-- Allows an application to read the user's shared images collection. -->
     <permission android:name="android.permission.READ_MEDIA_IMAGES"
-        android:permissionGroup="android.permission-group.MEDIA_VISUAL"
         android:label="@string/permlab_imagesRead"
         android:description="@string/permdesc_imagesRead"
         android:protectionLevel="dangerous" />
 
     <!-- Allows an application to modify the user's shared images collection. -->
     <permission android:name="android.permission.WRITE_MEDIA_IMAGES"
-        android:permissionGroup="android.permission-group.MEDIA_VISUAL"
         android:label="@string/permlab_imagesWrite"
         android:description="@string/permdesc_imagesWrite"
         android:protectionLevel="dangerous" />
 
     <!-- Allows an application to read the user's shared video collection. -->
     <permission android:name="android.permission.READ_MEDIA_VIDEO"
-        android:permissionGroup="android.permission-group.MEDIA_VISUAL"
         android:label="@string/permlab_videoRead"
         android:description="@string/permdesc_videoRead"
         android:protectionLevel="dangerous" />
 
     <!-- Allows an application to modify the user's shared video collection. -->
     <permission android:name="android.permission.WRITE_MEDIA_VIDEO"
-        android:permissionGroup="android.permission-group.MEDIA_VISUAL"
         android:label="@string/permlab_videoWrite"
         android:description="@string/permdesc_videoWrite"
         android:protectionLevel="dangerous" />
@@ -890,7 +872,6 @@
     <!-- Allows an application to access any geographic locations persisted in the
          user's shared collection. -->
     <permission android:name="android.permission.ACCESS_MEDIA_LOCATION"
-        android:permissionGroup="android.permission-group.MEDIA_VISUAL"
         android:label="@string/permlab_mediaLocation"
         android:description="@string/permdesc_mediaLocation"
         android:protectionLevel="dangerous" />
@@ -921,7 +902,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.ACCESS_FINE_LOCATION"
-        android:permissionGroup="android.permission-group.LOCATION"
         android:label="@string/permlab_accessFineLocation"
         android:description="@string/permdesc_accessFineLocation"
         android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
@@ -932,7 +912,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.ACCESS_COARSE_LOCATION"
-        android:permissionGroup="android.permission-group.LOCATION"
         android:label="@string/permlab_accessCoarseLocation"
         android:description="@string/permdesc_accessCoarseLocation"
         android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
@@ -945,7 +924,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
-        android:permissionGroup="android.permission-group.LOCATION"
         android:label="@string/permlab_accessBackgroundLocation"
         android:description="@string/permdesc_accessBackgroundLocation"
         android:protectionLevel="dangerous|instant" />
@@ -986,7 +964,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.READ_CALL_LOG"
-        android:permissionGroup="android.permission-group.CALL_LOG"
         android:label="@string/permlab_readCallLog"
         android:description="@string/permdesc_readCallLog"
         android:protectionLevel="dangerous" />
@@ -1005,7 +982,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.WRITE_CALL_LOG"
-        android:permissionGroup="android.permission-group.CALL_LOG"
         android:label="@string/permlab_writeCallLog"
         android:description="@string/permdesc_writeCallLog"
         android:protectionLevel="dangerous" />
@@ -1016,7 +992,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.PROCESS_OUTGOING_CALLS"
-        android:permissionGroup="android.permission-group.CALL_LOG"
         android:label="@string/permlab_processOutgoingCalls"
         android:description="@string/permdesc_processOutgoingCalls"
         android:protectionLevel="dangerous" />
@@ -1048,7 +1023,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.READ_PHONE_STATE"
-        android:permissionGroup="android.permission-group.PHONE"
         android:label="@string/permlab_readPhoneState"
         android:description="@string/permdesc_readPhoneState"
         android:protectionLevel="dangerous" />
@@ -1057,7 +1031,6 @@
          granted by {@link #READ_PHONE_STATE} but is exposed to instant applications.
          <p>Protection level: dangerous-->
     <permission android:name="android.permission.READ_PHONE_NUMBERS"
-        android:permissionGroup="android.permission-group.PHONE"
         android:label="@string/permlab_readPhoneNumbers"
         android:description="@string/permdesc_readPhoneNumbers"
         android:protectionLevel="dangerous|instant" />
@@ -1067,7 +1040,6 @@
         <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.CALL_PHONE"
-        android:permissionGroup="android.permission-group.PHONE"
         android:permissionFlags="costsMoney"
         android:label="@string/permlab_callPhone"
         android:description="@string/permdesc_callPhone"
@@ -1077,7 +1049,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"
-        android:permissionGroup="android.permission-group.PHONE"
         android:label="@string/permlab_addVoicemail"
         android:description="@string/permdesc_addVoicemail"
         android:protectionLevel="dangerous" />
@@ -1086,7 +1057,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.USE_SIP"
-        android:permissionGroup="android.permission-group.PHONE"
         android:description="@string/permdesc_use_sip"
         android:label="@string/permlab_use_sip"
         android:protectionLevel="dangerous"/>
@@ -1095,7 +1065,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.ANSWER_PHONE_CALLS"
-        android:permissionGroup="android.permission-group.PHONE"
         android:label="@string/permlab_answerPhoneCalls"
         android:description="@string/permdesc_answerPhoneCalls"
         android:protectionLevel="dangerous|runtime" />
@@ -1123,7 +1092,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.ACCEPT_HANDOVER"
-                android:permissionGroup="android.permission-group.PHONE"
                 android.label="@string/permlab_acceptHandover"
                 android:description="@string/permdesc_acceptHandovers"
                 android:protectionLevel="dangerous" />
@@ -1147,7 +1115,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.RECORD_AUDIO"
-        android:permissionGroup="android.permission-group.MICROPHONE"
         android:label="@string/permlab_recordAudio"
         android:description="@string/permdesc_recordAudio"
         android:protectionLevel="dangerous|instant"/>
@@ -1169,7 +1136,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.ACTIVITY_RECOGNITION"
-        android:permissionGroup="android.permission-group.ACTIVITY_RECOGNITION"
         android:label="@string/permlab_activityRecognition"
         android:description="@string/permdesc_activityRecognition"
         android:protectionLevel="dangerous|instant" />
@@ -1218,7 +1184,6 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.CAMERA"
-        android:permissionGroup="android.permission-group.CAMERA"
         android:label="@string/permlab_camera"
         android:description="@string/permdesc_camera"
         android:protectionLevel="dangerous|instant" />
@@ -1242,7 +1207,6 @@
          measure what is happening inside his/her body, such as heart rate.
          <p>Protection level: dangerous -->
     <permission android:name="android.permission.BODY_SENSORS"
-        android:permissionGroup="android.permission-group.SENSORS"
         android:label="@string/permlab_bodySensors"
         android:description="@string/permdesc_bodySensors"
         android:protectionLevel="dangerous" />
@@ -1721,7 +1685,6 @@
     <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.GET_ACCOUNTS"
-        android:permissionGroup="android.permission-group.CONTACTS"
         android:protectionLevel="dangerous"
         android:description="@string/permdesc_getAccounts"
         android:label="@string/permlab_getAccounts" />
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 4ee9731..433ae39 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -116,17 +116,6 @@
         android:contentDescription="@string/expand_button_content_description_collapsed"
         />
     <ImageView
-        android:id="@+id/profile_badge"
-        android:layout_width="@dimen/notification_badge_size"
-        android:layout_height="@dimen/notification_badge_size"
-        android:layout_gravity="center"
-        android:layout_marginStart="4dp"
-        android:paddingTop="1dp"
-        android:scaleType="fitCenter"
-        android:visibility="gone"
-        android:contentDescription="@string/notification_work_profile_content_description"
-        />
-    <ImageView
         android:id="@+id/alerted_icon"
         android:layout_width="@dimen/notification_alerted_size"
         android:layout_height="@dimen/notification_alerted_size"
@@ -138,6 +127,17 @@
         android:contentDescription="@string/notification_alerted_content_description"
         android:src="@drawable/ic_notifications_alerted"
         android:tint="@color/notification_secondary_text_color_light"
+    />
+    <ImageView
+        android:id="@+id/profile_badge"
+        android:layout_width="@dimen/notification_badge_size"
+        android:layout_height="@dimen/notification_badge_size"
+        android:layout_gravity="center"
+        android:layout_marginStart="4dp"
+        android:paddingTop="1dp"
+        android:scaleType="fitCenter"
+        android:visibility="gone"
+        android:contentDescription="@string/notification_work_profile_content_description"
         />
     <LinearLayout
         android:id="@+id/app_ops"
diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
index 931674a..84c6446 100644
--- a/core/res/res/values-night/themes_device_defaults.xml
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -52,6 +52,9 @@
     <!-- DeviceDefault theme for a window that should look like the Settings app.  -->
     <style name="Theme.DeviceDefault.Settings" parent="Theme.DeviceDefault">
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
+        <item name="colorBackground">@color/primary_dark_device_default_settings</item>
+
+        <item name="listDivider">@color/list_divider_color_dark</item>
     </style>
 
     <!-- Theme for the dialog shown when an app crashes or ANRs. -->
diff --git a/core/res/res/values/colors_car.xml b/core/res/res/values/colors_car.xml
index 32671ac8..ea7c009 100644
--- a/core/res/res/values/colors_car.xml
+++ b/core/res/res/values/colors_car.xml
@@ -284,4 +284,8 @@
 
     <color name="car_red_500a">#ffd50000</color>
     <color name="car_red_a700">#ffd50000</color>
+
+    <color name="car_keyboard_divider_line">#38ffffff</color>
+    <color name="car_keyboard_text_primary_color">@color/car_grey_50</color>
+    <color name="car_keyboard_text_secondary_color">#8af8f9fa</color>
 </resources>
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 0fe80a1..ded916f 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -42,4 +42,7 @@
     <!-- Error color -->
     <color name="error_color_device_default_dark">@color/error_color_material_dark</color>
     <color name="error_color_device_default_light">@color/error_color_material_light</color>
+
+    <color name="list_divider_color_light">#64000000</color>
+    <color name="list_divider_color_dark">#85ffffff</color>
 </resources>
diff --git a/core/res/res/values/dimens_car.xml b/core/res/res/values/dimens_car.xml
index c1ca33e..5014a29 100644
--- a/core/res/res/values/dimens_car.xml
+++ b/core/res/res/values/dimens_car.xml
@@ -34,7 +34,6 @@
     <!-- The diff between keyline 1 and keyline 3. -->
     <dimen name="car_keyline_1_keyline_3_diff">88dp</dimen>
     <dimen name="car_dialog_action_bar_height">@dimen/car_card_action_bar_height</dimen>
-    <dimen name="car_primary_icon_size">44dp</dimen>
 
     <!-- Text size for car -->
     <dimen name="car_title_size">32sp</dimen>
@@ -56,16 +55,19 @@
 
     <!-- Common icon size for car app -->
     <dimen name="car_icon_size">56dp</dimen>
+    <dimen name="car_primary_icon_size">44dp</dimen>
+    <dimen name="car_secondary_icon_size">36dp</dimen>
 
-    <dimen name="car_card_header_height">96dp</dimen>
-    <dimen name="car_card_action_bar_height">96dp</dimen>
+    <dimen name="car_card_header_height">76dp</dimen>
+    <dimen name="car_card_action_bar_height">76dp</dimen>
 
     <!-- Paddings -->
-    <dimen name="car_padding_1">4dp</dimen>
-    <dimen name="car_padding_2">10dp</dimen>
-    <dimen name="car_padding_3">16dp</dimen>
-    <dimen name="car_padding_4">28dp</dimen>
-    <dimen name="car_padding_5">32dp</dimen>
+    <dimen name="car_padding_0">4dp</dimen>
+    <dimen name="car_padding_1">8dp</dimen>
+    <dimen name="car_padding_2">16dp</dimen>
+    <dimen name="car_padding_3">28dp</dimen>
+    <dimen name="car_padding_4">32dp</dimen>
+    <dimen name="car_padding_5">64dp</dimen>
 
     <!-- Radius -->
     <dimen name="car_radius_1">4dp</dimen>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 3385527..fa009bd 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1475,6 +1475,8 @@
 
         <!-- Toolbar attributes -->
         <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
+
+        <item name="listDivider">@color/list_divider_color_light</item>
     </style>
 
     <!-- @hide DeviceDefault theme for a window that should use Settings theme colors
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 835b735..fec800d 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -23,6 +23,7 @@
 import android.app.KeyguardManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Binder;
 import android.os.IBinder;
@@ -1254,7 +1255,7 @@
                     return new UserNotAuthenticatedException();
                 }
 
-                long fingerprintOnlySid = getFingerprintOnlySid();
+                final long fingerprintOnlySid = getFingerprintOnlySid();
                 if ((fingerprintOnlySid != 0)
                         && (keySids.contains(KeymasterArguments.toUint64(fingerprintOnlySid)))) {
                     // One of the key's SIDs is the current fingerprint SID -- user can be
@@ -1262,6 +1263,14 @@
                     return new UserNotAuthenticatedException();
                 }
 
+                final long faceOnlySid = getFaceOnlySid();
+                if ((faceOnlySid != 0)
+                        && (keySids.contains(KeymasterArguments.toUint64(faceOnlySid)))) {
+                    // One of the key's SIDs is the current face SID -- user can be
+                    // authenticated against that SID.
+                    return new UserNotAuthenticatedException();
+                }
+
                 // None of the key's SIDs can ever be authenticated
                 return new KeyPermanentlyInvalidatedException();
             }
@@ -1272,6 +1281,21 @@
         }
     }
 
+    private long getFaceOnlySid() {
+        final PackageManager packageManager = mContext.getPackageManager();
+        if (!packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+            return 0;
+        }
+        FaceManager faceManager = mContext.getSystemService(FaceManager.class);
+        if (faceManager == null) {
+            return 0;
+        }
+
+        // TODO: Restore USE_BIOMETRIC or USE_BIOMETRIC_INTERNAL permission check in
+        // FaceManager.getAuthenticatorId once the ID is no longer needed here.
+        return faceManager.getAuthenticatorId();
+    }
+
     private long getFingerprintOnlySid() {
         final PackageManager packageManager = mContext.getPackageManager();
         if (!packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 7bbc099..a2d2355 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -182,8 +182,8 @@
                     KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
 
         boolean invalidatedByBiometricEnrollment = false;
-        if (keymasterSwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_FINGERPRINT
-            || keymasterHwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_FINGERPRINT) {
+        if (keymasterSwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_BIOMETRIC
+            || keymasterHwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_BIOMETRIC) {
             // Fingerprint-only key; will be invalidated if the root SID isn't in the SID list.
             invalidatedByBiometricEnrollment = keymasterSecureUserIds != null
                     && !keymasterSecureUserIds.isEmpty()
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index f829bb7..52896b5 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -16,7 +16,7 @@
 
 package android.security.keystore;
 
-import android.app.ActivityManager;
+import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.security.GateKeeper;
 import android.security.KeyStore;
@@ -24,6 +24,8 @@
 import android.security.keymaster.KeymasterDefs;
 
 import java.security.ProviderException;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * @hide
@@ -121,35 +123,44 @@
 
         if (spec.getUserAuthenticationValidityDurationSeconds() == -1) {
             // Every use of this key needs to be authorized by the user. This currently means
-            // fingerprint-only auth.
+            // fingerprint or face auth.
             FingerprintManager fingerprintManager =
                     KeyStore.getApplicationContext().getSystemService(FingerprintManager.class);
+            FaceManager faceManager =
+                    KeyStore.getApplicationContext().getSystemService(FaceManager.class);
             // TODO: Restore USE_FINGERPRINT permission check in
             // FingerprintManager.getAuthenticatorId once the ID is no longer needed here.
-            long fingerprintOnlySid =
+            final long fingerprintOnlySid =
                     (fingerprintManager != null) ? fingerprintManager.getAuthenticatorId() : 0;
-            if (fingerprintOnlySid == 0) {
+            final long faceOnlySid =
+                    (faceManager != null) ? faceManager.getAuthenticatorId() : 0;
+
+            if (fingerprintOnlySid == 0 && faceOnlySid == 0) {
                 throw new IllegalStateException(
-                        "At least one fingerprint must be enrolled to create keys requiring user"
+                        "At least one biometric must be enrolled to create keys requiring user"
                         + " authentication for every use");
             }
 
-            long sid;
+            List<Long> sids = new ArrayList<>();
             if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = spec.getBoundToSpecificSecureUserId();
+                sids.add(spec.getBoundToSpecificSecureUserId());
             } else if (spec.isInvalidatedByBiometricEnrollment()) {
-                // The fingerprint-only SID will change on fingerprint enrollment or removal of all,
-                // enrolled fingerprints, invalidating the key.
-                sid = fingerprintOnlySid;
+                // The biometric-only SIDs will change on biometric enrollment or removal of all
+                // enrolled templates, invalidating the key.
+                sids.add(fingerprintOnlySid);
+                sids.add(faceOnlySid);
             } else {
                 // The root SID will *not* change on fingerprint enrollment, or removal of all
                 // enrolled fingerprints, allowing the key to remain valid.
-                sid = getRootSid();
+                sids.add(getRootSid());
             }
 
-            args.addUnsignedLong(
-                    KeymasterDefs.KM_TAG_USER_SECURE_ID, KeymasterArguments.toUint64(sid));
-            args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT);
+            for (int i = 0; i < sids.size(); i++) {
+                args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
+                        KeymasterArguments.toUint64(sids.get(i)));
+            }
+            args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_BIOMETRIC);
+
             if (spec.isUserAuthenticationValidWhileOnBody()) {
                 throw new ProviderException("Key validity extension while device is on-body is not "
                         + "supported for keys requiring fingerprint authentication");
@@ -166,7 +177,7 @@
             args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
                     KeymasterArguments.toUint64(sid));
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
-                    KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT);
+                    KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_BIOMETRIC);
             args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
                     spec.getUserAuthenticationValidityDurationSeconds());
             if (spec.isUserAuthenticationValidWhileOnBody()) {
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index d401b38..6ae5999 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -164,7 +164,11 @@
 
     if (surface) {
         mRenderThread.requireGlContext();
-        mEglSurface = mEglManager.createSurface(surface, colorMode);
+        auto newSurface = mEglManager.createSurface(surface, colorMode);
+        if (!newSurface) {
+            return false;
+        }
+        mEglSurface = newSurface.unwrap();
     }
 
     if (colorMode == ColorMode::SRGB) {
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index d4ffddd..65ced6a 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -261,7 +261,7 @@
     }
 }
 
-EGLSurface EglManager::createSurface(EGLNativeWindowType window, ColorMode colorMode) {
+Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window, ColorMode colorMode) {
     LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized");
 
     bool wideColorGamut = colorMode == ColorMode::WideColorGamut && EglExtensions.glColorSpace &&
@@ -311,9 +311,9 @@
 
     EGLSurface surface = eglCreateWindowSurface(
             mEglDisplay, wideColorGamut ? mEglConfigWideGamut : mEglConfig, window, attribs);
-    LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
-                        "Failed to create EGLSurface for window %p, eglErr = %s", (void*)window,
-                        eglErrorString());
+    if (surface == EGL_NO_SURFACE) {
+        return Error<EGLint> { eglGetError() };
+    }
 
     if (mSwapBehavior != SwapBehavior::Preserved) {
         LOG_ALWAYS_FATAL_IF(eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR,
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 55c81d4..2a44f7e 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -25,6 +25,7 @@
 #include <ui/GraphicBuffer.h>
 #include <utils/StrongPointer.h>
 #include "IRenderPipeline.h"
+#include "utils/Result.h"
 
 namespace android {
 namespace uirenderer {
@@ -47,7 +48,7 @@
 
     bool hasEglContext();
 
-    EGLSurface createSurface(EGLNativeWindowType window, ColorMode colorMode);
+    Result<EGLSurface, EGLint> createSurface(EGLNativeWindowType window, ColorMode colorMode);
     void destroySurface(EGLSurface surface);
 
     void destroy();
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 0795208..a6073eb 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -321,7 +321,7 @@
     // Check that the VD is in the dislay list, and the layer update queue contains the correct
     // damage rect.
     EXPECT_TRUE(rootNode->getDisplayList()->hasVectorDrawables());
-    EXPECT_FALSE(info.layerUpdateQueue->entries().empty());
+    ASSERT_FALSE(info.layerUpdateQueue->entries().empty());
     EXPECT_EQ(rootNode.get(), info.layerUpdateQueue->entries().at(0).renderNode.get());
     EXPECT_EQ(uirenderer::Rect(0, 0, 200, 400), info.layerUpdateQueue->entries().at(0).damage);
     canvasContext->destroy();
diff --git a/libs/hwui/utils/Result.h b/libs/hwui/utils/Result.h
new file mode 100644
index 0000000..7f33f2e
--- /dev/null
+++ b/libs/hwui/utils/Result.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <variant>
+#include <log/log.h>
+
+namespace android::uirenderer {
+
+template <typename E>
+struct Error {
+    E error;
+};
+
+template <typename R, typename E>
+class Result {
+public:
+    Result(const R& r) : result(std::forward<R>(r)) {}
+    Result(R&& r) : result(std::forward<R>(r)) {}
+    Result(Error<E>&& error) : result(std::forward<Error<E>>(error)) {}
+
+    operator bool() const {
+        return result.index() == 0;
+    }
+
+    R unwrap() const {
+        LOG_ALWAYS_FATAL_IF(result.index() == 1, "unwrap called on error value!");
+        return std::get<R>(result);
+    }
+
+    E error() const {
+        LOG_ALWAYS_FATAL_IF(result.index() == 0, "No error to get from Result");
+        return std::get<Error<E>>(result).error;
+    }
+
+private:
+    std::variant<R, Error<E>> result;
+};
+
+}; // namespace android::uirenderer
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 8a02a82..057a4ae 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -20,9 +20,12 @@
 import android.os.connectivity.GpsBatteryStats;
 import android.os.SystemProperties;
 
+import android.server.location.ServerLocationProtoEnums;
+
 import android.text.format.DateUtils;
 import android.util.Base64;
 import android.util.Log;
+import android.util.StatsLog;
 import android.util.TimeUtils;
 
 import java.util.Arrays;
@@ -39,11 +42,17 @@
 
   private static final String TAG = GnssMetrics.class.getSimpleName();
 
+  /* Constant which indicates GPS signal quality is as yet unknown */
+  public static final int GPS_SIGNAL_QUALITY_UNKNOWN =
+          ServerLocationProtoEnums.GPS_SIGNAL_QUALITY_UNKNOWN; // -1
+
   /* Constant which indicates GPS signal quality is poor */
-  public static final int GPS_SIGNAL_QUALITY_POOR = 0;
+  public static final int GPS_SIGNAL_QUALITY_POOR =
+      ServerLocationProtoEnums.GPS_SIGNAL_QUALITY_POOR; // 0
 
   /* Constant which indicates GPS signal quality is good */
-  public static final int GPS_SIGNAL_QUALITY_GOOD = 1;
+  public static final int GPS_SIGNAL_QUALITY_GOOD =
+      ServerLocationProtoEnums.GPS_SIGNAL_QUALITY_GOOD; // 1
 
   /* Number of GPS signal quality levels */
   public static final int NUM_GPS_SIGNAL_QUALITY_LEVELS = GPS_SIGNAL_QUALITY_GOOD + 1;
@@ -329,11 +338,15 @@
     /* Last reported Top Four Average CN0 */
     private double mLastAverageCn0;
 
+    /* Last reported signal quality bin (based on Top Four Average CN0) */
+    private int mLastSignalLevel;
+
     public GnssPowerMetrics(IBatteryStats stats) {
       mBatteryStats = stats;
       // Used to initialize the variable to a very small value (unachievable in practice) so that
       // the first CNO report will trigger an update to BatteryStats
       mLastAverageCn0 = -100.0;
+      mLastSignalLevel = GPS_SIGNAL_QUALITY_UNKNOWN;
     }
 
     /**
@@ -384,8 +397,13 @@
       if (Math.abs(avgCn0 - mLastAverageCn0) < REPORTING_THRESHOLD_DB_HZ) {
         return;
       }
+      int signalLevel = getSignalLevel(avgCn0);
+      if (signalLevel != mLastSignalLevel) {
+        StatsLog.write(StatsLog.GPS_SIGNAL_QUALITY_CHANGED, signalLevel);
+        mLastSignalLevel = signalLevel;
+      }
       try {
-        mBatteryStats.noteGpsSignalQuality(getSignalLevel(avgCn0));
+        mBatteryStats.noteGpsSignalQuality(signalLevel);
         mLastAverageCn0 = avgCn0;
       } catch (Exception e) {
         Log.w(TAG, "Exception", e);
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 4919eeb..c203fa9 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -272,10 +272,12 @@
     public static final class CasInfo {
         private final int mSystemId;
         private final MediaCas.Session mSession;
+        private final byte[] mPrivateData;
 
-        CasInfo(int systemId, @Nullable MediaCas.Session session) {
+        CasInfo(int systemId, @Nullable MediaCas.Session session, @Nullable byte[] privateData) {
             mSystemId = systemId;
             mSession = session;
+            mPrivateData = privateData;
         }
 
         /**
@@ -288,10 +290,30 @@
         }
 
         /**
+         * Retrieves the private data in the CA_Descriptor associated with a track.
+         * Some CAS systems may need this to initialize the CAS plugin object. This
+         * private data can only be retrieved before a valid {@link MediaCas} object
+         * is set on the extractor.
+         * <p>
+         * @see MediaExtractor#setMediaCas
+         * <p>
+         * @return a byte array containing the private data. A null return value
+         *         indicates that the private data is unavailable. An empty array,
+         *         on the other hand, indicates that the private data is empty
+         *         (zero in length).
+         */
+        @Nullable
+        public byte[] getPrivateData() {
+            return mPrivateData;
+        }
+
+        /**
          * Retrieves the {@link MediaCas.Session} associated with a track. The
          * session is needed to initialize a descrambler in order to decode the
-         * scrambled track.
+         * scrambled track. The session object can only be retrieved after a valid
+         * {@link MediaCas} object is set on the extractor.
          * <p>
+         * @see MediaExtractor#setMediaCas
          * @see MediaDescrambler#setMediaCasSession
          * <p>
          * @return a {@link MediaCas.Session} object associated with a track.
@@ -321,6 +343,13 @@
         if (formatMap.containsKey(MediaFormat.KEY_CA_SYSTEM_ID)) {
             int systemId = ((Integer)formatMap.get(MediaFormat.KEY_CA_SYSTEM_ID)).intValue();
             MediaCas.Session session = null;
+            byte[] privateData = null;
+            if (formatMap.containsKey(MediaFormat.KEY_CA_PRIVATE_DATA)) {
+                ByteBuffer buf = (ByteBuffer) formatMap.get(MediaFormat.KEY_CA_PRIVATE_DATA);
+                buf.rewind();
+                privateData = new byte[buf.remaining()];
+                buf.get(privateData);
+            }
             if (mMediaCas != null && formatMap.containsKey(MediaFormat.KEY_CA_SESSION_ID)) {
                 ByteBuffer buf = (ByteBuffer) formatMap.get(MediaFormat.KEY_CA_SESSION_ID);
                 buf.rewind();
@@ -328,7 +357,7 @@
                 buf.get(sessionId);
                 session = mMediaCas.createFromSessionId(toByteArray(sessionId));
             }
-            return new CasInfo(systemId, session);
+            return new CasInfo(systemId, session, privateData);
         }
         return null;
     }
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index d10cbbc..5dee16e 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -919,7 +919,7 @@
      * a media track.
      * <p>
      * This key is set by {@link MediaExtractor} if the track is scrambled with a conditional
-     * access system.
+     * access system, regardless of the presence of a valid {@link MediaCas} object.
      * <p>
      * The associated value is an integer.
      * @hide
@@ -930,13 +930,25 @@
      * A key describing the {@link MediaCas.Session} object associated with a media track.
      * <p>
      * This key is set by {@link MediaExtractor} if the track is scrambled with a conditional
-     * access system.
+     * access system, after it receives a valid {@link MediaCas} object.
      * <p>
      * The associated value is a ByteBuffer.
      * @hide
      */
     public static final String KEY_CA_SESSION_ID = "ca-session-id";
 
+
+    /**
+     * A key describing the private data in the CA_descriptor associated with a media track.
+     * <p>
+     * This key is set by {@link MediaExtractor} if the track is scrambled with a conditional
+     * access system, before it receives a valid {@link MediaCas} object.
+     * <p>
+     * The associated value is a ByteBuffer.
+     * @hide
+     */
+    public static final String KEY_CA_PRIVATE_DATA = "ca-private-data";
+
     /* package private */ MediaFormat(Map<String, Object> map) {
         mMap = map;
     }
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 9426148..942eafd 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -37,6 +37,7 @@
             ldflags: ["-Wl,--hash-style=both"],
         },
     },
+    version_script: "libjnigraphics.map.txt",
 }
 
 // The headers module is in frameworks/native/Android.bp.
diff --git a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
index 8447b08..8af20e2 100644
--- a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml
@@ -17,7 +17,7 @@
 
 <resources>
     <style name="SettingsSpinnerTitleBar">
-        <item name="android:textAppearance">?android:attr/textAppearance</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceButton</item>
         <item name="android:paddingStart">16dp</item>
         <item name="android:paddingEnd">36dp</item>
         <item name="android:paddingTop">8dp</item>
diff --git a/packages/SettingsLib/res/drawable/list_divider_dark.xml b/packages/SettingsLib/res/drawable/list_divider_dark.xml
deleted file mode 100644
index 5773d9e..0000000
--- a/packages/SettingsLib/res/drawable/list_divider_dark.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2017 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="#64000000" />
-    <size
-        android:height="1dp"
-        android:width="1dp" />
-</shape>
diff --git a/packages/SettingsLib/res/layout/preference_two_target_divider.xml b/packages/SettingsLib/res/layout/preference_two_target_divider.xml
index 60efed4..b81dd83 100644
--- a/packages/SettingsLib/res/layout/preference_two_target_divider.xml
+++ b/packages/SettingsLib/res/layout/preference_two_target_divider.xml
@@ -27,5 +27,5 @@
     <View
         android:layout_width="1dp"
         android:layout_height="match_parent"
-        android:background="@drawable/list_divider_dark" />
+        android:background="?android:attr/listDivider" />
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml
index 02b7ea6..66bbb3a 100644
--- a/packages/SettingsLib/res/values/colors.xml
+++ b/packages/SettingsLib/res/values/colors.xml
@@ -18,4 +18,5 @@
     <color name="disabled_text_color">#66000000</color> <!-- 38% black -->
 
     <color name="usage_graph_dots">@*android:color/tertiary_device_default_settings</color>
+    <color name="list_divider_color">#64000000</color>
 </resources>
diff --git a/packages/SystemUI/res/drawable/ic_notification_block.xml b/packages/SystemUI/res/drawable/ic_notification_block.xml
new file mode 100644
index 0000000..572e97b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_notification_block.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12.0,2.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zM4.0,12.0c0.0,-4.42 3.58,-8.0 8.0,-8.0 1.85,0.0 3.5,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4.0,13.85 4.0,12.0zm8.0,8.0c-1.85,0.0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20.0,10.15 20.0,12.0c0.0,4.42 -3.58,8.0 -8.0,8.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_notifications_alert.xml b/packages/SystemUI/res/drawable/ic_notifications_alert.xml
new file mode 100644
index 0000000..eb7b8ee
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_notifications_alert.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M7.58 4.08L6.15 2.65C3.75 4.48 2.17 7.3 2.03 10.5h2c.15-2.65 1.51-4.97 3.55-6.42zm12.39 6.42h2c-.15-3.2-1.73-6.02-4.12-7.85l-1.42 1.43c2.02 1.45 3.39 3.77 3.54 6.42zM18 11c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2v-5zm-6 11c.14 0 .27-.01.4-.04.65-.14 1.18-.58 1.44-1.18.1-.24.15-.5.15-.78h-4c.01 1.1.9 2 2.01 2z"
+        android:fillColor="#FF000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_notifications_silence.xml b/packages/SystemUI/res/drawable/ic_notifications_silence.xml
new file mode 100644
index 0000000..ff136eb
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_notifications_silence.xml
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M0 0h24v24H0z"
+    />
+    <path
+        android:pathData="M20 18.69L7.84 6.14 5.27 3.49 4 4.76l2.8 2.8v.01c-.52.99-.8 2.16-.8 3.42v5l-2 2v1h13.73l2 2L21 19.72l-1-1.03zM12 22c1.11 0 2-.89 2-2h-4c0 1.11.89 2 2 2zm6-7.32V11c0-3.08-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68c-.15.03-.29.08-.42.12-.1.03-.2.07-.3.11h-.01c-.01 0-.01 0-.02.01-.23.09-.46.2-.68.31 0 0-.01 0-.01.01L18 14.68z"
+        android:fillColor="#FF000000"
+    />
+</vector>
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
index 8247c27..36d0659 100644
--- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -16,7 +16,7 @@
 -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="#bbbbbb" />
+    <solid android:color="#4a4a4a" />
     <padding android:padding="@dimen/ongoing_appops_chip_bg_padding" />
     <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
index ddefb6a..cbdd51b 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -18,11 +18,14 @@
 <com.android.systemui.privacy.OngoingPrivacyChip
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/privacy_chip"
-    android:layout_width="wrap_content"
     android:layout_height="match_parent"
-    android:layout_margin="@dimen/ongoing_appops_chip_margin"
+    android:layout_width="wrap_content"
+    android:layout_marginLeft="@dimen/ongoing_appops_chip_margin"
+    android:layout_marginRight="@dimen/ongoing_appops_chip_margin"
+    android:layout_marginTop="@dimen/ongoing_appops_top_chip_margin"
+    android:layout_marginBottom="@dimen/ongoing_appops_top_chip_margin"
     android:gravity="center_vertical|center_horizontal"
-    android:layout_gravity="center_vertical|end"
+    android:layout_gravity="center_vertical|start"
     android:orientation="horizontal"
     android:paddingStart="@dimen/ongoing_appops_chip_side_padding"
     android:paddingEnd="@dimen/ongoing_appops_chip_side_padding"
@@ -38,12 +41,17 @@
             />
 
         <TextView
-            android:id="@+id/app_name"
+            android:id="@+id/text_container"
             android:layout_height="match_parent"
             android:layout_width="wrap_content"
             android:singleLine="true"
             android:ellipsize="end"
+            android:lines="1"
             android:layout_gravity="center_vertical|end"
             android:gravity="center_vertical"
+            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+            android:textColor="@color/status_bar_clock_color"
+            android:layout_marginStart="@dimen/ongoing_appops_chip_icon_margin"
+            android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin"
         />
 </com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
index b5e24a0..2f7d486 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
@@ -29,22 +29,30 @@
         android:orientation="vertical"
         android:padding="@dimen/ongoing_appops_dialog_content_padding">
 
-        <LinearLayout
-            android:id="@+id/icons_container"
+        <TextView
+            android:id="@+id/title"
             android:layout_width="match_parent"
-            android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
-            android:orientation="horizontal"
+            android:layout_height="wrap_content"
             android:gravity="center"
-            android:importantForAccessibility="no"
+            android:textDirection="locale"
+            android:textAppearance="@style/TextAppearance.AppOpsDialog.Title"
+            android:textColor="@*android:color/text_color_primary"
+            android:paddingStart="@dimen/ongoing_appops_dialog_title_padding"
+            android:paddingEnd="@dimen/ongoing_appops_dialog_title_padding"
+            android:paddingBottom="@dimen/ongoing_appops_dialog_sep"
         />
 
         <LinearLayout
-            android:id="@+id/text_container"
+            android:id="@+id/items_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="vertical"
             android:gravity="start"
         />
+
+        <include android:id="@+id/overflow" layout="@layout/ongoing_privacy_dialog_item"
+                 android:visibility="gone" />
+
     </LinearLayout>
 
 </ScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
new file mode 100644
index 0000000..f05f7ba
--- /dev/null
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
@@ -0,0 +1,53 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:fillViewport="true"
+    android:orientation="horizontal"
+    android:layout_marginTop="@dimen/ongoing_appops_dialog_text_margin"
+    android:focusable="true" >
+
+    <ImageView
+        android:id="@+id/app_icon"
+        android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+        android:layout_width="@dimen/ongoing_appops_dialog_icon_height"
+    />
+
+    <TextView
+        android:id="@+id/app_name"
+        android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:gravity="bottom|start"
+        android:textDirection="locale"
+        android:textAppearance="@style/TextAppearance.AppOpsDialog.Item"
+        android:textColor="@*android:color/text_color_primary"
+        android:paddingStart="@dimen/ongoing_appops_dialog_text_padding"
+        android:paddingEnd="@dimen/ongoing_appops_dialog_text_padding"
+
+    />
+
+    <LinearLayout
+        android:id="@+id/icons"
+        android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+        android:layout_width="wrap_content"
+        android:gravity="end"
+        android:visibility="gone"
+    />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index e7f2c51..22b8d2f 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -59,7 +59,7 @@
         android:layout_height="match_parent"
         android:layout_weight="1"
         android:orientation="horizontal"
-        android:gravity="center_vertical|end">
+        android:gravity="center_vertical|end" >
 
     <include layout="@layout/ongoing_privacy_chip" />
 
@@ -67,6 +67,7 @@
         android:id="@+id/battery"
         android:layout_height="match_parent"
         android:layout_width="wrap_content"
-        android:gravity="center_vertical|end" />
+        android:gravity="center_vertical|end"
+        android:layout_gravity="center_vertical|end" />
     </LinearLayout>
 </LinearLayout>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index bb0c6f6..df858f0 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -34,4 +34,5 @@
     <bool name="quick_settings_wide">true</bool>
     <dimen name="qs_detail_margin_top">0dp</dimen>
     <dimen name="qs_paged_tile_layout_padding_bottom">0dp</dimen>
+    <dimen name="ongoing_appops_top_chip_margin">2dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d4e6987..97f5f86 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -940,18 +940,34 @@
          that just start below the notch. -->
     <dimen name="display_cutout_touchable_region_size">12dp</dimen>
 
+    <!-- Padding below Ongoing App Ops dialog title -->
+    <dimen name="ongoing_appops_dialog_sep">16dp</dimen>
+    <!--Padding around text items in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_text_padding">16dp</dimen>
     <!-- Height of icons in Ongoing App Ops dialog. Both App Op icon and application icon -->
-    <dimen name="ongoing_appops_dialog_icon_height">48dp</dimen>
+    <dimen name="ongoing_appops_dialog_icon_height">28dp</dimen>
     <!-- Margin between text lines in Ongoing App Ops dialog -->
     <dimen name="ongoing_appops_dialog_text_margin">15dp</dimen>
+    <!-- Side padding of title in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_title_padding">10dp</dimen>
     <!-- Padding around Ongoing App Ops dialog content -->
     <dimen name="ongoing_appops_dialog_content_padding">24dp</dimen>
-    <!-- Margins around the Ongoing App Ops chip. In landscape, the side margins are 0 -->
+    <!-- Side margins around the Ongoing App Ops chip-->
     <dimen name="ongoing_appops_chip_margin">12dp</dimen>
+    <!-- Top and bottom margins around the Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_top_chip_margin">12dp</dimen>
     <!-- Start and End padding for Ongoing App Ops chip -->
     <dimen name="ongoing_appops_chip_side_padding">6dp</dimen>
     <!-- Padding between background of Ongoing App Ops chip and content -->
-    <dimen name="ongoing_appops_chip_bg_padding">4dp</dimen>
+    <dimen name="ongoing_appops_chip_bg_padding">0dp</dimen>
+    <!-- Margin between icons of Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_icon_margin">4dp</dimen>
+    <!-- Icon size of Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_icon_size">18dp</dimen>
     <!-- Radius of Ongoing App Ops chip corners -->
     <dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
+    <!-- Text size for Ongoing App Ops dialog title -->
+    <dimen name="ongoing_appops_dialog_title_size">24sp</dimen>
+    <!-- Text size for Ongoing App Ops dialog items -->
+    <dimen name="ongoing_appops_dialog_item_size">20sp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 50454fc..4a0bc9b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2250,39 +2250,48 @@
          app for debugging. Will not be seen by users. [CHAR LIMIT=20] -->
     <string name="heap_dump_tile_name">Dump SysUI Heap</string>
 
+    <!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=10] -->
+    <string name="ongoing_privacy_chip_multiple_apps"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</string>
+
     <!-- Content description for ongoing privacy chip. Use with a single app [CHAR LIMIT=NONE]-->
     <string name="ongoing_privacy_chip_content_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g>.</string>
 
     <!-- Content description for ongoing privacy chip. Use with multiple apps [CHAR LIMIT=NONE]-->
     <string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
 
-    <!-- Action on Ongoing Privacy Dialog to open application [CHAR LIMIT=10]-->
-    <string name="ongoing_privacy_dialog_open_app">Open app</string>
+    <!-- Content description for ongoing privacy chip. Use with multiple apps using same app op[CHAR LIMIT=NONE]-->
+    <string name="ongoing_privacy_chip_content_multiple_apps_single_op"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</string>
 
     <!-- Action on Ongoing Privacy Dialog to dismiss [CHAR LIMIT=10]-->
     <string name="ongoing_privacy_dialog_cancel">Cancel</string>
 
-    <!-- Action on Ongoing Privacy Dialog to dismiss [CHAR LIMIT=10]-->
-    <string name="ongoing_privacy_dialog_okay">Okay</string>
+    <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=15]-->
+    <string name="ongoing_privacy_dialog_open_settings">View details</string>
 
-    <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=10]-->
-    <string name="ongoing_privacy_dialog_open_settings">Settings</string>
+    <!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
+    <string name="ongoing_privacy_dialog_single_app_title">App using your <xliff:g id="types_list" example="camera( and location)">%s</xliff:g></string>
 
-    <!-- Text for item in Ongoing Privacy Dialog when only one app is using a particular type of app op [CHAR LIMIT=NONE] -->
-    <string name="ongoing_privacy_dialog_app_item"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="type" example="camera">%2$s</xliff:g> for the last <xliff:g id="time" example="3">%3$d</xliff:g> min</string>
+    <!-- Text for item in Ongoing Privacy Dialog title when multiple apps is using app ops [CHAR LIMIT=NONE] -->
+    <string name="ongoing_privacy_dialog_multiple_apps_title">Apps using your <xliff:g id="types_list" example="camera( and location)">%s</xliff:g></string>
 
-    <!-- Text for item in Ongoing Privacy Dialog when only multiple apps are using a particular type of app op [CHAR LIMIT=NONE] -->
-    <string name="ongoing_privacy_dialog_apps_item"><xliff:g id="apps" example="Camera, Phone">%1$s</xliff:g> are using your <xliff:g id="type" example="camera">%2$s</xliff:g></string>
+    <!-- Separator for types. Include spaces before and after if needed [CHAR LIMIT=10] -->
+    <string name="ongoing_privacy_dialog_separator">,\u0020</string>
 
-    <!-- Text for Ongoing Privacy Dialog when a single app is using app ops [CHAR LIMIT=NONE] -->
-    <string name="ongoing_privacy_dialog_single_app"><xliff:g id="app" example="Example App">%1$s</xliff:g> is using your <xliff:g id="types_list" example="camera, location">%2$s</xliff:g></string>
+    <!-- Separator for types, before last type. Include spaces before and after if needed [CHAR LIMIT=10] -->
+    <string name="ongoing_privacy_dialog_last_separator">\u0020and\u0020</string>
 
-    <!-- Text for camera app op [CHAR LIMIT=12]-->
+    <!-- Text for camera app op [CHAR LIMIT=20]-->
     <string name="privacy_type_camera">camera</string>
 
-    <!-- Text for location app op [CHAR LIMIT=12]-->
+    <!-- Text for location app op [CHAR LIMIT=20]-->
     <string name="privacy_type_location">location</string>
 
-    <!-- Text for microphone app op [CHAR LIMIT=12]-->
+    <!-- Text for microphone app op [CHAR LIMIT=20]-->
     <string name="privacy_type_microphone">microphone</string>
+
+    <!-- Text for indicating extra apps using app ops [CHAR LIMIT=NONE] -->
+    <plurals name="ongoing_privacy_dialog_overflow_text">
+        <item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> other app</item>
+        <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> other app</item>
+    </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 95fd86f..e9aa1b6 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -253,6 +253,18 @@
         <item name="android:textSize">@dimen/qs_carrier_info_text_size</item>
     </style>
 
+    <style name="TextAppearance.AppOpsDialog" />
+
+    <style name="TextAppearance.AppOpsDialog.Title">
+        <item name="android:textSize">@dimen/ongoing_appops_dialog_title_size</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+    </style>
+
+    <style name="TextAppearance.AppOpsDialog.Item">
+        <item name="android:textSize">@dimen/ongoing_appops_dialog_item_size</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+    </style>
+
     <style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index e3584cf..3666400 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -88,8 +88,6 @@
     private Runnable mWatchLongPress;
     private final long mLongPressTimeout;
 
-    protected boolean mSwipingInProgress;
-
     final private int[] mTmpPos = new int[2];
     private final int mFalsingThreshold;
     private boolean mTouchAboveFalsingThreshold;
@@ -130,10 +128,6 @@
         mDisableHwLayers = disableHwLayers;
     }
 
-    public boolean isSwipingInProgress() {
-        return mSwipingInProgress;
-    }
-
     private float getPos(MotionEvent ev) {
         return mSwipeDirection == X ? ev.getX() : ev.getY();
     }
@@ -325,7 +319,6 @@
                     if (Math.abs(delta) > mPagingTouchSlop
                             && Math.abs(delta) > Math.abs(deltaPerpendicular)) {
                         if (mCallback.canChildBeDragged(mCurrView)) {
-                            mSwipingInProgress = true;
                             mCallback.onBeginDrag(mCurrView);
                             mDragging = true;
                             mInitialTouchPos = getPos(ev);
@@ -445,7 +438,6 @@
                     wasRemoved = row.isRemoved();
                 }
                 if (!mCancelled || wasRemoved) {
-                    mSwipingInProgress = false;
                     mCallback.onChildDismissed(animView);
                 }
                 if (endAction != null) {
@@ -637,7 +629,6 @@
                                 !swipedFastEnough() /* useAccelerateInterpolator */);
                     } else {
                         // snappity
-                        mSwipingInProgress = false;
                         mCallback.onDragCancelled(mCurrView);
                         snapChild(mCurrView, 0 /* leftTarget */, velocity);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index fc1baef..d3715d0 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -15,7 +15,6 @@
 package com.android.systemui.privacy
 
 import android.content.Context
-import android.graphics.Color
 import android.util.AttributeSet
 import android.view.ViewGroup
 import android.widget.ImageView
@@ -30,7 +29,13 @@
     defStyleRes: Int = 0
 ) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) {
 
-    private lateinit var appName: TextView
+    private val iconMargin =
+            context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin)
+    private val iconSize =
+            context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
+    val iconColor = context.resources.getColor(
+            R.color.status_bar_clock_color, context.theme)
+    private lateinit var text: TextView
     private lateinit var iconsContainer: LinearLayout
     var builder = PrivacyDialogBuilder(context, emptyList<PrivacyItem>())
     var privacyList = emptyList<PrivacyItem>()
@@ -43,7 +48,7 @@
     override fun onFinishInflate() {
         super.onFinishInflate()
 
-        appName = findViewById(R.id.app_name)
+        text = findViewById(R.id.text_container)
         iconsContainer = findViewById(R.id.icons_container)
     }
 
@@ -53,39 +58,52 @@
             iconsContainer.removeAllViews()
             dialogBuilder.generateIcons().forEach {
                 it.mutate()
-                it.setTint(Color.WHITE)
-                iconsContainer.addView(ImageView(context).apply {
+                it.setTint(iconColor)
+                val image = ImageView(context).apply {
                     setImageDrawable(it)
-                    maxHeight = this@OngoingPrivacyChip.height
-                })
+                    scaleType = ImageView.ScaleType.CENTER_INSIDE
+                }
+                iconsContainer.addView(image, iconSize, iconSize)
+                val lp = image.layoutParams as MarginLayoutParams
+                lp.marginStart = iconMargin
+                image.layoutParams = lp
             }
         }
 
-        if (privacyList.isEmpty()) {
-            return
-        } else {
+        if (!privacyList.isEmpty()) {
             generateContentDescription()
             setIcons(builder, iconsContainer)
-            appName.visibility = GONE
-            builder.app?.let {
-                appName.apply {
-                    setText(it.applicationName)
-                    setTextColor(Color.WHITE)
-                    visibility = VISIBLE
+            text.visibility = if (builder.types.size == 1) VISIBLE else GONE
+            if (builder.types.size == 1) {
+                if (builder.app != null) {
+                    text.setText(builder.app?.applicationName)
+                } else {
+                    text.text = context.getString(R.string.ongoing_privacy_chip_multiple_apps,
+                            builder.appsAndTypes.size)
                 }
             }
+        } else {
+            text.visibility = GONE
+            iconsContainer.removeAllViews()
         }
         requestLayout()
     }
 
     private fun generateContentDescription() {
-        val typesText = builder.generateTypesText()
-        if (builder.app != null) {
-            contentDescription = context.getString(R.string.ongoing_privacy_chip_content_single_app,
-                    builder.app?.applicationName, typesText)
-        } else {
+        val typesText = builder.joinTypes()
+        if (builder.types.size > 1) {
             contentDescription = context.getString(
                     R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
+        } else {
+            if (builder.app != null) {
+                contentDescription =
+                        context.getString(R.string.ongoing_privacy_chip_content_single_app,
+                                builder.app?.applicationName, typesText)
+            } else {
+                contentDescription = context.getString(
+                        R.string.ongoing_privacy_chip_content_multiple_apps_single_op,
+                        builder.appsAndTypes.size, typesText)
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index 1d0e16e..f6a95af 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -18,10 +18,10 @@
 import android.app.Dialog
 import android.content.Context
 import android.content.DialogInterface
-import android.graphics.drawable.Drawable
+import android.content.Intent
+import android.content.res.ColorStateList
 import android.view.LayoutInflater
 import android.view.View
-import android.view.ViewGroup
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.TextView
@@ -34,29 +34,25 @@
     val dialogBuilder: PrivacyDialogBuilder
 ) {
 
-    val iconHeight = context.resources.getDimensionPixelSize(
+    val iconSize = context.resources.getDimensionPixelSize(
             R.dimen.ongoing_appops_dialog_icon_height)
-    val textMargin = context.resources.getDimensionPixelSize(
-            R.dimen.ongoing_appops_dialog_text_margin)
     val iconColor = context.resources.getColor(
             com.android.internal.R.color.text_color_primary, context.theme)
+    companion object {
+        private const val MAX_ITEMS = 10
+    }
 
     fun createDialog(): Dialog {
-        val builder = AlertDialog.Builder(context)
-                .setNeutralButton(R.string.ongoing_privacy_dialog_open_settings, null)
-        if (dialogBuilder.app != null) {
-            builder.setPositiveButton(R.string.ongoing_privacy_dialog_open_app,
+        val builder = AlertDialog.Builder(context).apply {
+            setNegativeButton(R.string.ongoing_privacy_dialog_cancel, null)
+            setPositiveButton(R.string.ongoing_privacy_dialog_open_settings,
                     object : DialogInterface.OnClickListener {
-                        val intent = context.packageManager
-                                .getLaunchIntentForPackage(dialogBuilder.app.packageName)
+                        val intent = Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE)
 
                         override fun onClick(dialog: DialogInterface?, which: Int) {
                             Dependency.get(ActivityStarter::class.java).startActivity(intent, false)
                         }
                     })
-            builder.setNegativeButton(R.string.ongoing_privacy_dialog_cancel, null)
-        } else {
-            builder.setPositiveButton(R.string.ongoing_privacy_dialog_okay, null)
         }
         builder.setView(getContentView())
         return builder.create()
@@ -66,44 +62,67 @@
         val layoutInflater = LayoutInflater.from(context)
         val contentView = layoutInflater.inflate(R.layout.ongoing_privacy_dialog_content, null)
 
-        val iconsContainer = contentView.findViewById(R.id.icons_container) as LinearLayout
-        val textContainer = contentView.findViewById(R.id.text_container) as LinearLayout
+        val title = contentView.findViewById(R.id.title) as TextView
+        val appsList = contentView.findViewById(R.id.items_container) as LinearLayout
 
-        addIcons(dialogBuilder, iconsContainer)
-        val lm = ViewGroup.MarginLayoutParams(
-                ViewGroup.MarginLayoutParams.WRAP_CONTENT,
-                ViewGroup.MarginLayoutParams.WRAP_CONTENT)
-        lm.topMargin = textMargin
-        val now = System.currentTimeMillis()
-        dialogBuilder.generateText(now).forEach {
-            val text = layoutInflater.inflate(R.layout.ongoing_privacy_text_item, null) as TextView
-            text.setText(it)
-            textContainer.addView(text, lm)
+        title.setText(dialogBuilder.getDialogTitle())
+
+        val numItems = dialogBuilder.appsAndTypes.size
+        for (i in 0..(numItems - 1)) {
+            if (i >= MAX_ITEMS) break
+            val item = dialogBuilder.appsAndTypes[i]
+            addAppItem(appsList, item.first, item.second, dialogBuilder.types.size > 1)
         }
+
+        if (numItems > MAX_ITEMS) {
+            val overflow = contentView.findViewById(R.id.overflow) as LinearLayout
+            overflow.visibility = View.VISIBLE
+            val overflowText = overflow.findViewById(R.id.app_name) as TextView
+            overflowText.text = context.resources.getQuantityString(
+                    R.plurals.ongoing_privacy_dialog_overflow_text,
+                    numItems - MAX_ITEMS,
+                    numItems - MAX_ITEMS
+            )
+            val overflowPlus = overflow.findViewById(R.id.app_icon) as ImageView
+            overflowPlus.apply {
+                imageTintList = ColorStateList.valueOf(iconColor)
+                setImageDrawable(context.getDrawable(R.drawable.plus))
+            }
+        }
+
         return contentView
     }
 
-    private fun addIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: LinearLayout) {
+    private fun addAppItem(
+        itemList: LinearLayout,
+        app: PrivacyApplication,
+        types: List<PrivacyType>,
+        showIcons: Boolean = true
+    ) {
+        val layoutInflater = LayoutInflater.from(context)
+        val item = layoutInflater.inflate(R.layout.ongoing_privacy_dialog_item, itemList, false)
+        val appIcon = item.findViewById(R.id.app_icon) as ImageView
+        val appName = item.findViewById(R.id.app_name) as TextView
+        val icons = item.findViewById(R.id.icons) as LinearLayout
 
-        fun LinearLayout.addIcon(icon: Drawable) {
-            val image = ImageView(context).apply {
-                setImageDrawable(icon.apply {
-                    setBounds(0, 0, iconHeight, iconHeight)
-                    maxHeight = this@addIcon.height
-                })
-                adjustViewBounds = true
+        app.icon?.let {
+            appIcon.setImageDrawable(it)
+        }
+
+        appName.text = app.applicationName
+        if (showIcons) {
+            dialogBuilder.generateIconsForApp(types).forEach {
+                it.setBounds(0, 0, iconSize, iconSize)
+                val image = ImageView(context).apply {
+                    imageTintList = ColorStateList.valueOf(iconColor)
+                    setImageDrawable(it)
+                }
+                icons.addView(image, iconSize, LinearLayout.LayoutParams.WRAP_CONTENT)
             }
-            addView(image, LinearLayout.LayoutParams.WRAP_CONTENT,
-                    LinearLayout.LayoutParams.MATCH_PARENT)
+            icons.visibility = View.VISIBLE
+        } else {
+            icons.visibility = View.GONE
         }
-
-        dialogBuilder.generateIcons().forEach {
-            it.mutate()
-            it.setTint(iconColor)
-            iconsContainer.addIcon(it)
-        }
-        dialogBuilder.app.let {
-            it?.icon?.let { iconsContainer.addIcon(it) }
-        }
+        itemList.addView(item)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
index 5ce4ee7..519df19 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
@@ -15,59 +15,53 @@
 package com.android.systemui.privacy
 
 import android.content.Context
+import android.graphics.drawable.Drawable
 import com.android.systemui.R
-import java.lang.Math.max
 
 class PrivacyDialogBuilder(val context: Context, itemsList: List<PrivacyItem>) {
-    companion object {
-        val MILLIS_IN_MINUTE: Long = 1000 * 60
-    }
 
-    private val itemsByType: Map<PrivacyType, List<PrivacyItem>>
+    val appsAndTypes: List<Pair<PrivacyApplication, List<PrivacyType>>>
+    val types: List<PrivacyType>
     val app: PrivacyApplication?
+    private val separator = context.getString(R.string.ongoing_privacy_dialog_separator)
+    private val lastSeparator = context.getString(R.string.ongoing_privacy_dialog_last_separator)
 
     init {
-        itemsByType = itemsList.groupBy { it.privacyType }
-        val apps = itemsList.map { it.application }.distinct()
-        val singleApp = apps.size == 1
-        app = if (singleApp) apps.get(0) else null
+        appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
+                .toList()
+                .sortedWith(compareBy({ -it.second.size }, { it.first }))
+        types = itemsList.map { it.privacyType }.distinct().sorted()
+        val singleApp = appsAndTypes.size == 1
+        app = if (singleApp) appsAndTypes[0].first else null
     }
 
-    private fun buildTextForItem(type: PrivacyType, now: Long): String {
-        val items = itemsByType.getOrDefault(type, emptyList<PrivacyItem>())
-        return when (items.size) {
-            0 -> throw IllegalStateException("List cannot be empty")
-            1 -> {
-                val item = items.get(0)
-                val minutesUsed = max(((now - item.timeStarted) / MILLIS_IN_MINUTE).toInt(), 1)
-                context.getString(R.string.ongoing_privacy_dialog_app_item,
-                        item.application.applicationName, type.getName(context), minutesUsed)
-            }
-            else -> {
-                val apps = items.map { it.application.applicationName }.joinToString()
-                context.getString(R.string.ongoing_privacy_dialog_apps_item,
-                        apps, type.getName(context))
-            }
+    fun generateIconsForApp(types: List<PrivacyType>): List<Drawable> {
+        return types.sorted().map { it.getIcon(context) }
+    }
+
+    fun generateIcons() = types.map { it.getIcon(context) }
+
+    private fun <T> List<T>.joinWithAnd(): StringBuilder {
+        return subList(0, size - 1).joinTo(StringBuilder(), separator = separator).apply {
+            append(lastSeparator)
+            append(this@joinWithAnd.last())
         }
     }
 
-    private fun buildTextForApp(types: Set<PrivacyType>): List<String> {
-        app?.let {
-            val typesText = types.map { it.getName(context) }.sorted().joinToString()
-            return listOf(context.getString(R.string.ongoing_privacy_dialog_single_app,
-                    it.applicationName, typesText))
-        } ?: throw IllegalStateException("There has to be a single app")
+    fun joinTypes(): String {
+        return when (types.size) {
+            0 -> ""
+            1 -> types[0].getName(context)
+            else -> types.map { it.getName(context) }.joinWithAnd().toString()
+        }
     }
 
-    fun generateText(now: Long): List<String> {
-        if (app == null || itemsByType.keys.size == 1) {
-            return itemsByType.keys.map { buildTextForItem(it, now) }
+    fun getDialogTitle(): String {
+        if (app != null) {
+            return context.getString(R.string.ongoing_privacy_dialog_single_app_title, joinTypes())
         } else {
-            return buildTextForApp(itemsByType.keys)
+            return context.getString(R.string.ongoing_privacy_dialog_multiple_apps_title,
+                    joinTypes())
         }
     }
-
-    fun generateTypesText() = itemsByType.keys.map { it.getName(context) }.sorted().joinToString()
-
-    fun generateIcons() = itemsByType.keys.map { it.getIcon(context) }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index 3d9aa0f..85e99f0 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -29,16 +29,21 @@
 
     fun getName(context: Context) = context.resources.getString(nameId)
 
-    fun getIcon(context: Context) = context.resources.getDrawable(iconId, null)
+    fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme)
 }
 
 data class PrivacyItem(
     val privacyType: PrivacyType,
-    val application: PrivacyApplication,
-    val timeStarted: Long
+    val application: PrivacyApplication
 )
 
-data class PrivacyApplication(val packageName: String, val context: Context) {
+data class PrivacyApplication(val packageName: String, val context: Context)
+    : Comparable<PrivacyApplication> {
+
+    override fun compareTo(other: PrivacyApplication): Int {
+        return applicationName.compareTo(other.applicationName)
+    }
+
     var icon: Drawable? = null
     var applicationName: String
 
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 5141e50..3fa3e8e 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -95,7 +95,7 @@
             else -> return null
         }
         val app = PrivacyApplication(appOpItem.packageName, context)
-        return PrivacyItem(type, app, appOpItem.timeStarted)
+        return PrivacyItem(type, app)
     }
 
     // Used by containing class to get notified of changes
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index e3f85d9..427f638 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -325,15 +325,10 @@
                 newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
         mBatteryMeterView.useWallpaperTextColor(shouldUseWallpaperTextColor);
         mClockView.useWallpaperTextColor(shouldUseWallpaperTextColor);
-
-        MarginLayoutParams lm = (MarginLayoutParams) mPrivacyChip.getLayoutParams();
-        int sideMargins = lm.leftMargin;
-        int topBottomMargins = (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
-                ? 0 : sideMargins;
-        lm.setMargins(sideMargins, topBottomMargins, sideMargins, topBottomMargins);
-        mPrivacyChip.setLayoutParams(lm);
     }
 
+
+
     @Override
     public void onRtlPropertiesChanged(int layoutDirection) {
         super.onRtlPropertiesChanged(layoutDirection);
@@ -378,6 +373,15 @@
 
         setLayoutParams(lp);
 
+        if (mPrivacyChip != null) {
+            MarginLayoutParams lm = (MarginLayoutParams) mPrivacyChip.getLayoutParams();
+            int sideMargins = lm.leftMargin;
+            int topBottomMargins = resources.getDimensionPixelSize(
+                    R.dimen.ongoing_appops_top_chip_margin);
+            lm.setMargins(sideMargins, topBottomMargins, sideMargins, topBottomMargins);
+            mPrivacyChip.setLayoutParams(lm);
+        }
+
         updateStatusIconAlphaAnimator();
         updateHeaderTextContainerAlphaAnimator();
     }
@@ -729,7 +733,8 @@
     public void setMargins(int sideMargins) {
         for (int i = 0; i < getChildCount(); i++) {
             View v = getChildAt(i);
-            if (v == mSystemIconsView || v == mQuickQsStatusIcons || v == mHeaderQsPanel) {
+            if (v == mSystemIconsView || v == mQuickQsStatusIcons || v == mHeaderQsPanel
+                    || v == mPrivacyChip) {
                 continue;
             }
             RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) v.getLayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
index f42c6ef..f23ae3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -53,7 +53,8 @@
 
     public static NotificationUiAdjustment extractFromNotificationEntry(
             NotificationData.Entry entry) {
-        return new NotificationUiAdjustment(entry.key, entry.smartActions, entry.smartReplies);
+        return new NotificationUiAdjustment(
+                entry.key, entry.systemGeneratedSmartActions, entry.smartReplies);
     }
 
     public static boolean needReinflate(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index 4e712a5..da6d977 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -114,8 +114,9 @@
         public CharSequence remoteInputText;
         public List<SnoozeCriterion> snoozeCriteria;
         public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
+        /** Smart Actions provided by the NotificationAssistantService. */
         @NonNull
-        public List<Notification.Action> smartActions = Collections.emptyList();
+        public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
         public CharSequence[] smartReplies = new CharSequence[0];
 
         private int mCachedContrastColor = COLOR_INVALID;
@@ -171,7 +172,7 @@
             importance = ranking.getImportance();
             snoozeCriteria = ranking.getSnoozeCriteria();
             userSentiment = ranking.getUserSentiment();
-            smartActions = ranking.getSmartActions() == null
+            systemGeneratedSmartActions = ranking.getSmartActions() == null
                     ? Collections.emptyList() : ranking.getSmartActions();
             smartReplies = ranking.getSmartReplies() == null
                     ? new CharSequence[0]
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 3bea7db..274d4b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -702,7 +702,6 @@
                 && !mPresenter.isPresenterFullyCollapsed();
         row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
         row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
-        row.setSmartActions(entry.smartActions);
         row.setEntry(entry);
 
         row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, shouldHeadsUp(entry));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 5166e06..c7876cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -39,7 +39,6 @@
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.Notification;
 import android.app.NotificationChannel;
 import android.content.Context;
 import android.content.pm.PackageInfo;
@@ -1567,10 +1566,6 @@
         mNotificationInflater.setUsesIncreasedHeight(use);
     }
 
-    public void setSmartActions(List<Notification.Action> smartActions) {
-        mNotificationInflater.setSmartActions(smartActions);
-    }
-
     public void setUseIncreasedHeadsUpHeight(boolean use) {
         mUseIncreasedHeadsUpHeight = use;
         mNotificationInflater.setUsesIncreasedHeadsUpHeight(use);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
index 38d6b35..e1c2f73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -43,10 +43,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadFactory;
@@ -131,7 +128,6 @@
     private boolean mIsChildInGroup;
     private InflationCallback mCallback;
     private boolean mRedactAmbient;
-    private List<Notification.Action> mSmartActions;
     private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
 
     public NotificationInflater(ExpandableNotificationRow row) {
@@ -161,10 +157,6 @@
         mUsesIncreasedHeight = usesIncreasedHeight;
     }
 
-    public void setSmartActions(List<Notification.Action> smartActions) {
-        mSmartActions = smartActions;
-    }
-
     public void setUsesIncreasedHeadsUpHeight(boolean usesIncreasedHeight) {
         mUsesIncreasedHeadsUpHeight = usesIncreasedHeight;
     }
@@ -258,8 +250,7 @@
         StatusBarNotification sbn = mRow.getEntry().notification;
         AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mCachedContentViews,
                 mRow, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
-                mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler,
-                mSmartActions);
+                mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler);
         if (mCallback != null && mCallback.doInflateSynchronous()) {
             task.onPostExecute(task.doInBackground());
         } else {
@@ -765,15 +756,13 @@
         private Exception mError;
         private RemoteViews.OnClickHandler mRemoteViewClickHandler;
         private CancellationSignal mCancellationSignal;
-        private List<Notification.Action> mSmartActions;
 
         private AsyncInflationTask(StatusBarNotification notification,
                 @InflationFlag int reInflateFlags,
                 ArrayMap<Integer, RemoteViews> cachedContentViews, ExpandableNotificationRow row,
                 boolean isLowPriority, boolean isChildInGroup, boolean usesIncreasedHeight,
                 boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
-                InflationCallback callback, RemoteViews.OnClickHandler remoteViewClickHandler,
-                List<Notification.Action> smartActions) {
+                InflationCallback callback, RemoteViews.OnClickHandler remoteViewClickHandler) {
             mRow = row;
             mSbn = notification;
             mReInflateFlags = reInflateFlags;
@@ -786,9 +775,6 @@
             mRedactAmbient = redactAmbient;
             mRemoteViewClickHandler = remoteViewClickHandler;
             mCallback = callback;
-            mSmartActions = smartActions == null
-                    ? Collections.emptyList()
-                    : new ArrayList<>(smartActions);
             NotificationData.Entry entry = row.getEntry();
             entry.setInflationTask(this);
         }
@@ -806,8 +792,6 @@
                         = Notification.Builder.recoverBuilder(mContext,
                         mSbn.getNotification());
 
-                applyChanges(recoveredBuilder);
-
                 Context packageContext = mSbn.getPackageContext(mContext);
                 Notification notification = mSbn.getNotification();
                 if (notification.isMediaNotification()) {
@@ -834,18 +818,6 @@
             }
         }
 
-        /**
-         * Apply changes to the given notification builder, like adding smart actions suggested by
-         * a {@link android.service.notification.NotificationAssistantService}.
-         */
-        private void applyChanges(Notification.Builder builder) {
-            if (mSmartActions != null) {
-                for (Notification.Action smartAction : mSmartActions) {
-                    builder.addAction(smartAction);
-                }
-            }
-        }
-
         private void handleError(Exception e) {
             mRow.getEntry().onInflationTaskFinished();
             StatusBarNotification sbn = mRow.getStatusBarNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 2bc4f02..c16b28f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -17,13 +17,16 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
+import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_BLOCK;
 import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;
+import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_TOGGLE_SILENT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.Nullable;
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
@@ -41,6 +44,7 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.AlphaOptimizedImageView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
 import com.android.systemui.statusbar.notification.row.NotificationInfo.NotificationInfoAction;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -69,7 +73,7 @@
 
     private Context mContext;
     private FrameLayout mMenuContainer;
-    private MenuItem mInfoItem;
+    private NotificationInfoMenuItem mInfoItem;
     private MenuItem mAppOpsItem;
     private MenuItem mSnoozeItem;
     private ArrayList<MenuItem> mLeftMenuItems;
@@ -172,7 +176,9 @@
     @Override
     public void createMenu(ViewGroup parent, StatusBarNotification sbn) {
         mParent = (ExpandableNotificationRow) parent;
-        createMenuViews(true /* resetState */);
+        createMenuViews(true /* resetState */,
+                sbn != null && (sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE)
+                        != 0);
     }
 
     @Override
@@ -216,7 +222,8 @@
             // Menu hasn't been created yet, no need to do anything.
             return;
         }
-        createMenuViews(!isMenuVisible() /* resetState */);
+        createMenuViews(!isMenuVisible() /* resetState */,
+                (sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0);
     }
 
     @Override
@@ -231,30 +238,47 @@
         mParent.removeListener();
     }
 
-    private void createMenuViews(boolean resetState) {
+    private void createMenuViews(boolean resetState, final boolean isForeground) {
         final Resources res = mContext.getResources();
         mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size);
         mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
         mLeftMenuItems.clear();
         mRightMenuItems.clear();
         // Construct the menu items based on the notification
-        if (mParent != null && mParent.getStatusBarNotification() != null) {
-            int flags = mParent.getStatusBarNotification().getNotification().flags;
-            boolean isForeground = (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
-            if (!isForeground) {
-                // Only show snooze for non-foreground notifications
-                mSnoozeItem = createSnoozeItem(mContext);
-                mLeftMenuItems.add(mSnoozeItem);
-                mRightMenuItems.add(mSnoozeItem);
-            }
+        if (!isForeground) {
+            // Only show snooze for non-foreground notifications
+            mSnoozeItem = createSnoozeItem(mContext);
+            mLeftMenuItems.add(mSnoozeItem);
         }
         mInfoItem = createInfoItem(mContext);
-        mLeftMenuItems.add(mInfoItem);
-        mRightMenuItems.add(mInfoItem);
+        if (!NotificationUtils.useNewInterruptionModel(mContext)) {
+            mLeftMenuItems.add(mInfoItem);
+        }
 
         mAppOpsItem = createAppOpsItem(mContext);
         mLeftMenuItems.add(mAppOpsItem);
-        mRightMenuItems.add(mAppOpsItem);
+
+        if (NotificationUtils.useNewInterruptionModel(mContext)) {
+            if (!mParent.getIsNonblockable()) {
+                mRightMenuItems.add(createBlockItem(mContext, mInfoItem.getGutsView()));
+            }
+            // TODO(kprevas): this is duplicated logic
+            // but it's currently spread across NotificationGutsManager and NotificationInfo.
+            // Try to consolidate and reuse here.
+            boolean canToggleSilent = !mParent.getIsNonblockable()
+                    && !isForeground
+                    && mParent.getEntry().noisy;
+            if (canToggleSilent) {
+                int channelImportance = mParent.getEntry().channel.getImportance();
+                int effectiveImportance =
+                        channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED
+                                ? mParent.getEntry().importance : channelImportance;
+                mRightMenuItems.add(createToggleSilentItem(mContext, mInfoItem.getGutsView(),
+                        effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT));
+            }
+        } else {
+            mRightMenuItems.addAll(mLeftMenuItems);
+        }
 
         populateMenuViews();
         if (resetState) {
@@ -597,7 +621,7 @@
         // TODO -- handle / allow custom menu items!
     }
 
-    public static MenuItem createSnoozeItem(Context context) {
+    static MenuItem createSnoozeItem(Context context) {
         Resources res = context.getResources();
         NotificationSnooze content = (NotificationSnooze) LayoutInflater.from(context)
                 .inflate(R.layout.notification_snooze, null, false);
@@ -607,17 +631,16 @@
         return snooze;
     }
 
-    public static MenuItem createInfoItem(Context context) {
+    static NotificationInfoMenuItem createInfoItem(Context context) {
         Resources res = context.getResources();
         String infoDescription = res.getString(R.string.notification_menu_gear_description);
         NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
                 R.layout.notification_info, null, false);
-        MenuItem info = new NotificationInfoMenuItem(context, infoDescription, infoContent,
+        return new NotificationInfoMenuItem(context, infoDescription, infoContent,
                 R.drawable.ic_settings, ACTION_NONE);
-        return info;
     }
 
-    public static MenuItem createAppOpsItem(Context context) {
+    static MenuItem createAppOpsItem(Context context) {
         AppOpsInfo appOpsContent = (AppOpsInfo) LayoutInflater.from(context).inflate(
                 R.layout.app_ops_info, null, false);
         MenuItem info = new NotificationMenuItem(context, null, appOpsContent,
@@ -625,6 +648,29 @@
         return info;
     }
 
+    private static MenuItem createBlockItem(Context context, NotificationInfo gutsView) {
+        return new NotificationInfoMenuItem(
+                context,
+                context.getResources().getString(R.string.inline_stop_button),
+                gutsView,
+                R.drawable.ic_notification_block,
+                ACTION_BLOCK);
+    }
+
+    private static MenuItem createToggleSilentItem(Context context, NotificationInfo gutsView,
+            boolean isCurrentlySilent) {
+        return new NotificationInfoMenuItem(
+                context,
+                isCurrentlySilent
+                        ? context.getResources().getString(R.string.inline_silent_button_alert)
+                        : context.getResources().getString(R.string.inline_silent_button_silent),
+                gutsView,
+                isCurrentlySilent
+                        ? R.drawable.ic_notifications_alert
+                        : R.drawable.ic_notifications_silence,
+                ACTION_TOGGLE_SILENT);
+    }
+
     private void addMenuView(MenuItem item, ViewGroup parent) {
         View menuView = item.getMenuView();
         if (menuView != null) {
@@ -706,7 +752,8 @@
          * Add a new 'guts' panel. If iconResId < 0 it will not appear in the slow swipe menu
          * but can still be exposed via other affordances.
          */
-        public NotificationMenuItem(Context context, String s, GutsContent content, int iconResId) {
+        public NotificationMenuItem(Context context, String contentDescription, GutsContent content,
+                int iconResId) {
             Resources res = context.getResources();
             int padding = res.getDimensionPixelSize(R.dimen.notification_menu_icon_padding);
             int tint = res.getColor(R.color.notification_gear_color);
@@ -719,7 +766,7 @@
                 iv.setAlpha(1f);
                 mMenuView = iv;
             }
-            mContentDescription = s;
+            mContentDescription = contentDescription;
             mGutsContent = content;
         }
 
@@ -746,11 +793,16 @@
         @NotificationInfoAction
         int mAction;
 
-        public NotificationInfoMenuItem(Context context, String s,
+        public NotificationInfoMenuItem(Context context, String contentDescription,
                 NotificationInfo content, int iconResId,
                 @NotificationInfoAction int action) {
-            super(context, s, content, iconResId);
+            super(context, contentDescription, content, iconResId);
             this.mAction = action;
         }
+
+        @Override
+        public NotificationInfo getGutsView() {
+            return (NotificationInfo) super.getGutsView();
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index a8ced7a..1be2afe7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.ViewTransformationHelper;
 import com.android.systemui.statusbar.notification.CustomInterpolatorTransformation;
 import com.android.systemui.statusbar.notification.ImageTransformState;
+import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.TransformState;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
@@ -69,7 +70,8 @@
     protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
         super(ctx, view, row);
         mShowExpandButtonAtEnd = ctx.getResources().getBoolean(
-                R.bool.config_showNotificationExpandButtonAtEnd);
+                R.bool.config_showNotificationExpandButtonAtEnd)
+                || NotificationUtils.useNewInterruptionModel(ctx);
         mTransformationHelper = new ViewTransformationHelper();
 
         // we want to avoid that the header clashes with the other text when transforming
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index a7329b0..ff31b261 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -401,6 +401,8 @@
      */
     private float mBackgroundXFactor = 1f;
 
+    private boolean mSwipingInProgress;
+
     private boolean mUsingLightTheme;
     private boolean mQsExpanded;
     private boolean mForwardScrollable;
@@ -3286,7 +3288,7 @@
                 || ev.getActionMasked() == MotionEvent.ACTION_UP;
         handleEmptySpaceClick(ev);
         boolean expandWantsIt = false;
-        boolean swipingInProgress = mSwipeHelper.isSwipingInProgress();
+        boolean swipingInProgress = mSwipingInProgress;
         if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) {
             if (isCancelOrUp) {
                 mExpandHelper.onlyObserveMovements(false);
@@ -3341,7 +3343,7 @@
     @Override
     @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean onGenericMotionEvent(MotionEvent event) {
-        if (!isScrollingEnabled() || !mIsExpanded || mSwipeHelper.isSwipingInProgress() || mExpandingNotification
+        if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
                 || mDisallowScrollingInThisMotion) {
             return false;
         }
@@ -3568,7 +3570,7 @@
         initDownStates(ev);
         handleEmptySpaceClick(ev);
         boolean expandWantsIt = false;
-        boolean swipingInProgress = mSwipeHelper.isSwipingInProgress();
+        boolean swipingInProgress = mSwipingInProgress;
         if (!swipingInProgress && !mOnlyScrollingInThisMotion) {
             expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
         }
@@ -3847,6 +3849,14 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void setSwipingInProgress(boolean swiping) {
+        mSwipingInProgress = swiping;
+        if (swiping) {
+            requestDisallowInterceptTouchEvent(true);
+        }
+    }
+
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onWindowFocusChanged(boolean hasWindowFocus) {
@@ -5642,6 +5652,7 @@
 
         @Override
         public void onDragCancelled(View v) {
+            setSwipingInProgress(false);
             mFalsingManager.onNotificatonStopDismissing();
         }
 
@@ -5669,6 +5680,7 @@
          */
 
         public void handleChildViewDismissed(View view) {
+            setSwipingInProgress(false);
             if (mDismissAllInProgress) {
                 return;
             }
@@ -5737,6 +5749,7 @@
         @Override
         public void onBeginDrag(View v) {
             mFalsingManager.onNotificatonStartDismissing();
+            setSwipingInProgress(true);
             mAmbientState.onBeginDrag(v);
             updateContinuousShadowDrawing();
             if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 599da3b..f1d9549 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -165,15 +165,15 @@
 
         if (menuRow.isSnappedAndOnSameSide()) {
             // Menu was snapped to previously and we're on the same side
-            handleSwipeFromSnap(ev, animView, velocity, menuRow);
+            handleSwipeFromOpenState(ev, animView, velocity, menuRow);
         } else {
             // Menu has not been snapped, or was snapped previously but is now on
             // the opposite side.
-            handleSwipeFromNonSnap(ev, animView, velocity, menuRow);
+            handleSwipeFromClosedState(ev, animView, velocity, menuRow);
         }
     }
 
-    private void handleSwipeFromNonSnap(MotionEvent ev, View animView, float velocity,
+    private void handleSwipeFromClosedState(MotionEvent ev, View animView, float velocity,
             NotificationMenuRowPlugin menuRow) {
         boolean isDismissGesture = isDismissGesture(ev);
         final boolean gestureTowardsMenu = menuRow.isTowardsMenu(velocity);
@@ -183,10 +183,14 @@
         final boolean showMenuForSlowOnGoing = !menuRow.canBeDismissed()
                 && timeForGesture >= SWIPE_MENU_TIMING;
 
-        if (!isFalseGesture(ev)
-                && (swipedEnoughToShowMenu(menuRow)
-                && (!gestureFastEnough || showMenuForSlowOnGoing))
-                || (gestureTowardsMenu && !isDismissGesture)) {
+        boolean isNonDismissGestureTowardsMenu = gestureTowardsMenu && !isDismissGesture;
+        boolean isSlowSwipe = !gestureFastEnough || showMenuForSlowOnGoing;
+        boolean slowSwipedFarEnough = swipedEnoughToShowMenu(menuRow) && isSlowSwipe;
+        boolean isFastNonDismissGesture =
+                gestureFastEnough && !gestureTowardsMenu && !isDismissGesture;
+        boolean isMenuRevealingGestureAwayFromMenu = slowSwipedFarEnough || isFastNonDismissGesture;
+        if (isNonDismissGestureTowardsMenu
+                || (!isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu)) {
             // Menu has not been snapped to previously and this is menu revealing gesture
             snapOpen(animView, menuRow.getMenuSnapTarget(), velocity);
             menuRow.onSnapOpen();
@@ -199,7 +203,7 @@
         }
     }
 
-    private void handleSwipeFromSnap(MotionEvent ev, View animView, float velocity,
+    private void handleSwipeFromOpenState(MotionEvent ev, View animView, float velocity,
             NotificationMenuRowPlugin menuRow) {
         boolean isDismissGesture = isDismissGesture(ev);
 
@@ -227,7 +231,6 @@
         if (mCallback.isExpanded()) {
             // We don't want to quick-dismiss when it's a heads up as this might lead to closing
             // of the panel early.
-            mSwipingInProgress = false;
             mCallback.handleChildViewDismissed(view);
         }
         mCallback.onDismiss();
@@ -247,7 +250,6 @@
     @Override
     public void snapChild(final View animView, final float targetLeft, float velocity) {
         superSnapChild(animView, targetLeft, velocity);
-        mSwipingInProgress = false;
         mCallback.onDragCancelled(animView);
         if (targetLeft == 0) {
             handleMenuCoveredOrDismissed();
@@ -354,7 +356,6 @@
 
     public void onMenuShown(View animView) {
         setExposedMenuView(getTranslatingParentView());
-        mSwipingInProgress = false;
         mCallback.onDragCancelled(animView);
         Handler handler = getHandler();
 
@@ -422,4 +423,4 @@
 
         void onDismiss();
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
index 7204d31..b23f667 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
@@ -27,55 +27,28 @@
 @SmallTest
 class PrivacyDialogBuilderTest : SysuiTestCase() {
 
-    companion object {
-        val MILLIS_IN_MINUTE: Long = 1000 * 60
-        val NOW = 4 * MILLIS_IN_MINUTE
-    }
-
     @Test
-    fun testGenerateText_multipleApps() {
+    fun testGenerateAppsList() {
         val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
-                "Bar", context), 2 * MILLIS_IN_MINUTE)
+                "Bar", context))
         val bar3 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
-                "Bar", context), 3 * MILLIS_IN_MINUTE)
+                "Bar", context))
         val foo0 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
-                "Foo", context), 0)
+                "Foo", context))
         val baz1 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
-                "Baz", context), 1 * MILLIS_IN_MINUTE)
+                "Baz", context))
 
         val items = listOf(bar2, foo0, baz1, bar3)
 
         val textBuilder = PrivacyDialogBuilder(context, items)
 
-        val textList = textBuilder.generateText(NOW)
-        assertEquals(2, textList.size)
-        assertEquals("Bar, Foo, Baz are using your camera", textList[0])
-        assertEquals("Bar is using your location for the last 1 min", textList[1])
-    }
-
-    @Test
-    fun testGenerateText_singleApp() {
-        val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
-                "Bar", context), 0)
-        val bar1 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
-                "Bar", context), 0)
-
-        val items = listOf(bar2, bar1)
-
-        val textBuilder = PrivacyDialogBuilder(context, items)
-        val textList = textBuilder.generateText(NOW)
-        assertEquals(1, textList.size)
-        assertEquals("Bar is using your camera, location", textList[0])
-    }
-
-    @Test
-    fun testGenerateText_singleApp_singleType() {
-        val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
-                "Bar", context), 2 * MILLIS_IN_MINUTE)
-        val items = listOf(bar2)
-        val textBuilder = PrivacyDialogBuilder(context, items)
-        val textList = textBuilder.generateText(NOW)
-        assertEquals(1, textList.size)
-        assertEquals("Bar is using your camera for the last 2 min", textList[0])
+        val list = textBuilder.appsAndTypes
+        assertEquals(3, list.size)
+        val appsList = list.map { it.first }
+        val typesList = list.map { it.second }
+        assertEquals(listOf("Bar", "Baz", "Foo"), appsList.map { it.packageName })
+        assertEquals(listOf(Privacy.TYPE_CAMERA, Privacy.TYPE_LOCATION), typesList[0])
+        assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[1])
+        assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[2])
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index 8e6bfe3..f59bfae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -72,6 +72,8 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -390,10 +392,16 @@
     @Test
     public void testCreateNotificationDataEntry_RankingUpdate() {
         Ranking ranking = mock(Ranking.class);
+        initStatusBarNotification(false);
 
-        ArrayList<Notification.Action> smartActions = new ArrayList<>();
-        smartActions.add(createAction());
-        when(ranking.getSmartActions()).thenReturn(smartActions);
+        List<Notification.Action> appGeneratedSmartActions =
+                Collections.singletonList(createContextualAction("appGeneratedAction"));
+        mMockStatusBarNotification.getNotification().actions =
+                appGeneratedSmartActions.toArray(new Notification.Action[0]);
+
+        List<Notification.Action> systemGeneratedSmartActions =
+                Collections.singletonList(createAction("systemGeneratedAction"));
+        when(ranking.getSmartActions()).thenReturn(systemGeneratedSmartActions);
 
         when(ranking.getChannel()).thenReturn(NOTIFICATION_CHANNEL);
 
@@ -407,7 +415,7 @@
         NotificationData.Entry entry =
                 new NotificationData.Entry(mMockStatusBarNotification, ranking);
 
-        assertEquals(smartActions, entry.smartActions);
+        assertEquals(systemGeneratedSmartActions, entry.systemGeneratedSmartActions);
         assertEquals(NOTIFICATION_CHANNEL, entry.channel);
         assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, entry.userSentiment);
         assertEquals(snoozeCriterions, entry.snoozeCriteria);
@@ -459,10 +467,20 @@
         }
     }
 
-    private Notification.Action createAction() {
+    private Notification.Action createContextualAction(String title) {
         return new Notification.Action.Builder(
                 Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
-                "action",
+                title,
+                PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0))
+                        .setSemanticAction(
+                                Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION)
+                        .build();
+    }
+
+    private Notification.Action createAction(String title) {
+        return new Notification.Action.Builder(
+                Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
+                title,
                 PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 9f8a5cc..d1fe5af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -438,8 +438,8 @@
 
         mEntryManager.updateNotificationRanking(mRankingMap);
         verify(mRow).setEntry(eq(mEntry));
-        assertEquals(1, mEntry.smartActions.size());
-        assertEquals("action", mEntry.smartActions.get(0).title);
+        assertEquals(1, mEntry.systemGeneratedSmartActions.size());
+        assertEquals("action", mEntry.systemGeneratedSmartActions.get(0).title);
     }
 
     @Test
@@ -453,7 +453,7 @@
 
         mEntryManager.updateNotificationRanking(mRankingMap);
         verify(mRow, never()).setEntry(eq(mEntry));
-        assertEquals(0, mEntry.smartActions.size());
+        assertEquals(0, mEntry.systemGeneratedSmartActions.size());
     }
 
     @Test
@@ -467,8 +467,8 @@
 
         mEntryManager.updateNotificationRanking(mRankingMap);
         verify(mRow, never()).setEntry(eq(mEntry));
-        assertEquals(1, mEntry.smartActions.size());
-        assertEquals("action", mEntry.smartActions.get(0).title);
+        assertEquals(1, mEntry.systemGeneratedSmartActions.size());
+        assertEquals("action", mEntry.systemGeneratedSmartActions.get(0).title);
     }
 
     @Test
@@ -482,8 +482,8 @@
 
         mEntryManager.updateNotificationRanking(mRankingMap);
         verify(mRow, never()).setEntry(eq(mEntry));
-        assertEquals(1, mEntry.smartActions.size());
-        assertEquals("action", mEntry.smartActions.get(0).title);
+        assertEquals(1, mEntry.systemGeneratedSmartActions.size());
+        assertEquals("action", mEntry.systemGeneratedSmartActions.get(0).title);
     }
 
     private Notification.Action createAction() {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 1ad83ec..c12a5e7 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -104,7 +104,6 @@
 import com.android.server.backup.fullbackup.FullBackupEntry;
 import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
 import com.android.server.backup.internal.BackupHandler;
-import com.android.server.backup.keyvalue.BackupRequest;
 import com.android.server.backup.internal.ClearDataObserver;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.internal.Operation;
@@ -112,6 +111,7 @@
 import com.android.server.backup.internal.ProvisionedObserver;
 import com.android.server.backup.internal.RunBackupReceiver;
 import com.android.server.backup.internal.RunInitializeReceiver;
+import com.android.server.backup.keyvalue.BackupRequest;
 import com.android.server.backup.params.AdbBackupParams;
 import com.android.server.backup.params.AdbParams;
 import com.android.server.backup.params.AdbRestoreParams;
@@ -160,7 +160,6 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 public class BackupManagerService {
-
     public static final String TAG = "BackupManagerService";
     public static final boolean DEBUG = true;
     public static final boolean MORE_DEBUG = false;
@@ -170,6 +169,9 @@
     // nonzero == enabled.  File missing or contains a zero byte == disabled.
     private static final String BACKUP_ENABLE_FILE = "backup_enabled";
 
+    // Persistently track the need to do a full init.
+    private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
+
     // System-private key used for backing up an app's widget state.  Must
     // begin with U+FFxx by convention (we reserve all keys starting
     // with U+FF00 or higher for system use).
@@ -196,11 +198,16 @@
     public static final int BACKUP_METADATA_VERSION = 1;
     public static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
 
-    private static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
+    private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;
+
+    // Round-robin queue for scheduling full backup passes.
+    private static final int SCHEDULE_FILE_VERSION = 1;
 
     public static final String SETTINGS_PACKAGE = "com.android.providers.settings";
     public static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
-    private static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
+
+    // Pseudoname that we use for the Package Manager metadata "package".
+    public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
 
     // Retry interval for clear/init when the transport is unavailable
     private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
@@ -210,6 +217,21 @@
     public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
     public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
 
+    // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
+    // pending operations list.
+    public static final int OP_PENDING = 0;
+    private static final int OP_ACKNOWLEDGED = 1;
+    private static final int OP_TIMEOUT = -1;
+
+    // Waiting for backup agent to respond during backup operation.
+    public static final int OP_TYPE_BACKUP_WAIT = 0;
+
+    // Waiting for backup agent to respond during restore operation.
+    public static final int OP_TYPE_RESTORE_WAIT = 1;
+
+    // An entire backup operation spanning multiple packages.
+    public static final int OP_TYPE_BACKUP = 2;
+
     // Time delay for initialization operations that can be delayed so as not to consume too much CPU
     // on bring-up and increase time-to-UI.
     private static final long INITIALIZATION_DELAY_MILLIS = 3000;
@@ -226,8 +248,62 @@
     private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60;  // one hour
     private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
 
-    private BackupManagerConstants mConstants;
+    // The published binder is a singleton Trampoline object that calls through to the proper code.
+    // This indirection lets us turn down the heavy implementation object on the fly without
+    // disturbing binders that have been cached elsewhere in the system.
+    private static Trampoline sInstance;
+
+    static Trampoline getInstance() {
+        // Always constructed during system bring up, so no need to lazy-init.
+        return sInstance;
+    }
+
+    /** Helper to create the {@link BackupManagerService} instance. */
+    public static BackupManagerService create(
+            Context context,
+            Trampoline parent,
+            HandlerThread backupThread) {
+        // Set up our transport options and initialize the default transport
+        SystemConfig systemConfig = SystemConfig.getInstance();
+        Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
+        if (transportWhitelist == null) {
+            transportWhitelist = Collections.emptySet();
+        }
+
+        String transport =
+                Settings.Secure.getString(
+                        context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+        if (TextUtils.isEmpty(transport)) {
+            transport = null;
+        }
+        if (DEBUG) {
+            Slog.v(TAG, "Starting with transport " + transport);
+        }
+        TransportManager transportManager =
+                new TransportManager(
+                        context,
+                        transportWhitelist,
+                        transport);
+
+        // If encrypted file systems is enabled or disabled, this call will return the
+        // correct directory.
+        File baseStateDir = new File(Environment.getDataDirectory(), "backup");
+
+        // This dir on /cache is managed directly in init.rc
+        File dataDir = new File(Environment.getDownloadCacheDirectory(), "backup_stage");
+
+        return new BackupManagerService(
+                context,
+                parent,
+                backupThread,
+                baseStateDir,
+                dataDir,
+                transportManager);
+    }
+
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+    private final TransportManager mTransportManager;
+
     private Context mContext;
     private PackageManager mPackageManager;
     private IPackageManager mPackageManagerBinder;
@@ -235,20 +311,21 @@
     private PowerManager mPowerManager;
     private AlarmManager mAlarmManager;
     private IStorageManager mStorageManager;
+    private BackupManagerConstants mConstants;
+    private PowerManager.WakeLock mWakelock;
+    private BackupHandler mBackupHandler;
 
     private IBackupManager mBackupManagerBinder;
 
-    private final TransportManager mTransportManager;
-
     private boolean mEnabled;   // access to this is synchronized on 'this'
     private boolean mProvisioned;
     private boolean mAutoRestore;
-    private PowerManager.WakeLock mWakelock;
-    private BackupHandler mBackupHandler;
+
     private PendingIntent mRunBackupIntent;
     private PendingIntent mRunInitIntent;
-    private BroadcastReceiver mRunBackupReceiver;
-    private BroadcastReceiver mRunInitReceiver;
+
+    private final ArraySet<String> mPendingInits = new ArraySet<>();  // transport names
+
     // map UIDs to the set of participating packages under that UID
     private final SparseArray<HashSet<String>> mBackupParticipants
             = new SparseArray<>();
@@ -257,9 +334,6 @@
     private HashMap<String, BackupRequest> mPendingBackups
             = new HashMap<>();
 
-    // Pseudoname that we use for the Package Manager metadata "package"
-    public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
-
     // locking around the pending-backup management
     private final Object mQueueLock = new Object();
 
@@ -269,25 +343,32 @@
     // completed.
     private final Object mAgentConnectLock = new Object();
     private IBackupAgent mConnectedAgent;
-    private volatile boolean mBackupRunning;
     private volatile boolean mConnecting;
-    private volatile long mLastBackupPass;
 
-    // For debugging, we maintain a progress trace of operations during backup
-    public static final boolean DEBUG_BACKUP_TRACE = true;
-    private final List<String> mBackupTrace = new ArrayList<>();
+    private volatile boolean mBackupRunning;
+    private volatile long mLastBackupPass;
 
     // A similar synchronization mechanism around clearing apps' data for restore
     private final Object mClearDataLock = new Object();
     private volatile boolean mClearingData;
 
+    // Used by ADB.
     private final BackupPasswordManager mBackupPasswordManager;
+    private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>();
+    private final SecureRandom mRng = new SecureRandom();
 
     // Time when we post the transport registration operation
     private final long mRegisterTransportsRequestedTime;
 
+    @GuardedBy("mQueueLock")
+    private PerformFullTransportBackupTask mRunningFullBackupTask;
+
+    @GuardedBy("mQueueLock")
+    private ArrayList<FullBackupEntry> mFullBackupQueue;
+
     @GuardedBy("mPendingRestores")
     private boolean mIsRestoreInProgress;
+
     @GuardedBy("mPendingRestores")
     private final Queue<PerformUnifiedRestoreTask> mPendingRestores = new ArrayDeque<>();
 
@@ -296,17 +377,155 @@
     // Watch the device provisioning operation during setup
     private ContentObserver mProvisionedObserver;
 
-    // The published binder is actually to a singleton trampoline object that calls
-    // through to the proper code.  This indirection lets us turn down the heavy
-    // implementation object on the fly without disturbing binders that have been
-    // cached elsewhere in the system.
-    static Trampoline sInstance;
+    /**
+     * mCurrentOperations contains the list of currently active operations.
+     *
+     * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
+     * An operation wraps a BackupRestoreTask within it.
+     * It's the responsibility of this task to remove the operation from this array.
+     *
+     * A BackupRestore task gets notified of ack/timeout for the operation via
+     * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
+     * on the mCurrentOpLock.
+     * {@link BackupManagerService#waitUntilOperationComplete(int)} is
+     * used in various places to 'wait' for notifyAll and detect change of pending state of an
+     * operation. So typically, an operation will be removed from this array by:
+     * - BackupRestoreTask#handleCancel and
+     * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
+     * these places because waitUntilOperationComplete relies on the operation being present to
+     * determine its completion status.
+     *
+     * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
+     * cancel backup tasks.
+     */
+    @GuardedBy("mCurrentOpLock")
+    private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
+    private final Object mCurrentOpLock = new Object();
+    private final Random mTokenGenerator = new Random();
+    final AtomicInteger mNextToken = new AtomicInteger();
 
-    static Trampoline getInstance() {
-        // Always constructed during system bringup, so no need to lazy-init
-        return sInstance;
+    // Where we keep our journal files and other bookkeeping.
+    private File mBaseStateDir;
+    private File mDataDir;
+    private File mJournalDir;
+    @Nullable
+    private DataChangedJournal mJournal;
+    private File mFullBackupScheduleFile;
+
+    // Keep a log of all the apps we've ever backed up.
+    private ProcessedPackagesJournal mProcessedPackagesJournal;
+
+    private File mTokenFile;
+    private Set<String> mAncestralPackages = null;
+    private long mAncestralToken = 0;
+    private long mCurrentToken = 0;
+
+    @VisibleForTesting
+    public BackupManagerService(
+            Context context,
+            Trampoline parent,
+            HandlerThread backupThread,
+            File baseStateDir,
+            File dataDir,
+            TransportManager transportManager) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+        mPackageManagerBinder = AppGlobals.getPackageManager();
+        mActivityManager = ActivityManager.getService();
+
+        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
+
+        mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
+
+        mAgentTimeoutParameters = new
+                BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
+        mAgentTimeoutParameters.start();
+
+        // spin up the backup/restore handler thread
+        mBackupHandler = new BackupHandler(this, backupThread.getLooper());
+
+        // Set up our bookkeeping
+        final ContentResolver resolver = context.getContentResolver();
+        mProvisioned = Settings.Global.getInt(resolver,
+                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+        mAutoRestore = Settings.Secure.getInt(resolver,
+                Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
+
+        mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
+        resolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+                false, mProvisionedObserver);
+
+        mBaseStateDir = baseStateDir;
+        mBaseStateDir.mkdirs();
+        if (!SELinux.restorecon(mBaseStateDir)) {
+            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+        }
+
+        mDataDir = dataDir;
+
+        mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
+
+        // Alarm receivers for scheduled backups & initialization operations
+        BroadcastReceiver mRunBackupReceiver = new RunBackupReceiver(this);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(RUN_BACKUP_ACTION);
+        context.registerReceiver(mRunBackupReceiver, filter,
+                android.Manifest.permission.BACKUP, null);
+
+        BroadcastReceiver mRunInitReceiver = new RunInitializeReceiver(this);
+        filter = new IntentFilter();
+        filter.addAction(RUN_INITIALIZE_ACTION);
+        context.registerReceiver(mRunInitReceiver, filter,
+                android.Manifest.permission.BACKUP, null);
+
+        Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
+        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
+
+        Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
+        initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
+
+        // Set up the backup-request journaling
+        mJournalDir = new File(mBaseStateDir, "pending");
+        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
+        mJournal = null;        // will be created on first use
+
+        mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
+        // We are observing changes to the constants throughout the lifecycle of BMS. This is
+        // because we reference the constants in multiple areas of BMS, which otherwise would
+        // require frequent starting and stopping.
+        mConstants.start();
+
+        // Set up the various sorts of package tracking we do
+        mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
+        initPackageTracking();
+
+        // Build our mapping of uid to backup client services.  This implicitly
+        // schedules a backup pass on the Package Manager metadata the first
+        // time anything needs to be backed up.
+        synchronized (mBackupParticipants) {
+            addPackageParticipantsLocked(null);
+        }
+
+        mTransportManager = transportManager;
+        mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
+        mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
+        mBackupHandler.postDelayed(
+                mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
+
+        // Now that we know about valid backup participants, parse any leftover journal files into
+        // the pending backup set
+        mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
+
+        // Power management
+        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
     }
 
+
     public BackupManagerConstants getConstants() {
         return mConstants;
     }
@@ -549,6 +768,7 @@
         return mPendingInits;
     }
 
+    /** Clear all pending transport initializations. */
     public void clearPendingInits() {
         mPendingInits.clear();
     }
@@ -562,28 +782,10 @@
         mRunningFullBackupTask = runningFullBackupTask;
     }
 
-    public static final class Lifecycle extends SystemService {
-
-        public Lifecycle(Context context) {
-            super(context);
-            sInstance = new Trampoline(context);
-        }
-
-        @Override
-        public void onStart() {
-            publishBinderService(Context.BACKUP_SERVICE, sInstance);
-        }
-
-        @Override
-        public void onUnlockUser(int userId) {
-            if (userId == UserHandle.USER_SYSTEM) {
-                sInstance.unlockSystemUser();
-            }
-        }
-    }
-
-    // Called through the trampoline from onUnlockUser(), then we buck the work
-    // off to the background thread to keep the unlock time down.
+    /**
+     * Called through Trampoline from {@link Lifecycle#onUnlockUser(int)}. We run the heavy work on
+     * a background thread to keep the unlock time down.
+     */
     public void unlockSystemUser() {
         // Migrate legacy setting
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
@@ -618,89 +820,10 @@
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    // Bookkeeping of in-flight operations for timeout etc. purposes.  The operation
-    // token is the index of the entry in the pending-operations list.
-    public static final int OP_PENDING = 0;
-    private static final int OP_ACKNOWLEDGED = 1;
-    private static final int OP_TIMEOUT = -1;
-
-    // Waiting for backup agent to respond during backup operation.
-    public static final int OP_TYPE_BACKUP_WAIT = 0;
-
-    // Waiting for backup agent to respond during restore operation.
-    public static final int OP_TYPE_RESTORE_WAIT = 1;
-
-    // An entire backup operation spanning multiple packages.
-    public static final int OP_TYPE_BACKUP = 2;
-
     /**
-     * mCurrentOperations contains the list of currently active operations.
-     *
-     * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
-     * An operation wraps a BackupRestoreTask within it.
-     * It's the responsibility of this task to remove the operation from this array.
-     *
-     * A BackupRestore task gets notified of ack/timeout for the operation via
-     * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
-     * on the mCurrentOpLock.
-     * {@link BackupManagerService#waitUntilOperationComplete(int)} is
-     * used in various places to 'wait' for notifyAll and detect change of pending state of an
-     * operation. So typically, an operation will be removed from this array by:
-     * - BackupRestoreTask#handleCancel and
-     * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
-     * these places because waitUntilOperationComplete relies on the operation being present to
-     * determine its completion status.
-     *
-     * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
-     * cancel backup tasks.
+     *  Utility: build a new random integer token. The low bits are the ordinal of the operation for
+     *  near-time uniqueness, and the upper bits are random for app-side unpredictability.
      */
-    @GuardedBy("mCurrentOpLock")
-    private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
-    private final Object mCurrentOpLock = new Object();
-    private final Random mTokenGenerator = new Random();
-    final AtomicInteger mNextToken = new AtomicInteger();
-
-    private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>();
-
-    // Where we keep our journal files and other bookkeeping
-    private File mBaseStateDir;
-    private File mDataDir;
-    private File mJournalDir;
-    @Nullable
-    private DataChangedJournal mJournal;
-
-    private final SecureRandom mRng = new SecureRandom();
-
-    // Keep a log of all the apps we've ever backed up, and what the dataset tokens are for both
-    // the current backup dataset and the ancestral dataset.
-    private ProcessedPackagesJournal mProcessedPackagesJournal;
-
-    private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;
-    // increment when the schema changes
-    private File mTokenFile;
-    private Set<String> mAncestralPackages = null;
-    private long mAncestralToken = 0;
-    private long mCurrentToken = 0;
-
-    // Persistently track the need to do a full init
-    private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
-    private final ArraySet<String> mPendingInits = new ArraySet<>();  // transport names
-
-    // Round-robin queue for scheduling full backup passes
-    private static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file
-
-    private File mFullBackupScheduleFile;
-    // If we're running a schedule-driven full backup, this is the task instance doing it
-
-    @GuardedBy("mQueueLock")
-    private PerformFullTransportBackupTask mRunningFullBackupTask;
-
-    @GuardedBy("mQueueLock")
-    private ArrayList<FullBackupEntry> mFullBackupQueue;
-
-    // Utility: build a new random integer token. The low bits are the ordinal of the
-    // operation for near-time uniqueness, and the upper bits are random for app-
-    // side unpredictability.
     public int generateRandomIntegerToken() {
         int token = mTokenGenerator.nextInt();
         if (token < 0) token = -token;
@@ -709,10 +832,9 @@
         return token;
     }
 
-    /*
-     * Construct a backup agent instance for the metadata pseudopackage.  This is a
-     * process-local non-lifecycle agent instance, so we manually set up the context
-     * topology for it.
+    /**
+     * Construct a backup agent instance for the metadata pseudopackage. This is a process-local
+     * non-lifecycle agent instance, so we manually set up the context topology for it.
      */
     public BackupAgent makeMetadataAgent() {
         PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
@@ -721,8 +843,8 @@
         return pmAgent;
     }
 
-    /*
-     * Same as above but with the explicit package-set configuration.
+    /**
+     * Same as {@link #makeMetadataAgent()} but with explicit package-set configuration.
      */
     public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
         PackageManagerBackupAgent pmAgent =
@@ -732,172 +854,6 @@
         return pmAgent;
     }
 
-    // ----- Debug-only backup operation trace -----
-    public void addBackupTrace(String s) {
-        if (DEBUG_BACKUP_TRACE) {
-            synchronized (mBackupTrace) {
-                mBackupTrace.add(s);
-            }
-        }
-    }
-
-    public void clearBackupTrace() {
-        if (DEBUG_BACKUP_TRACE) {
-            synchronized (mBackupTrace) {
-                mBackupTrace.clear();
-            }
-        }
-    }
-
-    // ----- Main service implementation -----
-
-    public static BackupManagerService create(
-            Context context,
-            Trampoline parent,
-            HandlerThread backupThread) {
-        // Set up our transport options and initialize the default transport
-        SystemConfig systemConfig = SystemConfig.getInstance();
-        Set<ComponentName> transportWhitelist = systemConfig.getBackupTransportWhitelist();
-        if (transportWhitelist == null) {
-            transportWhitelist = Collections.emptySet();
-        }
-
-        String transport =
-                Settings.Secure.getString(
-                        context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
-        if (TextUtils.isEmpty(transport)) {
-            transport = null;
-        }
-        if (DEBUG) {
-            Slog.v(TAG, "Starting with transport " + transport);
-        }
-        TransportManager transportManager =
-                new TransportManager(
-                        context,
-                        transportWhitelist,
-                        transport);
-
-        // If encrypted file systems is enabled or disabled, this call will return the
-        // correct directory.
-        File baseStateDir = new File(Environment.getDataDirectory(), "backup");
-
-        // This dir on /cache is managed directly in init.rc
-        File dataDir = new File(Environment.getDownloadCacheDirectory(), "backup_stage");
-
-        return new BackupManagerService(
-                context,
-                parent,
-                backupThread,
-                baseStateDir,
-                dataDir,
-                transportManager);
-    }
-
-    @VisibleForTesting
-    public BackupManagerService(
-            Context context,
-            Trampoline parent,
-            HandlerThread backupThread,
-            File baseStateDir,
-            File dataDir,
-            TransportManager transportManager) {
-        mContext = context;
-        mPackageManager = context.getPackageManager();
-        mPackageManagerBinder = AppGlobals.getPackageManager();
-        mActivityManager = ActivityManager.getService();
-
-        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
-
-        mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
-
-        mAgentTimeoutParameters = new
-                BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
-        mAgentTimeoutParameters.start();
-
-        // spin up the backup/restore handler thread
-        mBackupHandler = new BackupHandler(this, backupThread.getLooper());
-
-        // Set up our bookkeeping
-        final ContentResolver resolver = context.getContentResolver();
-        mProvisioned = Settings.Global.getInt(resolver,
-                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
-        mAutoRestore = Settings.Secure.getInt(resolver,
-                Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
-
-        mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
-        resolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
-                false, mProvisionedObserver);
-
-        mBaseStateDir = baseStateDir;
-        mBaseStateDir.mkdirs();
-        if (!SELinux.restorecon(mBaseStateDir)) {
-            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
-        }
-
-        mDataDir = dataDir;
-
-        mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
-
-        // Alarm receivers for scheduled backups & initialization operations
-        mRunBackupReceiver = new RunBackupReceiver(this);
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(RUN_BACKUP_ACTION);
-        context.registerReceiver(mRunBackupReceiver, filter,
-                android.Manifest.permission.BACKUP, null);
-
-        mRunInitReceiver = new RunInitializeReceiver(this);
-        filter = new IntentFilter();
-        filter.addAction(RUN_INITIALIZE_ACTION);
-        context.registerReceiver(mRunInitReceiver, filter,
-                android.Manifest.permission.BACKUP, null);
-
-        Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
-        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
-
-        Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
-        initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
-
-        // Set up the backup-request journaling
-        mJournalDir = new File(mBaseStateDir, "pending");
-        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
-        mJournal = null;        // will be created on first use
-
-        mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
-        // We are observing changes to the constants throughout the lifecycle of BMS. This is
-        // because we reference the constants in multiple areas of BMS, which otherwise would
-        // require frequent starting and stopping.
-        mConstants.start();
-
-        // Set up the various sorts of package tracking we do
-        mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
-        initPackageTracking();
-
-        // Build our mapping of uid to backup client services.  This implicitly
-        // schedules a backup pass on the Package Manager metadata the first
-        // time anything needs to be backed up.
-        synchronized (mBackupParticipants) {
-            addPackageParticipantsLocked(null);
-        }
-
-        mTransportManager = transportManager;
-        mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
-        mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
-        mBackupHandler.postDelayed(
-                mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
-
-        // Now that we know about valid backup participants, parse any leftover journal files into
-        // the pending backup set
-        mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
-
-        // Power management
-        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
-    }
-
     private void initPackageTracking() {
         if (MORE_DEBUG) Slog.v(TAG, "` tracking");
 
@@ -2750,54 +2706,6 @@
         }
     }
 
-    private static boolean backupSettingMigrated(int userId) {
-        File base = new File(Environment.getDataDirectory(), "backup");
-        File enableFile = new File(base, BACKUP_ENABLE_FILE);
-        return enableFile.exists();
-    }
-
-    private static boolean readBackupEnableState(int userId) {
-        File base = new File(Environment.getDataDirectory(), "backup");
-        File enableFile = new File(base, BACKUP_ENABLE_FILE);
-        if (enableFile.exists()) {
-            try (FileInputStream fin = new FileInputStream(enableFile)) {
-                int state = fin.read();
-                return state != 0;
-            } catch (IOException e) {
-                // can't read the file; fall through to assume disabled
-                Slog.e(TAG, "Cannot read enable state; assuming disabled");
-            }
-        } else {
-            if (DEBUG) {
-                Slog.i(TAG, "isBackupEnabled() => false due to absent settings file");
-            }
-        }
-        return false;
-    }
-
-    private static void writeBackupEnableState(boolean enable, int userId) {
-        File base = new File(Environment.getDataDirectory(), "backup");
-        File enableFile = new File(base, BACKUP_ENABLE_FILE);
-        File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
-        try (FileOutputStream fout = new FileOutputStream(stage)) {
-            fout.write(enable ? 1 : 0);
-            fout.close();
-            stage.renameTo(enableFile);
-            // will be synced immediately by the try-with-resources call to close()
-        } catch (IOException | RuntimeException e) {
-            // Whoops; looks like we're doomed.  Roll everything out, disabled,
-            // including the legacy state.
-            Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: "
-                    + e.getMessage());
-
-            ContentResolver resolver = sInstance.getContext().getContentResolver();
-            Settings.Secure.putStringForUser(resolver,
-                    Settings.Secure.BACKUP_ENABLED, null, userId);
-            enableFile.delete();
-            stage.delete();
-        }
-    }
-
     // Enable/disable backups
     public void setBackupEnabled(boolean enable) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -3565,17 +3473,6 @@
                 pw.println("    " + s);
             }
 
-            if (DEBUG_BACKUP_TRACE) {
-                synchronized (mBackupTrace) {
-                    if (!mBackupTrace.isEmpty()) {
-                        pw.println("Most recent backup trace:");
-                        for (String s : mBackupTrace) {
-                            pw.println("   " + s);
-                        }
-                    }
-                }
-            }
-
             pw.print("Ancestral: ");
             pw.println(Long.toHexString(mAncestralToken));
             pw.print("Current:   ");
@@ -3627,4 +3524,71 @@
         return mBackupManagerBinder;
     }
 
+    private static boolean backupSettingMigrated(int userId) {
+        File base = new File(Environment.getDataDirectory(), "backup");
+        File enableFile = new File(base, BACKUP_ENABLE_FILE);
+        return enableFile.exists();
+    }
+
+    private static boolean readBackupEnableState(int userId) {
+        File base = new File(Environment.getDataDirectory(), "backup");
+        File enableFile = new File(base, BACKUP_ENABLE_FILE);
+        if (enableFile.exists()) {
+            try (FileInputStream fin = new FileInputStream(enableFile)) {
+                int state = fin.read();
+                return state != 0;
+            } catch (IOException e) {
+                // can't read the file; fall through to assume disabled
+                Slog.e(TAG, "Cannot read enable state; assuming disabled");
+            }
+        } else {
+            if (DEBUG) {
+                Slog.i(TAG, "isBackupEnabled() => false due to absent settings file");
+            }
+        }
+        return false;
+    }
+
+    private static void writeBackupEnableState(boolean enable, int userId) {
+        File base = new File(Environment.getDataDirectory(), "backup");
+        File enableFile = new File(base, BACKUP_ENABLE_FILE);
+        File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
+        try (FileOutputStream fout = new FileOutputStream(stage)) {
+            fout.write(enable ? 1 : 0);
+            fout.close();
+            stage.renameTo(enableFile);
+            // will be synced immediately by the try-with-resources call to close()
+        } catch (IOException | RuntimeException e) {
+            // Whoops; looks like we're doomed.  Roll everything out, disabled,
+            // including the legacy state.
+            Slog.e(TAG, "Unable to record backup enable state; reverting to disabled: "
+                    + e.getMessage());
+
+            ContentResolver resolver = sInstance.getContext().getContentResolver();
+            Settings.Secure.putStringForUser(resolver,
+                    Settings.Secure.BACKUP_ENABLED, null, userId);
+            enableFile.delete();
+            stage.delete();
+        }
+    }
+
+    /** Implementation to receive lifecycle event callbacks for system services. */
+    public static final class Lifecycle extends SystemService {
+        public Lifecycle(Context context) {
+            super(context);
+            sInstance = new Trampoline(context);
+        }
+
+        @Override
+        public void onStart() {
+            publishBinderService(Context.BACKUP_SERVICE, sInstance);
+        }
+
+        @Override
+        public void onUnlockUser(int userId) {
+            if (userId == UserHandle.USER_SYSTEM) {
+                sInstance.unlockSystemUser();
+            }
+        }
+    }
 }
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index e108026..755095e 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -45,9 +45,9 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.FullBackupJob;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.internal.Operation;
@@ -599,7 +599,6 @@
                 cleanUpPipes(enginePipes);
                 if (currentPackage.applicationInfo != null) {
                     Slog.i(TAG, "Unbinding agent in " + packageName);
-                    backupManagerService.addBackupTrace("unbinding " + packageName);
                     try {
                         backupManagerService.getActivityManager().unbindBackupAgent(
                                 currentPackage.applicationInfo);
@@ -709,7 +708,6 @@
             try {
                 backupManagerService.prepareOperationTimeout(
                         mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
-                backupManagerService.addBackupTrace("preflighting");
                 if (MORE_DEBUG) {
                     Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
                 }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b5217ad..c660cc6 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -677,6 +677,8 @@
             stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
         }
         r.callStart = false;
+        StatsLog.write(StatsLog.SERVICE_STATE_CHANGED, r.appInfo.uid, r.name.getPackageName(),
+                r.name.getClassName(), StatsLog.SERVICE_STATE_CHANGED__STATE__START);
         synchronized (r.stats.getBatteryStats()) {
             r.stats.startRunningLocked();
         }
@@ -715,6 +717,9 @@
             service.delayedStop = true;
             return;
         }
+        StatsLog.write(StatsLog.SERVICE_STATE_CHANGED, service.appInfo.uid,
+                service.name.getPackageName(), service.name.getClassName(),
+                StatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
         synchronized (service.stats.getBatteryStats()) {
             service.stats.stopRunningLocked();
         }
@@ -856,6 +861,8 @@
                 }
             }
 
+            StatsLog.write(StatsLog.SERVICE_STATE_CHANGED, r.appInfo.uid, r.name.getPackageName(),
+                    r.name.getClassName(), StatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
             synchronized (r.stats.getBatteryStats()) {
                 r.stats.stopRunningLocked();
             }
@@ -2517,6 +2524,8 @@
                 EventLogTags.writeAmCreateService(
                         r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
             }
+            StatsLog.write(StatsLog.SERVICE_LAUNCH_REPORTED, r.appInfo.uid, r.name.getPackageName(),
+                    r.name.getClassName());
             synchronized (r.stats.getBatteryStats()) {
                 r.stats.startLaunchedLocked();
             }
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index 002d4e1..6612d02 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -40,6 +40,8 @@
  * notification callbacks. This class implements the IContextHubClient object, and the implemented
  * APIs must be thread-safe.
  *
+ * TODO: Consider refactoring this class via inheritance
+ *
  * @hide
  */
 public class ContextHubClientBroker extends IContextHubClient.Stub
@@ -92,7 +94,7 @@
     /*
      * The PendingIntent registered with this client.
      */
-    private final PendingIntentRequest mPendingIntentRequest = new PendingIntentRequest();
+    private final PendingIntentRequest mPendingIntentRequest;
 
     /*
      * Helper class to manage registered PendingIntent requests from the client.
@@ -130,41 +132,31 @@
         public void clear() {
             mPendingIntent = null;
         }
-
-        public boolean register(PendingIntent pendingIntent, long nanoAppId) {
-            boolean success = false;
-            if (hasPendingIntent()) {
-                Log.e(TAG, "Failed to register PendingIntent: registered PendingIntent exists");
-            } else {
-                mNanoAppId = nanoAppId;
-                mPendingIntent = pendingIntent;
-                success = true;
-            }
-
-            return success;
-        }
-
-        public boolean unregister(PendingIntent pendingIntent) {
-            boolean success = false;
-            if (!hasPendingIntent() || !mPendingIntent.equals(pendingIntent)) {
-                Log.e(TAG, "Failed to unregister PendingIntent: PendingIntent is not registered");
-            } else {
-                mPendingIntent = null;
-                success = true;
-            }
-
-            return success;
-        }
     }
 
     /* package */ ContextHubClientBroker(
             Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
-            ContextHubInfo contextHubInfo, short hostEndPointId) {
+            ContextHubInfo contextHubInfo, short hostEndPointId,
+            IContextHubClientCallback callback) {
         mContext = context;
         mContextHubProxy = contextHubProxy;
         mClientManager = clientManager;
         mAttachedContextHubInfo = contextHubInfo;
         mHostEndPointId = hostEndPointId;
+        mCallbackInterface = callback;
+        mPendingIntentRequest = new PendingIntentRequest();
+    }
+
+    /* package */ ContextHubClientBroker(
+            Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
+            ContextHubInfo contextHubInfo, short hostEndPointId, PendingIntent pendingIntent,
+            long nanoAppId) {
+        mContext = context;
+        mContextHubProxy = contextHubProxy;
+        mClientManager = clientManager;
+        mAttachedContextHubInfo = contextHubInfo;
+        mHostEndPointId = hostEndPointId;
+        mPendingIntentRequest = new PendingIntentRequest(pendingIntent, nanoAppId);
     }
 
     /**
@@ -179,11 +171,7 @@
         ContextHubServiceUtil.checkPermissions(mContext);
 
         int result;
-        IContextHubClientCallback callback = null;
-        synchronized (this) {
-            callback = mCallbackInterface;
-        }
-        if (callback != null) {
+        if (isRegistered()) {
             ContextHubMsg messageToNanoApp =
                     ContextHubServiceUtil.createHidlContextHubMessage(mHostEndPointId, message);
 
@@ -204,64 +192,16 @@
     }
 
     /**
-     * @param pendingIntent the intent to register
-     * @param nanoAppId     the ID of the nanoapp to send events for
-     * @return true on success, false otherwise
-     */
-    @Override
-    public boolean registerIntent(PendingIntent pendingIntent, long nanoAppId) {
-        ContextHubServiceUtil.checkPermissions(mContext);
-        if (mClientManager.isPendingIntentRegistered(pendingIntent)) {
-            Log.e(TAG, "Failed to register PendingIntent: already registered");
-            return false;
-        }
-
-        boolean success = false;
-        synchronized (this) {
-            if (mCallbackInterface == null) {
-                Log.e(TAG, "Failed to register PendingIntent: client connection is closed");
-            } else {
-                success = mPendingIntentRequest.register(pendingIntent, nanoAppId);
-            }
-        }
-
-        return success;
-    }
-
-    /**
-     * @param pendingIntent the intent to unregister
-     * @return true on success, false otherwise
-     */
-    @Override
-    public boolean unregisterIntent(PendingIntent pendingIntent) {
-        ContextHubServiceUtil.checkPermissions(mContext);
-
-        boolean success = false;
-        synchronized (this) {
-            success = mPendingIntentRequest.unregister(pendingIntent);
-            if (mCallbackInterface == null) {
-                close();
-            }
-        }
-
-        return success;
-    }
-
-    /**
      * Closes the connection for this client with the service.
+     *
+     * If the client has a PendingIntent registered, this method also unregisters it.
      */
     @Override
     public void close() {
         synchronized (this) {
-            if (mCallbackInterface != null) {
-                mCallbackInterface.asBinder().unlinkToDeath(this, 0 /* flags */);
-                mCallbackInterface = null;
-            }
-            if (!mPendingIntentRequest.hasPendingIntent() && mRegistered) {
-                mClientManager.unregisterClient(mHostEndPointId);
-                mRegistered = false;
-            }
+            mPendingIntentRequest.clear();
         }
+        onClientExit();
     }
 
     /**
@@ -269,38 +209,7 @@
      */
     @Override
     public void binderDied() {
-        close();
-    }
-
-    /**
-     * Sets the callback interface for this client, only if the callback is currently unregistered.
-     *
-     * Also attaches a death recipient to a ContextHubClientBroker object. If unsuccessful, the
-     * connection is closed.
-     *
-     * @param callback the callback interface
-     * @return true if the callback was successfully set, false otherwise
-     *
-     * @throws IllegalStateException if the client has already been registered to a callback
-     */
-    /* package */
-    synchronized boolean setCallback(IContextHubClientCallback callback) {
-        boolean success = false;
-        if (mCallbackInterface != null) {
-            throw new IllegalStateException("Client is already registered with a callback");
-        } else {
-            mCallbackInterface = callback;
-            try {
-                mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */);
-                success = true;
-            } catch (RemoteException e) {
-                // The client process has died, so we close the connection.
-                Log.e(TAG, "Failed to attach death recipient to client");
-                close();
-            }
-        }
-
-        return success;
+        onClientExit();
     }
 
     /**
@@ -375,15 +284,30 @@
     }
 
     /**
-     * @param intent the PendingIntent to compare to
+     * @param intent    the PendingIntent to compare to
+     * @param nanoAppId the ID of the nanoapp of the PendingIntent to compare to
      * @return true if the given PendingIntent is currently registered, false otherwise
      */
-    /* package */ boolean hasPendingIntent(PendingIntent intent) {
+    /* package */ boolean hasPendingIntent(PendingIntent intent, long nanoAppId) {
         PendingIntent pendingIntent = null;
+        long intentNanoAppId;
         synchronized (this) {
             pendingIntent = mPendingIntentRequest.getPendingIntent();
+            intentNanoAppId = mPendingIntentRequest.getNanoAppId();
         }
-        return (pendingIntent != null) && pendingIntent.equals(intent);
+        return (pendingIntent != null) && pendingIntent.equals(intent)
+                && intentNanoAppId == nanoAppId;
+    }
+
+    /**
+     * Attaches the death recipient to the callback interface object, if any.
+     *
+     * @throws RemoteException if the client process already died
+     */
+    /* package */ void attachDeathRecipient() throws RemoteException {
+        if (mCallbackInterface != null) {
+            mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */);
+        }
     }
 
     /**
@@ -446,11 +370,29 @@
                 // The PendingIntent is no longer valid
                 Log.w(TAG, "PendingIntent has been canceled, unregistering from client"
                         + " (host endpoint ID " + mHostEndPointId + ")");
-                mPendingIntentRequest.clear();
-                if (mCallbackInterface == null) {
-                    close();
-                }
+                close();
             }
         }
     }
+
+    /**
+     * @return true if the client is still registered with the service, false otherwise
+     */
+    private synchronized boolean isRegistered() {
+        return mRegistered;
+    }
+
+    /**
+     * Invoked when a client exits either explicitly or by binder death.
+     */
+    private synchronized void onClientExit() {
+        if (mCallbackInterface != null) {
+            mCallbackInterface.asBinder().unlinkToDeath(this, 0 /* flags */);
+            mCallbackInterface = null;
+        }
+        if (!mPendingIntentRequest.hasPendingIntent() && mRegistered) {
+            mClientManager.unregisterClient(mHostEndPointId);
+            mRegistered = false;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index fe93a1a..7293440 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -24,6 +24,7 @@
 import android.hardware.location.IContextHubClient;
 import android.hardware.location.IContextHubClientCallback;
 import android.hardware.location.NanoAppMessage;
+import android.os.RemoteException;
 import android.util.Log;
 
 import java.util.concurrent.ConcurrentHashMap;
@@ -68,7 +69,7 @@
     /*
      * The next host endpoint ID to start iterating for the next available host endpoint ID.
      */
-    private int mNextHostEndpointId = 0;
+    private int mNextHostEndPointId = 0;
 
     /* package */ ContextHubClientManager(
             Context context, IContexthub contextHubProxy) {
@@ -88,9 +89,22 @@
      */
     /* package */ IContextHubClient registerClient(
             IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
-        ContextHubClientBroker broker = createNewClientBroker(contextHubInfo);
-        if (!broker.setCallback(clientCallback)) {
-            return null; // Client process has died, so we return null
+        ContextHubClientBroker broker;
+        synchronized (this) {
+            short hostEndPointId = getHostEndPointId();
+            broker = new ContextHubClientBroker(
+                    mContext, mContextHubProxy, this /* clientManager */, contextHubInfo,
+                    hostEndPointId, clientCallback);
+            mHostEndPointIdToClientMap.put(hostEndPointId, broker);
+        }
+
+        try {
+            broker.attachDeathRecipient();
+        } catch (RemoteException e) {
+            // The client process has died, so we close the connection and return null
+            Log.e(TAG, "Failed to attach death recipient to client");
+            broker.close();
+            return null;
         }
 
         Log.d(TAG, "Registered client with host endpoint ID " + broker.getHostEndPointId());
@@ -98,32 +112,36 @@
     }
 
     /**
-     * Binds a existing and registered client with a new callback interface, provided a previously
-     * registered PendingIntent.
+     * Registers a new client with the service.
      *
-     * @param pendingIntent  a previously registered PendingIntent for a registered client
-     * @param clientCallback the callback interface of the client to bind to
-     * @param contextHubId   the ID of the hub this client is attached to
+     * @param pendingIntent  the callback interface of the client to register
+     * @param contextHubInfo the object describing the hub this client is attached to
+     * @param nanoAppId      the ID of the nanoapp to receive Intent events for
      *
      * @return the client interface
      *
-     * @throws IllegalArgumentException if no matching client is found
-     * @throws IllegalStateException    if the client has already been registered to a callback
+     * @throws IllegalArgumentException the PendingIntent was already registered for a different
+     *                                  ContextHubClient
+     * @throws IllegalStateException    if there were too many registered clients at the service
      */
-    /* package */ IContextHubClient bindClient(
-            PendingIntent pendingIntent, IContextHubClientCallback clientCallback,
-            int contextHubId) {
-        ContextHubClientBroker broker = getClientBroker(pendingIntent, contextHubId);
-        if (broker == null) {
-            throw new IllegalArgumentException("Could not find client of Context Hub (ID = "
-                    + contextHubId + ") with PendingIntent");
+    /* package */ IContextHubClient registerClient(
+            ContextHubInfo contextHubInfo, PendingIntent pendingIntent, long nanoAppId) {
+        ContextHubClientBroker broker;
+        String registerString = "Regenerated";
+        synchronized (this) {
+            broker = getClientBroker(contextHubInfo.getId(), pendingIntent, nanoAppId);
+
+            if (broker == null) {
+                short hostEndPointId = getHostEndPointId();
+                broker = new ContextHubClientBroker(
+                        mContext, mContextHubProxy, this /* clientManager */, contextHubInfo,
+                        hostEndPointId, pendingIntent, nanoAppId);
+                mHostEndPointIdToClientMap.put(hostEndPointId, broker);
+                registerString = "Registered";
+            }
         }
 
-        if (!broker.setCallback(clientCallback)) {
-            return null; // Client process has died, so we return null
-        }
-
-        Log.d(TAG, "Re-registered client with host endpoint ID " + broker.getHostEndPointId());
+        Log.d(TAG, registerString + " client with host endpoint ID " + broker.getHostEndPointId());
         return IContextHubClient.Stub.asInterface(broker);
     }
 
@@ -203,50 +221,28 @@
     }
 
     /**
-     * @param pendingIntent the PendingIntent to check
-     * @return true if the given PendingIntent is registered by a client, false otherwise
-     */
-    /* package */ boolean isPendingIntentRegistered(PendingIntent pendingIntent) {
-        for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
-            if (broker.hasPendingIntent(pendingIntent)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Creates a new ContextHubClientBroker object for a client and registers it with the client
-     * manager.
+     * Returns an available host endpoint ID.
      *
-     * @param contextHubInfo the object describing the hub this client is attached to
-     *
-     * @return the ContextHubClientBroker object
+     * @returns an available host endpoint ID
      *
      * @throws IllegalStateException if max number of clients have already registered
      */
-    private synchronized ContextHubClientBroker createNewClientBroker(
-            ContextHubInfo contextHubInfo) {
+    private short getHostEndPointId() {
         if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) {
             throw new IllegalStateException("Could not register client - max limit exceeded");
         }
 
-        ContextHubClientBroker broker = null;
-        int id = mNextHostEndpointId;
+        int id = mNextHostEndPointId;
         for (int i = 0; i <= MAX_CLIENT_ID; i++) {
             if (!mHostEndPointIdToClientMap.containsKey((short) id)) {
-                broker = new ContextHubClientBroker(
-                        mContext, mContextHubProxy, this, contextHubInfo, (short) id);
-                mHostEndPointIdToClientMap.put((short) id, broker);
-                mNextHostEndpointId = (id == MAX_CLIENT_ID) ? 0 : id + 1;
+                mNextHostEndPointId = (id == MAX_CLIENT_ID) ? 0 : id + 1;
                 break;
             }
 
             id = (id == MAX_CLIENT_ID) ? 0 : id + 1;
         }
 
-        return broker;
+        return (short) id;
     }
 
     /**
@@ -280,9 +276,10 @@
      * @param contextHubId  the ID of the Context Hub the client is attached to
      * @return the matching ContextHubClientBroker, null if not found
      */
-    private ContextHubClientBroker getClientBroker(PendingIntent pendingIntent, int contextHubId) {
+    private ContextHubClientBroker getClientBroker(
+            int contextHubId, PendingIntent pendingIntent, long nanoAppId) {
         for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
-            if (broker.hasPendingIntent(pendingIntent)
+            if (broker.hasPendingIntent(pendingIntent, nanoAppId)
                     && broker.getAttachedContextHubId() == contextHubId) {
                 return broker;
             }
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 215e67c..52f1c6b 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -632,34 +632,26 @@
     }
 
     /**
-     * Recreates and binds a IContextHubClientCallback interface to an existing and registered
-     * client at the service for the specified Context Hub, provided a previously registered
-     * PendingIntent.
+     * Creates and registers a PendingIntent client at the service for the specified Context Hub.
      *
-     * @param pendingIntent  the PendingIntent previously registered for the client
-     * @param clientCallback the client interface to register with the service
-     * @param contextHubId   the ID of the hub this client is attached to
-     * @return the generated client interface, null if registration was unsuccessful
+     * @param contextHubId  the ID of the hub this client is attached to
+     * @param pendingIntent the PendingIntent associated with this client
+     * @param nanoAppId     the ID of the nanoapp PendingIntent events will be sent for
+     * @return the generated client interface
      *
-     * @throws IllegalArgumentException if contextHubId is not a valid ID
-     * @throws NullPointerException if clientCallback or pendingIntent is null
+     * @throws IllegalArgumentException if hubInfo does not represent a valid hub
+     * @throws IllegalStateException    if there were too many registered clients at the service
      */
     @Override
-    public IContextHubClient bindClient(
-            PendingIntent pendingIntent, IContextHubClientCallback clientCallback,
-            int contextHubId) throws RemoteException {
+    public IContextHubClient createPendingIntentClient(
+            int contextHubId, PendingIntent pendingIntent, long nanoAppId) throws RemoteException {
         checkPermissions();
         if (!isValidContextHubId(contextHubId)) {
             throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
         }
-        if (pendingIntent == null) {
-            throw new NullPointerException("Cannot create client with null pending intent");
-        }
-        if (clientCallback == null) {
-            throw new NullPointerException("Cannot create client with null callback");
-        }
 
-        return mClientManager.bindClient(pendingIntent, clientCallback, contextHubId);
+        ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
+        return mClientManager.registerClient(contextHubInfo, pendingIntent, nanoAppId);
     }
 
     /**
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cfbe83d..6195ed9e 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -870,6 +870,7 @@
                     }
                     if (expanded && userAction) {
                         r.recordExpanded();
+                        reportUserInteraction(r);
                     }
                     EventLogTags.writeNotificationExpansion(key,
                             userAction ? 1 : 0, expanded ? 1 : 0,
@@ -1987,7 +1988,7 @@
     }
 
     /**
-     * Report to usage stats that the notification was clicked.
+     * Report to usage stats that the user interacted with the notification.
      * @param r notification record
      */
     protected void reportUserInteraction(NotificationRecord r) {
@@ -4539,6 +4540,7 @@
                             mDuration)
                     .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
                             mSnoozeCriterionId == null ? 0 : 1));
+            reportUserInteraction(r);
             boolean wasPosted = removeFromNotificationListsLocked(r);
             cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
             updateLightsLocked();
@@ -5547,7 +5549,7 @@
             ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
             ArrayList<Integer> userSentimentBefore = new ArrayList<>(N);
             ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N);
-            ArrayList<ArrayList<Notification.Action>> smartActionsBefore = new ArrayList<>(N);
+            ArrayList<ArrayList<Notification.Action>> systemSmartActionsBefore = new ArrayList<>(N);
             ArrayList<ArrayList<CharSequence>> smartRepliesBefore = new ArrayList<>(N);
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
@@ -5560,7 +5562,7 @@
                 snoozeCriteriaBefore.add(r.getSnoozeCriteria());
                 userSentimentBefore.add(r.getUserSentiment());
                 suppressVisuallyBefore.add(r.getSuppressedVisualEffects());
-                smartActionsBefore.add(r.getSmartActions());
+                systemSmartActionsBefore.add(r.getSystemGeneratedSmartActions());
                 smartRepliesBefore.add(r.getSmartReplies());
                 mRankingHelper.extractSignals(r);
             }
@@ -5577,7 +5579,8 @@
                         || !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())
                         || !Objects.equals(suppressVisuallyBefore.get(i),
                         r.getSuppressedVisualEffects())
-                        || !Objects.equals(smartActionsBefore.get(i), r.getSmartActions())
+                        || !Objects.equals(systemSmartActionsBefore.get(i),
+                                r.getSystemGeneratedSmartActions())
                         || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())) {
                     mHandler.scheduleSendRankingUpdate();
                     return;
@@ -6579,7 +6582,7 @@
         Bundle showBadge = new Bundle();
         Bundle userSentiment = new Bundle();
         Bundle hidden = new Bundle();
-        Bundle smartActions = new Bundle();
+        Bundle systemGeneratedSmartActions = new Bundle();
         Bundle smartReplies = new Bundle();
         Bundle audiblyAlerted = new Bundle();
         Bundle noisy = new Bundle();
@@ -6610,7 +6613,8 @@
             showBadge.putBoolean(key, record.canShowBadge());
             userSentiment.putInt(key, record.getUserSentiment());
             hidden.putBoolean(key, record.isHidden());
-            smartActions.putParcelableArrayList(key, record.getSmartActions());
+            systemGeneratedSmartActions.putParcelableArrayList(key,
+                    record.getSystemGeneratedSmartActions());
             smartReplies.putCharSequenceArrayList(key, record.getSmartReplies());
             audiblyAlerted.putBoolean(key, record.getAudiblyAlerted());
             noisy.putBoolean(key, record.getSound() != null || record.getVibration() != null);
@@ -6625,7 +6629,7 @@
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
                 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
                 channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden,
-                smartActions, smartReplies, audiblyAlerted, noisy);
+                systemGeneratedSmartActions, smartReplies, audiblyAlerted, noisy);
     }
 
     boolean hasCompanionDevice(ManagedServiceInfo info) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index a11b03f..1a9257c 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -163,7 +163,11 @@
     private Light mLight;
     private String mGroupLogTag;
     private String mChannelIdLogTag;
-    private ArrayList<Notification.Action> mSmartActions;
+    /**
+     * This list contains system generated smart actions from NAS, app-generated smart actions are
+     * stored in Notification.actions marked as SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION.
+     */
+    private ArrayList<Notification.Action> mSystemGeneratedSmartActions;
     private ArrayList<CharSequence> mSmartReplies;
 
     private final List<Adjustment> mAdjustments;
@@ -653,10 +657,11 @@
                     }
                 }
                 if (signals.containsKey(Adjustment.KEY_SMART_ACTIONS)) {
-                    setSmartActions(signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS));
+                    setSystemGeneratedSmartActions(
+                            signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS));
                     MetricsLogger.action(getAdjustmentLogMaker()
                             .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_ACTIONS,
-                                    getSmartActions().size()));
+                                    getSystemGeneratedSmartActions().size()));
                 }
                 if (signals.containsKey(Adjustment.KEY_SMART_REPLIES)) {
                     setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES));
@@ -1132,12 +1137,13 @@
         mHasSeenSmartReplies = hasSeenSmartReplies;
     }
 
-    public void setSmartActions(ArrayList<Notification.Action> smartActions) {
-        mSmartActions = smartActions;
+    public void setSystemGeneratedSmartActions(
+            ArrayList<Notification.Action> systemGeneratedSmartActions) {
+        mSystemGeneratedSmartActions = systemGeneratedSmartActions;
     }
 
-    public ArrayList<Notification.Action> getSmartActions() {
-        return mSmartActions;
+    public ArrayList<Notification.Action> getSystemGeneratedSmartActions() {
+        return mSystemGeneratedSmartActions;
     }
 
     public void setSmartReplies(ArrayList<CharSequence> smartReplies) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8a0c416..e4e8010 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -12937,6 +12937,25 @@
         }
     }
 
+    @Override
+    public boolean canSuspendPackageForUser(String packageName, int userId) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
+                "canSuspendPackageForUser");
+        final int callingUid = Binder.getCallingUid();
+        if (UserHandle.getUserId(callingUid) != userId) {
+            throw new SecurityException("Calling uid " + callingUid
+                    + " cannot query canSuspendPackageForUser for user " + userId);
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mPackages) {
+                return canSuspendPackageForUserLocked(packageName, userId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     @GuardedBy("mPackages")
     private boolean canSuspendPackageForUserLocked(String packageName, int userId) {
         if (isPackageDeviceAdmin(packageName, userId)) {
@@ -13000,7 +13019,7 @@
         }
 
         if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
-            Slog.w(TAG, "Cannot suspend package: " + packageName);
+            Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 073601d..912cb7f 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -809,7 +809,7 @@
             updateBoundsForDisplayChanges();
         }
 
-        if (mAnimationBackgroundSurface != null) {
+        if (mAnimationBackgroundSurface == null) {
             mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer(true)
                     .setName("animation background stackId=" + mStackId)
                     .build();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
index f17a30d..410ab87 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
@@ -72,7 +72,7 @@
         assertTrue(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
         assertEquals(people, r.getPeopleOverride());
         assertEquals(snoozeCriteria, r.getSnoozeCriteria());
-        assertEquals(smartActions, r.getSmartActions());
+        assertEquals(smartActions, r.getSystemGeneratedSmartActions());
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 9b41fdd..8690110 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -708,14 +708,14 @@
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
                 false /* lights */, false /* defaultLights */, groupId /* group */);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
-        assertNull(record.getSmartActions());
+        assertNull(record.getSystemGeneratedSmartActions());
 
         ArrayList<Notification.Action> smartActions = new ArrayList<>();
         smartActions.add(new Notification.Action.Builder(
                 Icon.createWithResource(getContext(), R.drawable.btn_default),
                 "text", null).build());
-        record.setSmartActions(smartActions);
-        assertEquals(smartActions, record.getSmartActions());
+        record.setSystemGeneratedSmartActions(smartActions);
+        assertEquals(smartActions, record.getSystemGeneratedSmartActions());
     }
 
     @Test
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 32875da..94d7dbb 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -915,8 +915,12 @@
                 return "SCREEN_INTERACTIVE";
             case UsageEvents.Event.SCREEN_NON_INTERACTIVE:
                 return "SCREEN_NON_INTERACTIVE";
+            case UsageEvents.Event.KEYGUARD_SHOWN:
+                return "KEYGUARD_SHOWN";
+            case UsageEvents.Event.KEYGUARD_HIDDEN:
+                return "KEYGUARD_HIDDEN";
             default:
-                return "UNKNOWN";
+                return "UNKNOWN_TYPE_" + eventType;
         }
     }
 
diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java
index 6090d5c..fd21d42 100644
--- a/telephony/java/android/telephony/CellSignalStrength.java
+++ b/telephony/java/android/telephony/CellSignalStrength.java
@@ -21,15 +21,20 @@
  */
 public abstract class CellSignalStrength {
 
-    public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
+    public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN =
+            TelephonyProtoEnums.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; // 0
 
-    public static final int SIGNAL_STRENGTH_POOR = 1;
+    public static final int SIGNAL_STRENGTH_POOR =
+            TelephonyProtoEnums.SIGNAL_STRENGTH_POOR; // 1
 
-    public static final int SIGNAL_STRENGTH_MODERATE = 2;
+    public static final int SIGNAL_STRENGTH_MODERATE =
+            TelephonyProtoEnums.SIGNAL_STRENGTH_MODERATE; // 2
 
-    public static final int SIGNAL_STRENGTH_GOOD = 3;
+    public static final int SIGNAL_STRENGTH_GOOD =
+            TelephonyProtoEnums.SIGNAL_STRENGTH_GOOD; // 3
 
-    public static final int SIGNAL_STRENGTH_GREAT = 4;
+    public static final int SIGNAL_STRENGTH_GREAT =
+            TelephonyProtoEnums.SIGNAL_STRENGTH_GREAT; // 4
 
     /** @hide */
     public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
diff --git a/telephony/java/android/telephony/ICellInfoCallback.aidl b/telephony/java/android/telephony/ICellInfoCallback.aidl
new file mode 100644
index 0000000..7fb62682
--- /dev/null
+++ b/telephony/java/android/telephony/ICellInfoCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.telephony.CellInfo;
+
+import java.util.List;
+
+/**
+ * Callback to provide asynchronous CellInfo.
+ * @hide
+ */
+oneway interface ICellInfoCallback
+{
+    void onCellInfo(in List<CellInfo> state);
+}
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index 75e8eda..aee744f 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -70,6 +70,43 @@
     /** Registered on roaming network */
     public static final int REG_STATE_ROAMING               = 5;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "NR_STATUS_",
+            value = {NR_STATUS_NONE, NR_STATUS_RESTRICTED, NR_STATUS_NOT_RESTRICTED,
+                    NR_STATUS_CONNECTED})
+    public @interface NRStatus {}
+
+    /**
+     * The device isn't camped on an LTE cell or the LTE cell doesn't support E-UTRA-NR
+     * Dual Connectivity(EN-DC).
+     * @hide
+     */
+    public static final int NR_STATUS_NONE = -1;
+
+    /**
+     * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) but
+     * either the use of dual connectivity with NR(DCNR) is restricted or NR is not supported by
+     * the selected PLMN.
+     * @hide
+     */
+    public static final int NR_STATUS_RESTRICTED = 1;
+
+    /**
+     * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) and both
+     * the use of dual connectivity with NR(DCNR) is not restricted and NR is supported by the
+     * selected PLMN.
+     * @hide
+     */
+    public static final int NR_STATUS_NOT_RESTRICTED = 2;
+
+    /**
+     * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) and
+     * also connected to at least one 5G cell as a secondary serving cell.
+     * @hide
+     */
+    public static final int NR_STATUS_CONNECTED = 3;
+
     /**
      * Supported service type
      * @hide
@@ -104,6 +141,9 @@
 
     private int mAccessNetworkTechnology;
 
+    @NRStatus
+    private int mNrStatus;
+
     private final int mRejectCause;
 
     private final boolean mEmergencyOnly;
@@ -154,6 +194,7 @@
         mAvailableServices = availableServices;
         mCellIdentity = cellIdentity;
         mEmergencyOnly = emergencyOnly;
+        mNrStatus = NR_STATUS_NONE;
     }
 
     /**
@@ -200,6 +241,7 @@
                 VoiceSpecificRegistrationStates.class.getClassLoader());
         mDataSpecificStates = source.readParcelable(
                 DataSpecificRegistrationStates.class.getClassLoader());
+        mNrStatus = source.readInt();
     }
 
     /**
@@ -213,6 +255,19 @@
     public @Domain int getDomain() { return mDomain; }
 
     /**
+     * @return the 5G NR connection status.
+     * @hide
+     */
+    public @NRStatus int getNrStatus() {
+        return mNrStatus;
+    }
+
+    /** @hide */
+    public void setNrStatus(@NRStatus int nrStatus) {
+        mNrStatus = nrStatus;
+    }
+
+    /**
      * @return The registration state.
      */
     public @RegState int getRegState() {
@@ -315,6 +370,19 @@
         return "Unknown reg state " + regState;
     }
 
+    private static String nrStatusToString(@NRStatus int nrStatus) {
+        switch (nrStatus) {
+            case NR_STATUS_RESTRICTED:
+                return "RESTRICTED";
+            case NR_STATUS_NOT_RESTRICTED:
+                return "NOT_RESTRICTED";
+            case NR_STATUS_CONNECTED:
+                return "CONNECTED";
+            default:
+                return "NONE";
+        }
+    }
+
     @Override
     public String toString() {
         return new StringBuilder("NetworkRegistrationState{")
@@ -330,6 +398,7 @@
                 .append(" cellIdentity=").append(mCellIdentity)
                 .append(" voiceSpecificStates=").append(mVoiceSpecificStates)
                 .append(" dataSpecificStates=").append(mDataSpecificStates)
+                .append(" nrStatus=").append(nrStatusToString(mNrStatus))
                 .append("}").toString();
     }
 
@@ -337,7 +406,7 @@
     public int hashCode() {
         return Objects.hash(mDomain, mTransportType, mRegState, mRoamingType,
                 mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices,
-                mCellIdentity, mVoiceSpecificStates, mDataSpecificStates);
+                mCellIdentity, mVoiceSpecificStates, mDataSpecificStates, mNrStatus);
     }
 
     @Override
@@ -359,7 +428,8 @@
                 && Arrays.equals(mAvailableServices, other.mAvailableServices)
                 && Objects.equals(mCellIdentity, other.mCellIdentity)
                 && Objects.equals(mVoiceSpecificStates, other.mVoiceSpecificStates)
-                && Objects.equals(mDataSpecificStates, other.mDataSpecificStates);
+                && Objects.equals(mDataSpecificStates, other.mDataSpecificStates)
+                && mNrStatus == other.mNrStatus;
     }
 
     @Override
@@ -375,6 +445,7 @@
         dest.writeParcelable(mCellIdentity, 0);
         dest.writeParcelable(mVoiceSpecificStates, 0);
         dest.writeParcelable(mDataSpecificStates, 0);
+        dest.writeInt(mNrStatus);
     }
 
     public static final Parcelable.Creator<NetworkRegistrationState> CREATOR =
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 0937b10..ab80e25 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -83,7 +83,45 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({DUPLEX_MODE_UNKNOWN, DUPLEX_MODE_FDD, DUPLEX_MODE_TDD})
+    @IntDef(prefix = "FREQUENCY_RANGE_",
+            value = {FREQUENCY_RANGE_UNKNOWN, FREQUENCY_RANGE_LOW, FREQUENCY_RANGE_MID,
+                    FREQUENCY_RANGE_HIGH, FREQUENCY_RANGE_MMWAVE})
+    public @interface FrequencyRange {}
+
+    /**
+     * Indicates frequency range is unknown.
+     * @hide
+     */
+    public static final int FREQUENCY_RANGE_UNKNOWN = -1;
+
+    /**
+     * Indicates the frequency range is below 1GHz.
+     * @hide
+     */
+    public static final int FREQUENCY_RANGE_LOW = 1;
+
+    /**
+     * Indicates the frequency range is between 1GHz to 3GHz.
+     * @hide
+     */
+    public static final int FREQUENCY_RANGE_MID = 2;
+
+    /**
+     * Indicates the frequency range is between 3GHz and 6GHz.
+     * @hide
+     */
+    public static final int FREQUENCY_RANGE_HIGH = 3;
+
+    /**
+     * Indicates the frequency range is above 6GHz (millimeter wave frequency).
+     * @hide
+     */
+    public static final int FREQUENCY_RANGE_MMWAVE = 4;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "DUPLEX_MODE_",
+            value = {DUPLEX_MODE_UNKNOWN, DUPLEX_MODE_FDD, DUPLEX_MODE_TDD})
     public @interface DuplexMode {}
 
     /**
@@ -283,6 +321,8 @@
     @UnsupportedAppUsage
     private boolean mIsUsingCarrierAggregation;
 
+    @FrequencyRange
+    private int mNrFrequencyRange;
     private int mChannelNumber;
     private int[] mCellBandwidths = new int[0];
 
@@ -375,6 +415,7 @@
         mLteEarfcnRsrpBoost = s.mLteEarfcnRsrpBoost;
         mNetworkRegistrationStates = s.mNetworkRegistrationStates == null ? null :
                 new ArrayList<>(s.mNetworkRegistrationStates);
+        mNrFrequencyRange = s.mNrFrequencyRange;
     }
 
     /**
@@ -406,6 +447,7 @@
         in.readList(mNetworkRegistrationStates, NetworkRegistrationState.class.getClassLoader());
         mChannelNumber = in.readInt();
         mCellBandwidths = in.createIntArray();
+        mNrFrequencyRange = in.readInt();
     }
 
     public void writeToParcel(Parcel out, int flags) {
@@ -433,6 +475,7 @@
         out.writeList(mNetworkRegistrationStates);
         out.writeInt(mChannelNumber);
         out.writeIntArray(mCellBandwidths);
+        out.writeInt(mNrFrequencyRange);
     }
 
     public int describeContents() {
@@ -792,7 +835,8 @@
                 mIsEmergencyOnly,
                 mIsUsingCarrierAggregation,
                 mLteEarfcnRsrpBoost,
-                mNetworkRegistrationStates);
+                mNetworkRegistrationStates,
+                mNrFrequencyRange);
     }
 
     @Override
@@ -823,7 +867,8 @@
                 && mIsUsingCarrierAggregation == s.mIsUsingCarrierAggregation)
                 && (mNetworkRegistrationStates == null ? s.mNetworkRegistrationStates == null :
                         s.mNetworkRegistrationStates != null &&
-                        mNetworkRegistrationStates.containsAll(s.mNetworkRegistrationStates));
+                        mNetworkRegistrationStates.containsAll(s.mNetworkRegistrationStates))
+                && mNrFrequencyRange == s.mNrFrequencyRange;
     }
 
     /**
@@ -958,6 +1003,7 @@
             .append(", mIsUsingCarrierAggregation=").append(mIsUsingCarrierAggregation)
             .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost)
             .append(", mNetworkRegistrationStates=").append(mNetworkRegistrationStates)
+            .append(", mNrFrequencyRange=").append(mNrFrequencyRange)
             .append("}").toString();
     }
 
@@ -987,6 +1033,7 @@
         mIsUsingCarrierAggregation = false;
         mLteEarfcnRsrpBoost = 0;
         mNetworkRegistrationStates = new ArrayList<>();
+        mNrFrequencyRange = FREQUENCY_RANGE_UNKNOWN;
     }
 
     public void setStateOutOfService() {
@@ -1225,6 +1272,7 @@
         m.putInt("LteEarfcnRsrpBoost", mLteEarfcnRsrpBoost);
         m.putInt("ChannelNumber", mChannelNumber);
         m.putIntArray("CellBandwidths", mCellBandwidths);
+        m.putInt("mNrFrequencyRange", mNrFrequencyRange);
     }
 
     /** @hide */
@@ -1288,6 +1336,22 @@
         mIsUsingCarrierAggregation = ca;
     }
 
+    /**
+     * @return the frequency range of 5G NR.
+     * @hide
+     */
+    public @FrequencyRange int getNrFrequencyRange() {
+        return mNrFrequencyRange;
+    }
+
+    /**
+     * @param nrFrequencyRange the frequency range of 5G NR.
+     * @hide
+     */
+    public void setNrFrequencyRange(@FrequencyRange int nrFrequencyRange) {
+        mNrFrequencyRange = nrFrequencyRange;
+    }
+
     /** @hide */
     public int getLteEarfcnRsrpBoost() {
         return mLteEarfcnRsrpBoost;
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index bc832c3..240b8a9 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -17,12 +17,12 @@
 package android.telephony;
 
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.CarrierConfigManager;
 import android.util.Log;
-import android.content.res.Resources;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -37,25 +37,25 @@
     private static final boolean DBG = false;
 
     /** @hide */
-    @UnsupportedAppUsage
-    public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN
-            = TelephonyProtoEnums.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; // = 0
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN =
+            CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; // = 0
     /** @hide */
-    @UnsupportedAppUsage
-    public static final int SIGNAL_STRENGTH_POOR
-            = TelephonyProtoEnums.SIGNAL_STRENGTH_POOR; // = 1
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static final int SIGNAL_STRENGTH_POOR =
+            CellSignalStrength.SIGNAL_STRENGTH_POOR; // = 1
     /** @hide */
-    @UnsupportedAppUsage
-    public static final int SIGNAL_STRENGTH_MODERATE
-            = TelephonyProtoEnums.SIGNAL_STRENGTH_MODERATE; // = 2
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static final int SIGNAL_STRENGTH_MODERATE =
+            CellSignalStrength.SIGNAL_STRENGTH_MODERATE; // = 2
     /** @hide */
-    @UnsupportedAppUsage
-    public static final int SIGNAL_STRENGTH_GOOD
-            = TelephonyProtoEnums.SIGNAL_STRENGTH_GOOD; // = 3
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static final int SIGNAL_STRENGTH_GOOD =
+            CellSignalStrength.SIGNAL_STRENGTH_GOOD; // = 3
     /** @hide */
-    @UnsupportedAppUsage
-    public static final int SIGNAL_STRENGTH_GREAT
-            = TelephonyProtoEnums.SIGNAL_STRENGTH_GREAT; // = 4
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    public static final int SIGNAL_STRENGTH_GREAT =
+            CellSignalStrength.SIGNAL_STRENGTH_GREAT; // = 4
     /** @hide */
     @UnsupportedAppUsage
     public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 22c1e58..b41e14e 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -143,9 +143,11 @@
     private boolean mIsOpportunistic;
 
     /**
-     * SubId of the parent subscription, if there is one.
+     * A UUID assigned to the subscription group. It returns
+     * null if not assigned.
      */
-    private int mParentSubId;
+    @Nullable
+    private String mGroupUUID;
 
     /**
      * @hide
@@ -156,7 +158,7 @@
             @Nullable UiccAccessRule[] accessRules, String cardId) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
                 roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
-                false, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+                false, null);
     }
 
     /**
@@ -166,7 +168,7 @@
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
             @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
-            int parentSubId) {
+            @Nullable String groupUUID) {
         this.mId = id;
         this.mIccId = iccId;
         this.mSimSlotIndex = simSlotIndex;
@@ -184,7 +186,7 @@
         this.mAccessRules = accessRules;
         this.mCardId = cardId;
         this.mIsOpportunistic = isOpportunistic;
-        this.mParentSubId = parentSubId;
+        this.mGroupUUID = groupUUID;
     }
 
     /**
@@ -388,16 +390,16 @@
     }
 
     /**
-     * Used in scenarios where a child subscription is bundled with a primary parent subscription.
-     * The child subscription will typically be opportunistic (see {@link #isOpportunistic()})
-     * and will be used to provide data services where available, with the parent being the primary
-     * fallback subscription.
+     * Used in scenarios where different subscriptions are bundled as a group.
+     * It's typically a primary and an opportunistic subscription. (see {@link #isOpportunistic()})
+     * Such that those subscriptions will have some affiliated behaviors such as opportunistic
+     * subscription may be invisible to the user.
      *
-     * @return subId of parent subscription if it’s bundled with a primary subscription.
-     * If there isn't one, {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
+     * @return group UUID a String of group UUID if it belongs to a group. Otherwise
+     * it will return null.
      */
-    public int getParentSubId() {
-        return mParentSubId;
+    public String getGroupUuid() {
+        return mGroupUUID;
     }
 
     /**
@@ -493,11 +495,11 @@
             UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
             String cardId = source.readString();
             boolean isOpportunistic = source.readBoolean();
-            int parentSubId = source.readInt();
+            String groupUUID = source.readString();
 
             return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
                     nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
-                    isEmbedded, accessRules, cardId, isOpportunistic, parentSubId);
+                    isEmbedded, accessRules, cardId, isOpportunistic, groupUUID);
         }
 
         @Override
@@ -525,7 +527,7 @@
         dest.writeTypedArray(mAccessRules, flags);
         dest.writeString(mCardId);
         dest.writeBoolean(mIsOpportunistic);
-        dest.writeInt(mParentSubId);
+        dest.writeString(mGroupUUID);
     }
 
     @Override
@@ -559,13 +561,13 @@
                 + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
                 + " accessRules " + Arrays.toString(mAccessRules)
                 + " cardId=" + cardIdToPrint + " isOpportunistic " + mIsOpportunistic
-                + " parentSubId=" + mParentSubId + "}";
+                + " mGroupUUID=" + mGroupUUID + "}";
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
-                mIsOpportunistic, mParentSubId, mIccId, mNumber, mMcc, mMnc, mCountryIso,
+                mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc, mCountryIso,
                 mCardId, mDisplayName, mCarrierName, mAccessRules);
     }
 
@@ -588,7 +590,7 @@
                 && mDataRoaming == toCompare.mDataRoaming
                 && mIsEmbedded == toCompare.mIsEmbedded
                 && mIsOpportunistic == toCompare.mIsOpportunistic
-                && mParentSubId == toCompare.mParentSubId
+                && Objects.equals(mGroupUUID, toCompare.mGroupUUID)
                 && Objects.equals(mIccId, toCompare.mIccId)
                 && Objects.equals(mNumber, toCompare.mNumber)
                 && Objects.equals(mMcc, toCompare.mMcc)
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 31770ec..1dafc12 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -119,7 +119,6 @@
     @UnsupportedAppUsage
     public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo");
 
-
     /**
      * Generates a content {@link Uri} used to receive updates on simInfo change
      * on the given subscriptionId
@@ -577,6 +576,15 @@
     public static final String PARENT_SUB_ID = "parent_sub_id";
 
     /**
+     * TelephonyProvider column name for group ID. Subscriptions with same group ID
+     * are considered bundled together, and should behave as a single subscription at
+     * certain scenarios.
+     *
+     * @hide
+     */
+    public static final String GROUP_UUID = "group_uuid";
+
+    /**
      * Broadcast Action: The user has changed one of the default subs related to
      * data, phone calls, or sms</p>
      *
@@ -2290,7 +2298,7 @@
      * subscription dynamically in multi-SIM devices.
      *
      * @param subId which subscription is preferred to for cellular data. If it's
-     *              {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}, it means
+     *              {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, it means
      *              it's unset and {@link SubscriptionManager#getDefaultDataSubscriptionId()}
      *              is used to determine which modem is preferred.
      * @hide
@@ -2365,19 +2373,40 @@
     }
 
     /**
-     * Set parent subId by simInfo index
+     * Inform SubscriptionManager that subscriptions in the list are bundled
+     * as a group. Typically it's a primary subscription and an opportunistic
+     * subscription. It should only affect multi-SIM scenarios where primary
+     * and opportunistic subscriptions can be activated together.
+     * Being in the same group means they might be activated or deactivated
+     * together, some of them may be invisible to the users, etc.
      *
-     * @param parentSubId subId of its parent subscription.
-     * @param subId the unique SubscriptionInfo index in database
-     * @return the number of records updated
-     * @hide
+     * Caller will either have {@link android.Manifest.permission.MODIFY_PHONE_STATE}
+     * permission or can manage all subscriptions in the list, according to their
+     * acess rules.
+     *
+     * @param subIdList list of subId that will be in the same group
+     * @return groupUUID a UUID assigned to the subscription group. It returns
+     * null if fails.
      *
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public int setParentSubId(int parentSubId, int subId) {
-        if (VDBG) logd("[setParentSubId]+ parentSubId:" + parentSubId + " subId:" + subId);
-        return setSubscriptionPropertyHelper(subId, "parentSubId",
-                (iSub)-> iSub.setParentSubId(parentSubId, subId));
+    public String setSubscriptionGroup(int[] subIdList) {
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        if (VDBG) {
+            logd("[setSubscriptionGroup]+ subIdList:" + Arrays.toString(subIdList));
+        }
+
+        String groupUUID = null;
+        try {
+            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            if (iSub != null) {
+                groupUUID = iSub.setSubscriptionGroup(subIdList, pkgForDebug);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return groupUUID;
     }
 
     private interface CallISubMethodHelper {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index bd1a0fb..d72b5ec 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -21,6 +21,7 @@
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.Manifest;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -43,6 +44,7 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.BatteryStats;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -52,6 +54,7 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.os.WorkSource;
 import android.provider.Settings.SettingNotFoundException;
 import android.service.carrier.CarrierIdentifier;
 import android.telecom.PhoneAccount;
@@ -4750,37 +4753,42 @@
     }
 
     /**
-     * Returns all observed cell information from all radios on the
-     * device including the primary and neighboring cells. Calling this method does
-     * not trigger a call to {@link android.telephony.PhoneStateListener#onCellInfoChanged
-     * onCellInfoChanged()}, or change the rate at which
-     * {@link android.telephony.PhoneStateListener#onCellInfoChanged
-     * onCellInfoChanged()} is called.
+     * Requests all available cell information from all radios on the device including the
+     * camped/registered, serving, and neighboring cells.
      *
-     *<p>
-     * The list can include one or more {@link android.telephony.CellInfoGsm CellInfoGsm},
+     * <p>The response can include one or more {@link android.telephony.CellInfoGsm CellInfoGsm},
      * {@link android.telephony.CellInfoCdma CellInfoCdma},
+     * {@link android.telephony.CellInfoTdscdma CellInfoTdscdma},
      * {@link android.telephony.CellInfoLte CellInfoLte}, and
      * {@link android.telephony.CellInfoWcdma CellInfoWcdma} objects, in any combination.
-     * On devices with multiple radios it is typical to see instances of
-     * one or more of any these in the list. In addition, zero, one, or more
-     * of the returned objects may be considered registered; that is, their
+     * It is typical to see instances of one or more of any these in the list. In addition, zero
+     * or more of the returned objects may be considered registered; that is, their
      * {@link android.telephony.CellInfo#isRegistered CellInfo.isRegistered()}
-     * methods may return true.
+     * methods may return true, indicating that the cell is being used or would be used for
+     * signaling communication if necessary.
      *
-     * <p>This method returns valid data for registered cells on devices with
-     * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}. In cases where only
-     * partial information is available for a particular CellInfo entry, unavailable fields
-     * will be reported as Integer.MAX_VALUE. All reported cells will include at least a
-     * valid set of technology-specific identification info and a power level measurement.
+     * <p>Beginning with {@link android.os.Build.VERSION_CODES#Q Android Q},
+     * if this API results in a change of the cached CellInfo, that change will be reported via
+     * {@link android.telephony.PhoneStateListener#onCellInfoChanged onCellInfoChanged()}.
      *
-     *<p>
-     * This method is preferred over using {@link
+     * <p>Apps targeting {@link android.os.Build.VERSION_CODES#Q Android Q} or higher will no
+     * longer trigger a refresh of the cached CellInfo by invoking this API. Instead, those apps
+     * will receive the latest cached results. Apps targeting
+     * {@link android.os.Build.VERSION_CODES#Q Android Q} or higher that wish to request updated
+     * CellInfo should call
+     * {android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()} and
+     * listen for responses via {@link android.telephony.PhoneStateListener#onCellInfoChanged
+     * onCellInfoChanged()}.
+     *
+     * <p>This method returns valid data for devices with
+     * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. In cases
+     * where only partial information is available for a particular CellInfo entry, unavailable
+     * fields will be reported as {@link android.telephony.CellInfo#UNAVAILABLE}. All reported
+     * cells will include at least a valid set of technology-specific identification info and a
+     * power level measurement.
+     *
+     * <p>This method is preferred over using {@link
      * android.telephony.TelephonyManager#getCellLocation getCellLocation()}.
-     * However, for older devices, <code>getAllCellInfo()</code> may return
-     * null. In these cases, you should call {@link
-     * android.telephony.TelephonyManager#getCellLocation getCellLocation()}
-     * instead.
      *
      * @return List of {@link android.telephony.CellInfo}; null if cell
      * information is unavailable.
@@ -4791,11 +4799,92 @@
             ITelephony telephony = getITelephony();
             if (telephony == null)
                 return null;
-            return telephony.getAllCellInfo(getOpPackageName());
+            return telephony.getAllCellInfo(
+                    getOpPackageName());
         } catch (RemoteException ex) {
-            return null;
         } catch (NullPointerException ex) {
-            return null;
+        }
+        return null;
+    }
+
+    /** Callback for providing asynchronous {@link CellInfo} on request */
+    public abstract static class CellInfoCallback {
+        /**
+         * Response to
+         * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}.
+         *
+         * <p>Invoked when there is a response to
+         * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}
+         * to provide a list of {@link CellInfo}. If no {@link CellInfo} is available then an empty
+         * list will be provided. If an error occurs, null will be provided.
+         *
+         * @param cellInfo a list of {@link CellInfo}, an empty list, or null.
+         *
+         * {@see android.telephony.TelephonyManager#getAllCellInfo getAllCellInfo()}
+         */
+        public abstract void onCellInfo(List<CellInfo> cellInfo);
+    };
+
+    /**
+     * Requests all available cell information from the current subscription for observed
+     * camped/registered, serving, and neighboring cells.
+     *
+     * <p>Any available results from this request will be provided by calls to
+     * {@link android.telephony.PhoneStateListener#onCellInfoChanged onCellInfoChanged()}
+     * for each active subscription.
+     *
+     * @param executor the executor on which callback will be invoked.
+     * @param callback a callback to receive CellInfo.
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+    public void requestCellInfoUpdate(
+            @NonNull Executor executor, @NonNull CellInfoCallback callback) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) return;
+            telephony.requestCellInfoUpdate(
+                    getSubId(),
+                    new ICellInfoCallback.Stub() {
+                        public void onCellInfo(List<CellInfo> cellInfo) {
+                            Binder.withCleanCallingIdentity(() ->
+                                    executor.execute(() -> callback.onCellInfo(cellInfo)));
+                        }
+                    }, getOpPackageName());
+
+        } catch (RemoteException ex) {
+        }
+    }
+
+    /**
+     * Requests all available cell information from the current subscription for observed
+     * camped/registered, serving, and neighboring cells.
+     *
+     * <p>Any available results from this request will be provided by calls to
+     * {@link android.telephony.PhoneStateListener#onCellInfoChanged onCellInfoChanged()}
+     * for each active subscription.
+     *
+     * @param workSource the requestor to whom the power consumption for this should be attributed.
+     * @param executor the executor on which callback will be invoked.
+     * @param callback a callback to receive CellInfo.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_COARSE_LOCATION,
+            android.Manifest.permission.MODIFY_PHONE_STATE})
+    public void requestCellInfoUpdate(@NonNull WorkSource workSource,
+            @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) return;
+            telephony.requestCellInfoUpdateWithWorkSource(
+                    getSubId(),
+                    new ICellInfoCallback.Stub() {
+                        public void onCellInfo(List<CellInfo> cellInfo) {
+                            Binder.withCleanCallingIdentity(() ->
+                                    executor.execute(() -> callback.onCellInfo(cellInfo)));
+                        }
+                    }, getOpPackageName(), workSource);
+        } catch (RemoteException ex) {
         }
     }
 
@@ -6349,7 +6438,6 @@
 
     /**
      * Set the preferred network type.
-     * Used for device configuration by some CDMA operators.
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
diff --git a/telephony/java/com/android/internal/telephony/IApnSourceService.aidl b/telephony/java/com/android/internal/telephony/IApnSourceService.aidl
index 07bb18b..34c9067 100644
--- a/telephony/java/com/android/internal/telephony/IApnSourceService.aidl
+++ b/telephony/java/com/android/internal/telephony/IApnSourceService.aidl
@@ -20,5 +20,5 @@
 
 interface IApnSourceService {
     /** Retreive APNs. */
-    ContentValues[] getApns();
+    ContentValues[] getApns(int subId);
 }
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 4bdec08..bc44519 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -165,13 +165,23 @@
     int setOpportunistic(boolean opportunistic, int subId);
 
     /**
-     * Set parent subId by simInfo index
+     * Inform SubscriptionManager that subscriptions in the list are bundled
+     * as a group. Typically it's a primary subscription and an opportunistic
+     * subscription. It should only affect multi-SIM scenarios where primary
+     * and opportunistic subscriptions can be activated together.
+     * Being in the same group means they might be activated or deactivated
+     * together, some of them may be invisible to the users, etc.
      *
-     * @param parentSubId: subId of its parent subscription.
-     * @param subId the unique SubscriptionInfo index in database
-     * @return the number of records updated
+     * Caller will either have {@link android.Manifest.permission.MODIFY_PHONE_STATE}
+     * permission or can manage all subscriptions in the list, according to their
+     * acess rules.
+     *
+     * @param subIdList list of subId that will be in the same group
+     * @return groupUUID a UUID assigned to the subscription group. It returns
+     * null if fails.
+     *
      */
-    int setParentSubId(int parentSubId, int subId);
+    String setSubscriptionGroup(in int[] subIdList, String callingPackage);
 
     /**
      * Set which subscription is preferred for cellular data. It's
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 85a9cf5..f0e8586 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -22,6 +22,7 @@
 import android.os.IBinder;
 import android.os.Messenger;
 import android.os.ResultReceiver;
+import android.os.WorkSource;
 import android.net.NetworkStats;
 import android.net.Uri;
 import android.service.carrier.CarrierIdentifier;
@@ -30,6 +31,7 @@
 import android.telephony.CellInfo;
 import android.telephony.ClientRequestStats;
 import android.telephony.IccOpenLogicalChannelResponse;
+import android.telephony.ICellInfoCallback;
 import android.telephony.ModemActivityInfo;
 import android.telephony.NeighboringCellInfo;
 import android.telephony.NetworkScanRequest;
@@ -507,11 +509,26 @@
     int getLteOnCdmaModeForSubscriber(int subId, String callingPackage);
 
     /**
-     * Returns the all observed cell information of the device.
+     * Returns all observed cell information of the device.
      */
     List<CellInfo> getAllCellInfo(String callingPkg);
 
     /**
+     * Request a cell information update for the specified subscription,
+     * reported via the CellInfoCallback.
+     */
+    void requestCellInfoUpdate(int subId, in ICellInfoCallback cb, String callingPkg);
+
+    /**
+     * Request a cell information update for the specified subscription,
+     * reported via the CellInfoCallback.
+     *
+     * @param workSource the requestor to whom the power consumption for this should be attributed.
+     */
+    void requestCellInfoUpdateWithWorkSource(
+            int subId, in ICellInfoCallback cb, in String callingPkg, in WorkSource ws);
+
+    /**
      * Sets minimum time in milli-seconds between onCellInfoChanged
      */
     void setCellInfoListRate(int rateInMillis);
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 0bc5221..583f14a 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -431,7 +431,7 @@
 
 void Debug::DumpResStringPool(const android::ResStringPool* pool, text::Printer* printer) {
   using namespace android;
-  
+
   if (pool->getError() == NO_INIT) {
     printer->Print("String pool is unitialized.\n");
     return;
@@ -460,7 +460,7 @@
   const size_t NS = pool->size();
   for (size_t s=0; s<NS; s++) {
     String8 str = pool->string8ObjectAt(s);
-    printer->Print(StringPrintf("String #%zd : %s\n", s, str.string()));
+    printer->Print(StringPrintf("String #%zd: %s\n", s, str.string()));
   }
 }
 
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index dbe5ac5..da22e88 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -718,7 +718,7 @@
           // This must be a FileReference.
           std::unique_ptr<FileReference> file_ref =
               util::make_unique<FileReference>(dst_pool->MakeRef(
-                  str, StringPool::Context(StringPool::Context::kHighPriority, config)));
+                  str, StringPool::Context(StringPool::Context::kHighPriority, config), data));
           if (type == ResourceType::kRaw) {
             file_ref->type = ResourceFile::Type::kUnknown;
           } else if (util::EndsWith(*file_ref->path, ".xml")) {
@@ -730,7 +730,7 @@
         }
 
         // There are no styles associated with this string, so treat it as a simple string.
-        return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config)));
+        return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config), data));
       }
     } break;
 
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index 8eabd32..a8c2666 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -165,12 +165,13 @@
   return MakeRefImpl(str, Context{}, true);
 }
 
-StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context) {
-  return MakeRefImpl(str, context, true);
+StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context,
+                                    Maybe<size_t> index) {
+  return MakeRefImpl(str, context, true, index);
 }
 
 StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, const Context& context,
-                                        bool unique) {
+                                        bool unique, Maybe<size_t> index) {
   if (unique) {
     auto range = indexed_strings_.equal_range(str);
     for (auto iter = range.first; iter != range.second; ++iter) {
@@ -180,15 +181,26 @@
     }
   }
 
+  const size_t size = strings_.size();
+  // Insert the string at the end of the string vector if no index is specified
+  const size_t insertion_index = index ? index.value() : size;
+
   std::unique_ptr<Entry> entry(new Entry());
   entry->value = str.to_string();
   entry->context = context;
-  entry->index_ = strings_.size();
+  entry->index_ = insertion_index;
   entry->ref_ = 0;
   entry->pool_ = this;
 
   Entry* borrow = entry.get();
-  strings_.emplace_back(std::move(entry));
+  if (insertion_index == size) {
+    strings_.emplace_back(std::move(entry));
+  } else {
+    // Allocate enough space for the string at the index
+    strings_.resize(std::max(insertion_index + 1, size));
+    strings_[insertion_index] = std::move(entry);
+  }
+
   indexed_strings_.insert(std::make_pair(StringPiece(borrow->value), borrow));
   return Ref(borrow);
 }
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 1006ca9..115d5d3 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -166,7 +166,8 @@
 
   // Adds a string to the pool, unless it already exists, with a context object that can be used
   // when sorting the string pool. Returns a reference to the string in the pool.
-  Ref MakeRef(const android::StringPiece& str, const Context& context);
+  Ref MakeRef(const android::StringPiece& str, const Context& context,
+              Maybe<size_t> index = {});
 
   // Adds a string from another string pool. Returns a reference to the string in the string pool.
   Ref MakeRef(const Ref& ref);
@@ -210,7 +211,8 @@
 
   static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8, IDiagnostics* diag);
 
-  Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique);
+  Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique,
+                  Maybe<size_t> index = {});
   void ReAssignIndices();
 
   std::vector<std::unique_ptr<Entry>> strings_;
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 9a7238b..648be7d 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -84,6 +84,24 @@
   EXPECT_THAT(ref_c.index(), Eq(2u));
 }
 
+TEST(StringPoolTest, AssignStringIndex) {
+  StringPool pool;
+
+  StringPool::Ref ref_a = pool.MakeRef("0", StringPool::Context{}, 0u);
+  StringPool::Ref ref_b = pool.MakeRef("1", StringPool::Context{}, 1u);
+  StringPool::Ref ref_c = pool.MakeRef("5", StringPool::Context{}, 5u);
+  StringPool::Ref ref_d = pool.MakeRef("2", StringPool::Context{}, 2u);
+  StringPool::Ref ref_e = pool.MakeRef("4", StringPool::Context{}, 4u);
+  StringPool::Ref ref_f = pool.MakeRef("3", StringPool::Context{}, 3u);
+
+  EXPECT_THAT(ref_a.index(), Eq(0u));
+  EXPECT_THAT(ref_b.index(), Eq(1u));
+  EXPECT_THAT(ref_d.index(), Eq(2u));
+  EXPECT_THAT(ref_f.index(), Eq(3u));
+  EXPECT_THAT(ref_e.index(), Eq(4u));
+  EXPECT_THAT(ref_c.index(), Eq(5u));
+}
+
 TEST(StringPoolTest, PruneStringsWithNoReferences) {
   StringPool pool;
 
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 3ea1755..4492f6b 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -57,78 +57,6 @@
   Source source_;
 };
 
-bool ConvertApk(IAaptContext* context, unique_ptr<LoadedApk> apk, IApkSerializer* serializer,
-                IArchiveWriter* writer) {
-  io::IFile* manifest = apk->GetFileCollection()->FindFile(kAndroidManifestPath);
-  if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer,
-                                (manifest != nullptr && manifest->WasCompressed())
-                                    ? ArchiveEntry::kCompress : 0u)) {
-    context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                     << "failed to serialize AndroidManifest.xml");
-    return false;
-  }
-
-  if (apk->GetResourceTable() != nullptr) {
-    // The table might be modified by below code.
-    auto converted_table = apk->GetResourceTable();
-
-    // Resources
-    for (const auto& package : converted_table->packages) {
-      for (const auto& type : package->types) {
-        for (const auto& entry : type->entries) {
-          for (const auto& config_value : entry->values) {
-            FileReference* file = ValueCast<FileReference>(config_value->value.get());
-            if (file != nullptr) {
-              if (file->file == nullptr) {
-                context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                                 << "no file associated with " << *file);
-                return false;
-              }
-
-              if (!serializer->SerializeFile(file, writer)) {
-                context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                                 << "failed to serialize file " << *file->path);
-                return false;
-              }
-            } // file
-          } // config_value
-        } // entry
-      } // type
-    } // package
-
-    // Converted resource table
-    if (!serializer->SerializeTable(converted_table, writer)) {
-      context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                       << "failed to serialize the resource table");
-      return false;
-    }
-  }
-
-  // Other files
-  std::unique_ptr<io::IFileCollectionIterator> iterator = apk->GetFileCollection()->Iterator();
-  while (iterator->HasNext()) {
-    io::IFile* file = iterator->Next();
-    std::string path = file->GetSource().path;
-
-    // Manifest, resource table and resources have already been taken care of.
-    if (path == kAndroidManifestPath ||
-        path == kApkResourceTablePath ||
-        path == kProtoResourceTablePath ||
-        path.find("res/") == 0) {
-      continue;
-    }
-
-    if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) {
-      context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                       << "failed to copy file " << path);
-      return false;
-    }
-  }
-
-  return true;
-}
-
-
 class BinaryApkSerializer : public IApkSerializer {
  public:
   BinaryApkSerializer(IAaptContext* context, const Source& source,
@@ -323,12 +251,97 @@
   StdErrDiagnostics diag_;
 };
 
+int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer,
+            ApkFormat output_format, TableFlattenerOptions& options) {
+  // Do not change the ordering of strings in the values string pool
+  options.sort_stringpool_entries = false;
+
+  unique_ptr<IApkSerializer> serializer;
+  if (output_format == ApkFormat::kBinary) {
+    serializer.reset(new BinaryApkSerializer(context, apk->GetSource(), options));
+  } else if (output_format == ApkFormat::kProto) {
+    serializer.reset(new ProtoApkSerializer(context, apk->GetSource()));
+  } else {
+    context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+                                     << "Cannot convert APK to unknown format");
+    return 1;
+  }
+
+  io::IFile* manifest = apk->GetFileCollection()->FindFile(kAndroidManifestPath);
+  if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/,
+                                output_writer, (manifest != nullptr && manifest->WasCompressed())
+                                ? ArchiveEntry::kCompress : 0u)) {
+    context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+                                     << "failed to serialize AndroidManifest.xml");
+    return 1;
+  }
+
+  if (apk->GetResourceTable() != nullptr) {
+    // The table might be modified by below code.
+    auto converted_table = apk->GetResourceTable();
+
+    // Resources
+    for (const auto& package : converted_table->packages) {
+      for (const auto& type : package->types) {
+        for (const auto& entry : type->entries) {
+          for (const auto& config_value : entry->values) {
+            FileReference* file = ValueCast<FileReference>(config_value->value.get());
+            if (file != nullptr) {
+              if (file->file == nullptr) {
+                context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+                                                 << "no file associated with " << *file);
+                return 1;
+              }
+
+              if (!serializer->SerializeFile(file, output_writer)) {
+                context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+                                                 << "failed to serialize file " << *file->path);
+                return 1;
+              }
+            } // file
+          } // config_value
+        } // entry
+      } // type
+    } // package
+
+    // Converted resource table
+    if (!serializer->SerializeTable(converted_table, output_writer)) {
+      context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+                                       << "failed to serialize the resource table");
+      return 1;
+    }
+  }
+
+  // Other files
+  std::unique_ptr<io::IFileCollectionIterator> iterator = apk->GetFileCollection()->Iterator();
+  while (iterator->HasNext()) {
+    io::IFile* file = iterator->Next();
+    std::string path = file->GetSource().path;
+
+    // Manifest, resource table and resources have already been taken care of.
+    if (path == kAndroidManifestPath ||
+        path == kApkResourceTablePath ||
+        path == kProtoResourceTablePath ||
+        path.find("res/") == 0) {
+      continue;
+    }
+
+    if (!io::CopyFileToArchivePreserveCompression(context, file, path, output_writer)) {
+      context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+                                       << "failed to copy file " << path);
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
 const char* ConvertCommand::kOutputFormatProto = "proto";
 const char* ConvertCommand::kOutputFormatBinary = "binary";
 
 int ConvertCommand::Action(const std::vector<std::string>& args) {
   if (args.size() != 1) {
-    std::cerr << "must supply a single proto APK\n";
+    std::cerr << "must supply a single APK\n";
     Usage(&std::cerr);
     return 1;
   }
@@ -341,34 +354,31 @@
     return 1;
   }
 
-  Maybe<AppInfo> app_info =
-      ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics());
+  Maybe<AppInfo> app_info = ExtractAppInfoFromBinaryManifest(*apk->GetManifest(),
+                                                             context.GetDiagnostics());
   if (!app_info) {
     return 1;
   }
 
   context.package_ = app_info.value().package;
-
-  unique_ptr<IArchiveWriter> writer =
-      CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path_);
+  unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(context.GetDiagnostics(),
+                                                                 output_path_);
   if (writer == nullptr) {
     return 1;
   }
 
-  unique_ptr<IApkSerializer> serializer;
+  ApkFormat format;
   if (!output_format_ || output_format_.value() == ConvertCommand::kOutputFormatBinary) {
-
-    serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options_));
+    format = ApkFormat::kBinary;
   } else if (output_format_.value() == ConvertCommand::kOutputFormatProto) {
-    serializer.reset(new ProtoApkSerializer(&context, apk->GetSource()));
+    format = ApkFormat::kProto;
   } else {
-    context.GetDiagnostics()->Error(DiagMessage(path)
-        << "Invalid value for flag --output-format: "
-        << output_format_.value());
+    context.GetDiagnostics()->Error(DiagMessage(path) << "Invalid value for flag --output-format: "
+                                                      << output_format_.value());
     return 1;
   }
 
-  return ConvertApk(&context, std::move(apk), serializer.get(), writer.get()) ? 0 : 1;
+  return Convert(&context, apk.get(), writer.get(), format, options_);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h
index fcec23d..6a6719c 100644
--- a/tools/aapt2/cmd/Convert.h
+++ b/tools/aapt2/cmd/Convert.h
@@ -18,6 +18,7 @@
 #define AAPT2_CONVERT_H
 
 #include "Command.h"
+#include "LoadedApk.h"
 #include "format/binary/TableFlattener.h"
 
 namespace aapt {
@@ -49,6 +50,9 @@
   bool verbose_ = false;
 };
 
-}// namespace aapt
+int Convert(IAaptContext* context, LoadedApk* input, IArchiveWriter* output_writer,
+            ApkFormat output_format, TableFlattenerOptions& options);
+
+}  // namespace aapt
 
 #endif //AAPT2_CONVERT_H
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 8a86f63a..6c1a9ba 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -573,15 +573,17 @@
 }  // namespace
 
 bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
-  // We must do this before writing the resources, since the string pool IDs may change.
-  table->string_pool.Prune();
-  table->string_pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int {
-    int diff = util::compare(a.priority, b.priority);
-    if (diff == 0) {
-      diff = a.config.compare(b.config);
-    }
-    return diff;
-  });
+  if (options_.sort_stringpool_entries) {
+    // We must do this before writing the resources, since the string pool IDs may change.
+    table->string_pool.Prune();
+    table->string_pool.Sort([](const StringPool::Context &a, const StringPool::Context &b) -> int {
+      int diff = util::compare(a.priority, b.priority);
+      if (diff == 0) {
+        diff = a.config.compare(b.config);
+      }
+      return diff;
+    });
+  }
 
   // Write the ResTable header.
   ChunkWriter table_writer(buffer_);
diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
index c2e1d4b..635cb21 100644
--- a/tools/aapt2/format/binary/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -43,6 +43,9 @@
 
   // Set of whitelisted resource names to avoid altering in key stringpool
   std::set<std::string> whitelisted_resources;
+
+  // When true, sort the entries in the values string pool by priority and configuration.
+  bool sort_stringpool_entries = true;
 };
 
 class TableFlattener : public IResourceTableConsumer {
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1fd68ec..92f60ef 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -61,11 +61,9 @@
 
     ParceledListSlice getPrivilegedConfiguredNetworks();
 
-    WifiConfiguration getMatchingWifiConfig(in ScanResult scanResult);
+    List<WifiConfiguration> getAllMatchingWifiConfigs(in List<ScanResult> scanResult);
 
-    List<WifiConfiguration> getAllMatchingWifiConfigs(in ScanResult scanResult);
-
-    List<OsuProvider> getMatchingOsuProviders(in ScanResult scanResult);
+    List<OsuProvider> getMatchingOsuProviders(in List<ScanResult> scanResult);
 
     int addOrUpdateNetwork(in WifiConfiguration config, String packageName);
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7919074..f934380 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1073,55 +1073,43 @@
     }
 
     /**
-     * Returns a WifiConfiguration matching this ScanResult
-     *
-     * @param scanResult scanResult that represents the BSSID
-     * @return {@link WifiConfiguration} that matches this BSSID or null
-     * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
-        try {
-            return mService.getMatchingWifiConfig(scanResult);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Return all matching WifiConfigurations for this ScanResult.
+     * Returns all matching WifiConfigurations for a given list of ScanResult.
      *
      * An empty list will be returned when no configurations are installed or if no configurations
      * match the ScanResult.
-     *
-     * @param scanResult scanResult that represents the BSSID
-     * @return A list of {@link WifiConfiguration}
+
+     * @param scanResults a list of scanResult that represents the BSSID
+     * @return A list of {@link WifiConfiguration} that can have duplicate entries.
      * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
      * @hide
      */
-    public List<WifiConfiguration> getAllMatchingWifiConfigs(ScanResult scanResult) {
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public List<WifiConfiguration> getAllMatchingWifiConfigs(
+            @NonNull List<ScanResult> scanResults) {
         try {
-            return mService.getAllMatchingWifiConfigs(scanResult);
+            return mService.getAllMatchingWifiConfigs(scanResults);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
-
     /**
-     * Returns a list of Hotspot 2.0 OSU (Online Sign-Up) providers associated with the given AP.
+     * Returns a list of unique Hotspot 2.0 OSU (Online Sign-Up) providers associated with a given
+     * list of ScanResult.
      *
      * An empty list will be returned if no match is found.
      *
-     * @param scanResult scanResult that represents the BSSID
-     * @return list of {@link OsuProvider}
+     * @param scanResults a list of ScanResult
+     * @return A list of {@link OsuProvider} that does not contain duplicate entries.
      * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
      * @hide
      */
-    public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) {
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
         try {
-            return mService.getMatchingOsuProviders(scanResult);
+            return mService.getMatchingOsuProviders(scanResults);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index e6892be..f58a006 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -491,6 +491,17 @@
     /** @hide */
     public static final int FACTORY_RESET_SUCCEEDED                 = BASE + 84;
 
+    /** @hide */
+    public static final int REQUEST_ONGOING_PEER_CONFIG             = BASE + 85;
+    /** @hide */
+    public static final int RESPONSE_ONGOING_PEER_CONFIG            = BASE + 86;
+    /** @hide */
+    public static final int SET_ONGOING_PEER_CONFIG                 = BASE + 87;
+    /** @hide */
+    public static final int SET_ONGOING_PEER_CONFIG_FAILED          = BASE + 88;
+    /** @hide */
+    public static final int SET_ONGOING_PEER_CONFIG_SUCCEEDED       = BASE + 89;
+
     /**
      * Create a new WifiP2pManager instance. Applications use
      * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -680,6 +691,18 @@
     }
 
     /**
+     * Interface for callback invocation when ongoing peer info is available
+     * @hide
+     */
+    public interface OngoingPeerInfoListener {
+        /**
+         * The requested ongoing WifiP2pConfig is available
+         * @param peerConfig WifiP2pConfig for current connecting session
+         */
+        void onOngoingPeerAvailable(WifiP2pConfig peerConfig);
+    }
+
+    /**
      * A channel that connects the application to the Wifi p2p framework.
      * Most p2p operations require a Channel as an argument. An instance of Channel is obtained
      * by doing a call on {@link #initialize}
@@ -787,6 +810,7 @@
                     case SET_CHANNEL_FAILED:
                     case REPORT_NFC_HANDOVER_FAILED:
                     case FACTORY_RESET_FAILED:
+                    case SET_ONGOING_PEER_CONFIG_FAILED:
                         if (listener != null) {
                             ((ActionListener) listener).onFailure(message.arg1);
                         }
@@ -814,6 +838,7 @@
                     case SET_CHANNEL_SUCCEEDED:
                     case REPORT_NFC_HANDOVER_SUCCEEDED:
                     case FACTORY_RESET_SUCCEEDED:
+                    case SET_ONGOING_PEER_CONFIG_SUCCEEDED:
                         if (listener != null) {
                             ((ActionListener) listener).onSuccess();
                         }
@@ -857,6 +882,13 @@
                                     .onHandoverMessageAvailable(handoverMessage);
                         }
                         break;
+                    case RESPONSE_ONGOING_PEER_CONFIG:
+                        WifiP2pConfig peerConfig = (WifiP2pConfig) message.obj;
+                        if (listener != null) {
+                            ((OngoingPeerInfoListener) listener)
+                                    .onOngoingPeerAvailable(peerConfig);
+                        }
+                        break;
                     default:
                         Log.d(TAG, "Ignored " + message);
                         break;
@@ -1536,6 +1568,7 @@
 
     /**
      * Removes all saved p2p groups.
+     *
      * @param c is the channel created at {@link #initialize}.
      * @param listener for callback on success or failure. Can be null.
      * @hide
@@ -1550,4 +1583,37 @@
                 callingPackage);
     }
 
+    /**
+     * Request saved WifiP2pConfig which used for an ongoing peer connection
+     *
+     * @param c is the channel created at {@link #initialize}
+     * @param listener for callback when ongoing peer config updated. Can't be null.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+    public void requestOngoingPeerConfig(@NonNull Channel c,
+            @NonNull OngoingPeerInfoListener listener) {
+        checkChannel(c);
+        c.mAsyncChannel.sendMessage(REQUEST_ONGOING_PEER_CONFIG,
+                Binder.getCallingUid(), c.putListener(listener));
+    }
+
+     /**
+     * Set saved WifiP2pConfig which used for an ongoing peer connection
+     *
+     * @param c is the channel created at {@link #initialize}
+     * @param config used for change an ongoing peer connection
+     * @param listener for callback when ongoing peer config updated. Can be null.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+    public void setOngoingPeerConfig(@NonNull Channel c, @NonNull WifiP2pConfig config,
+            @Nullable ActionListener listener) {
+        checkChannel(c);
+        checkP2pConfig(config);
+        c.mAsyncChannel.sendMessage(SET_ONGOING_PEER_CONFIG, 0,
+                c.putListener(listener), config);
+    }
 }
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
index eede23b..e532428 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -25,7 +25,6 @@
 import android.net.wifi.ISoftApCallback;
 import android.net.wifi.ITrafficStateCallback;
 import android.net.wifi.IWifiManager;
-import android.net.wifi.PasspointManagementObjectDefinition;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiActivityEnergyInfo;
 import android.net.wifi.WifiConfiguration;
@@ -37,7 +36,6 @@
 import android.os.Messenger;
 import android.os.ResultReceiver;
 import android.os.WorkSource;
-import android.util.Slog;
 
 import java.util.List;
 
@@ -83,22 +81,51 @@
         throw new UnsupportedOperationException();
     }
 
-    @Override
+    /**
+     * Returns a WifiConfiguration matching this ScanResult
+     * @param scanResult a single ScanResult Object
+     * @return
+     * @deprecated use {@link #getAllMatchingWifiConfigs(List)} instead.
+     */
+    @Deprecated
     public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
         throw new UnsupportedOperationException();
     }
 
-    @Override
+    /**
+     * Returns all matching WifiConfigurations for this ScanResult.
+     * @param scanResult a single ScanResult Object
+     * @return
+     * @deprecated use {@link #getAllMatchingWifiConfigs(List)} instead.
+     */
+    @Deprecated
     public List<WifiConfiguration> getAllMatchingWifiConfigs(ScanResult scanResult) {
         throw new UnsupportedOperationException();
     }
 
     @Override
+    public List<WifiConfiguration> getAllMatchingWifiConfigs(List<ScanResult> scanResults) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns a list of Hotspot 2.0 OSU (Online Sign-Up) providers associated with the given AP.
+     *
+     * @param scanResult a single ScanResult Object
+     * @return
+     * @deprecated use {@link #getMatchingOsuProviders(List)} instead.
+     */
+    @Deprecated
     public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) {
         throw new UnsupportedOperationException();
     }
 
     @Override
+    public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public int addOrUpdateNetwork(WifiConfiguration config, String packageName) {
         throw new UnsupportedOperationException();
     }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index ea41bb3..a3deb93 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -35,7 +35,20 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyList;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -62,6 +75,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Unit tests for {@link android.net.wifi.WifiManager}.
@@ -1250,4 +1264,26 @@
         userSelectionCallbackCaptor.getValue().reject();
         verify(iUserSelectionCallback).reject();
     }
+
+    /**
+     * Check the call to getAllMatchingWifiConfigs calls getAllMatchingWifiConfigs of WifiService
+     * with the provided a list of ScanResult.
+     */
+    @Test
+    public void testGetAllMatchingWifiConfigs() throws Exception {
+        mWifiManager.getAllMatchingWifiConfigs(new ArrayList<>());
+
+        verify(mWifiService).getAllMatchingWifiConfigs(any(List.class));
+    }
+
+    /**
+     * Check the call to getMatchingOsuProviders calls getMatchingOsuProviders of WifiService
+     * with the provided a list of ScanResult.
+     */
+    @Test
+    public void testGetMatchingOsuProviders() throws Exception {
+        mWifiManager.getMatchingOsuProviders(new ArrayList<>());
+
+        verify(mWifiService).getMatchingOsuProviders(any(List.class));
+    }
 }