Merge "Fixes swipe gesture on non-dismissable notifications."
diff --git a/api/current.txt b/api/current.txt
index cde7499..84f5fcf 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();
   }
@@ -29000,11 +29002,14 @@
     method public boolean is5GHzBandSupported();
     method public boolean isDeviceToApRttSupported();
     method public boolean isEnhancedPowerReportingSupported();
+    method public boolean isOweSupported();
     method public boolean isP2pSupported();
     method public boolean isPreferredNetworkOffloadSupported();
     method public deprecated boolean isScanAlwaysAvailable();
     method public boolean isTdlsSupported();
     method public boolean isWifiEnabled();
+    method public boolean isWpa3SaeSupported();
+    method public boolean isWpa3SuiteBSupported();
     method public deprecated boolean pingSupplicant();
     method public deprecated boolean reassociate();
     method public deprecated boolean reconnect();
diff --git a/api/system-current.txt b/api/system-current.txt
index 2c2df81..da46015 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -137,6 +137,7 @@
     field public static final java.lang.String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
     field public static final java.lang.String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
     field public static final java.lang.String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
+    field public static final java.lang.String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
     field public static final java.lang.String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
     field public static final java.lang.String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
     field public static final java.lang.String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
@@ -3639,6 +3640,7 @@
 
   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.WifiConfiguration> getPrivilegedConfiguredNetworks();
     method public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method public int getWifiApState();
@@ -5461,6 +5463,7 @@
     method public static android.os.PersistableBundle getDefaultConfig();
     method public void overrideConfig(int, android.os.PersistableBundle);
     method public void updateConfigForPhoneId(int, java.lang.String);
+    field public static final java.lang.String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
     field public static final java.lang.String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
   }
 
@@ -5535,8 +5538,10 @@
   public class PhoneStateListener {
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
+    method public void onVoiceActivationStateChanged(int);
     field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
     field public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
+    field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
   }
 
   public class ServiceState implements android.os.Parcelable {
@@ -6600,6 +6605,22 @@
     method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
   }
 
+  public class ProvisioningManager {
+    method public static android.telephony.ims.ProvisioningManager createForSubscriptionId(android.content.Context, int);
+    method public int getProvisioningIntValue(int);
+    method public java.lang.String getProvisioningStringValue(int);
+    method public void registerProvisioningChangedCallback(java.util.concurrent.Executor, android.telephony.ims.ProvisioningManager.Callback);
+    method public int setProvisioningIntValue(int, int);
+    method public int setProvisioningStringValue(int, java.lang.String);
+    method public void unregisterProvisioningChangedCallback(android.telephony.ims.ProvisioningManager.Callback);
+  }
+
+  public static class ProvisioningManager.Callback {
+    ctor public ProvisioningManager.Callback();
+    method public void onProvisioningIntChanged(int, int);
+    method public void onProvisioningStringChanged(int, java.lang.String);
+  }
+
 }
 
 package android.telephony.ims.feature {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 53d9673..e6efc4c 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -148,6 +148,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.
@@ -192,6 +197,9 @@
         NativeProcessMemoryState native_process_memory_state = 10036;
         CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037;
         OnDevicePowerMeasurement on_device_power_measurement = 10038;
+        DeviceCalculatedPowerUse device_calculated_power_use = 10039;
+        DeviceCalculatedPowerBlameUid device_calculated_power_blame_uid = 10040;
+        DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = 10041;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -2135,16 +2143,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;
 }
 
 //////////////////////////////////////////////////////////////////////
@@ -3198,3 +3241,63 @@
     // Time spent in frequency in milliseconds, since thread start.
     optional uint32 time_millis = 7;
 }
+
+/**
+ * Pulls on-device BatteryStats power use calculations for the overall device.
+ */
+message DeviceCalculatedPowerUse {
+    // Power used by the device in mAh, as computed by BatteryStats, since BatteryStats last reset
+    // (i.e. roughly since device was last significantly charged).
+    // Currently, this is BatteryStatsHelper.getComputedPower() (not getTotalPower()).
+    optional float computed_power_milli_amp_hours = 1;
+}
+
+/**
+ * Pulls on-device BatteryStats power use calculations broken down by uid.
+ * This atom should be complemented by DeviceCalculatedPowerBlameOther, which contains the power use
+ * that is attributed to non-uid items. They must all be included to get the total power use.
+ */
+message DeviceCalculatedPowerBlameUid {
+    // Uid being blamed. Note: isolated uids have already been mapped to host uid.
+    optional int32 uid = 1 [(is_uid) = true];
+
+    // Power used by this uid in mAh, as computed by BatteryStats, since BatteryStats last reset
+    // (i.e. roughly since device was last significantly charged).
+    optional float power_milli_amp_hours = 2;
+}
+
+/**
+ * Pulls on-device BatteryStats power use calculations that are not due to a uid, broken down by
+ * drain type.
+ * This atom should be complemented by DeviceCalculatedPowerBlameUid, which contains the blame that
+ * is attributed uids. They must all be included to get the total power use.
+ */
+message DeviceCalculatedPowerBlameOther {
+    // The type of item whose power use is being reported.
+    enum DrainType {
+        AMBIENT_DISPLAY = 0;
+        // reserved 1; reserved "APP"; // Logged instead in DeviceCalculatedPowerBlameUid.
+        BLUETOOTH = 2;
+        CAMERA = 3;
+        // Cell-standby
+        CELL = 4;
+        FLASHLIGHT = 5;
+        IDLE = 6;
+        MEMORY = 7;
+        // Amount that total computed drain exceeded the drain estimated using the
+        // battery level changes and capacity.
+        OVERCOUNTED = 8;
+        PHONE = 9;
+        SCREEN = 10;
+        // Amount that total computed drain was below the drain estimated using the
+        // battery level changes and capacity.
+        UNACCOUNTED = 11;
+        // reserved 12; reserved "USER"; // Entire drain for a user. This is NOT supported.
+        WIFI = 13;
+    }
+    optional DrainType drain_type = 1;
+
+    // Power used by this item in mAh, as computed by BatteryStats, since BatteryStats last reset
+    // (i.e. roughly since device was last significantly charged).
+    optional float power_milli_amp_hours = 2;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 8378ae1..0e131cb 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -243,6 +243,20 @@
           {2, 3, 4, 5, 6},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
+        // DeviceCalculatedPowerUse.
+        {android::util::DEVICE_CALCULATED_POWER_USE,
+         {{}, {}, 1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
+        // DeviceCalculatedPowerBlameUid.
+        {android::util::DEVICE_CALCULATED_POWER_BLAME_UID,
+         {{}, {}, // BatteryStats already merged isolated with host ids so it's unnecessary here.
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
+        // DeviceCalculatedPowerBlameOther.
+        {android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER,
+         {{}, {},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index bd9cf6d..362f4ae 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -88,6 +88,8 @@
     ParceledListSlice getRecentNotifyingAppsForUser(int userId);
     int getBlockedAppCount(int userId);
     boolean areChannelsBypassingDnd();
+    int getAppsBypassingDndCount(int uid);
+    ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId);
 
     // TODO: Remove this when callers have been migrated to the equivalent
     // INotificationListener method.
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/Context.java b/core/java/android/content/Context.java
index 03eba7e..004417b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4335,6 +4335,16 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {android.os.IIdmap2} for managing idmap files (used by overlay
+     * packages).
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String IDMAP_SERVICE = "idmap";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link VrManager} for accessing the VR service.
      *
      * @see #getSystemService(String)
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 5f23749..4371c77 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -58,6 +58,7 @@
 public final class AssetManager implements AutoCloseable {
     private static final String TAG = "AssetManager";
     private static final boolean DEBUG_REFS = false;
+    private static final boolean FEATURE_FLAG_IDMAP2 = false;
 
     private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
 
@@ -195,13 +196,23 @@
             return;
         }
 
-        // Make sure that all IDMAPs are up to date.
-        nativeVerifySystemIdmaps();
 
         try {
             final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
             apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/));
-            loadStaticRuntimeOverlays(apkAssets);
+            if (FEATURE_FLAG_IDMAP2) {
+                final String[] systemIdmapPaths =
+                    nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
+                if (systemIdmapPaths == null) {
+                    throw new IOException("idmap2 scan failed");
+                }
+                for (String idmapPath : systemIdmapPaths) {
+                    apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
+                }
+            } else {
+                nativeVerifySystemIdmaps();
+                loadStaticRuntimeOverlays(apkAssets);
+            }
 
             sSystemApkAssetsSet = new ArraySet<>(apkAssets);
             sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]);
@@ -1404,6 +1415,7 @@
     private static native long nativeAssetGetRemainingLength(long assetPtr);
 
     private static native void nativeVerifySystemIdmaps();
+    private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
 
     // Global debug native methods.
     /**
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 72e1ab9..866bd9a 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -104,14 +104,6 @@
     }
 
     /**
-     * Write a double value.
-     */
-    public void writeDouble(double val) {
-        mTypes.add(EVENT_TYPE_DOUBLE);
-        mValues.add(val);
-    }
-
-    /**
      * Write a storage value.
      */
     public void writeStorage(byte[] val) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e3401e7..b266648 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10324,6 +10324,18 @@
         private static final Validator WIFI_PNO_FREQUENCY_CULLING_ENABLED_VALIDATOR =
                 BOOLEAN_VALIDATOR;
 
+        /**
+         * Setting to enable including recency information when determining pno network priorities.
+         * Disabled by default, and setting it to 1 will enable it.
+         * The value is boolean (0 or 1).
+         * @hide
+         */
+        public static final String WIFI_PNO_RECENCY_SORTING_ENABLED =
+                "wifi_pno_recency_sorting_enabled";
+
+        private static final Validator WIFI_PNO_RECENCY_SORTING_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
        /**
         * The maximum number of times we will retry a connection to an access
         * point for which we have failed in acquiring an IP address from DHCP.
@@ -12799,6 +12811,8 @@
             VALIDATORS.put(DEVICE_DEMO_MODE, BOOLEAN_VALIDATOR);
             VALIDATORS.put(WIFI_PNO_FREQUENCY_CULLING_ENABLED,
                     WIFI_PNO_FREQUENCY_CULLING_ENABLED_VALIDATOR);
+            VALIDATORS.put(WIFI_PNO_RECENCY_SORTING_ENABLED,
+                    WIFI_PNO_RECENCY_SORTING_ENABLED_VALIDATOR);
         }
 
         /**
diff --git a/core/java/android/rolecontrollerservice/IRoleControllerService.aidl b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
index 0000b9f..ac5be06 100644
--- a/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
+++ b/core/java/android/rolecontrollerservice/IRoleControllerService.aidl
@@ -30,4 +30,6 @@
                            in IRoleManagerCallback callback);
 
     void onClearRoleHolders(in String roleName, in IRoleManagerCallback callback);
+
+    void onGrantDefaultRoles(in IRoleManagerCallback callback);
 }
diff --git a/core/java/android/rolecontrollerservice/RoleControllerService.java b/core/java/android/rolecontrollerservice/RoleControllerService.java
index da11bca..44c45bb 100644
--- a/core/java/android/rolecontrollerservice/RoleControllerService.java
+++ b/core/java/android/rolecontrollerservice/RoleControllerService.java
@@ -89,6 +89,13 @@
                 RoleControllerService.this.onClearRoleHolders(roleName,
                         new RoleManagerCallbackDelegate(callback));
             }
+
+            @Override
+            public void onGrantDefaultRoles(IRoleManagerCallback callback) {
+                Preconditions.checkNotNull(callback, "callback cannot be null");
+                RoleControllerService.this.onGrantDefaultRoles(
+                        new RoleManagerCallbackDelegate(callback));
+            }
         };
     }
 
@@ -133,6 +140,16 @@
     public abstract void onClearRoleHolders(@NonNull String roleName,
             @NonNull RoleManagerCallback callback);
 
+    /**
+     * Called by system to grant default permissions and roles.
+     * <p>
+     * This is typically when creating a new user or upgrading either system or
+     * permission controller package
+     *
+     * @param callback the callback for whether this call is successful
+     */
+    public abstract void onGrantDefaultRoles(@NonNull RoleManagerCallback callback);
+
     private static class RoleManagerCallbackDelegate implements RoleManagerCallback {
 
         private IRoleManagerCallback mCallback;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index c8e0dd2..2d5f3bf 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1270,7 +1270,13 @@
      */
     public float getLineLeft(int line) {
         final int dir = getParagraphDirection(line);
-        final Alignment align = getParagraphAlignment(line);
+        Alignment align = getParagraphAlignment(line);
+        // Before Q, StaticLayout.Builder.setAlignment didn't check whether the input alignment
+        // is null. And when it is null, the old behavior is the same as ALIGN_CENTER.
+        // To keep consistency, we convert a null alignment to ALIGN_CENTER.
+        if (align == null) {
+            align = Alignment.ALIGN_CENTER;
+        }
 
         // First convert combinations of alignment and direction settings to
         // three basic cases: ALIGN_LEFT, ALIGN_RIGHT and ALIGN_CENTER.
@@ -1319,7 +1325,13 @@
      */
     public float getLineRight(int line) {
         final int dir = getParagraphDirection(line);
-        final Alignment align = getParagraphAlignment(line);
+        Alignment align = getParagraphAlignment(line);
+        // Before Q, StaticLayout.Builder.setAlignment didn't check whether the input alignment
+        // is null. And when it is null, the old behavior is the same as ALIGN_CENTER.
+        // To keep consistency, we convert a null alignment to ALIGN_CENTER.
+        if (align == null) {
+            align = Alignment.ALIGN_CENTER;
+        }
 
         final Alignment resultAlign;
         switch(align) {
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 7b638b4..6eb433a 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -201,7 +201,7 @@
             }
         }
 
-        mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
+        mCharsValid = hasReplacement;
 
         if (mCharsValid) {
             if (mChars == null || mChars.length < mLen) {
@@ -815,7 +815,6 @@
         } 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/services/core/java/com/android/server/input/InputApplicationHandle.java b/core/java/android/view/InputApplicationHandle.java
similarity index 97%
rename from services/core/java/com/android/server/input/InputApplicationHandle.java
rename to core/java/android/view/InputApplicationHandle.java
index 3cf7edc..dc1e505 100644
--- a/services/core/java/com/android/server/input/InputApplicationHandle.java
+++ b/core/java/android/view/InputApplicationHandle.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.input;
+package android.view;
 
 /**
  * Functions as a handle for an application that can receive input.
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index b2dd6ac..84c8e7a 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -18,6 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
+import android.os.IBinder;
 import android.os.Parcelable;
 import android.util.Slog;
 
@@ -50,15 +51,17 @@
     @SuppressWarnings("unused")
     @UnsupportedAppUsage
     private long mPtr; // used by native code
-    
+
     private static native InputChannel[] nativeOpenInputChannelPair(String name);
-    
+
     private native void nativeDispose(boolean finalized);
     private native void nativeTransferTo(InputChannel other);
     private native void nativeReadFromParcel(Parcel parcel);
     private native void nativeWriteToParcel(Parcel parcel);
     private native void nativeDup(InputChannel target);
-    
+    private native IBinder nativeGetToken();
+    private native void nativeSetToken(IBinder token);
+
     private native String nativeGetName();
 
     /**
@@ -159,14 +162,22 @@
         }
         
         nativeWriteToParcel(out);
-        
+
         if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0) {
             dispose();
         }
     }
-    
+
     @Override
     public String toString() {
         return getName();
     }
+
+    public IBinder getToken() {
+        return nativeGetToken();
+    }
+
+    public void setToken(IBinder token) {
+        nativeSetToken(token);
+    }
 }
diff --git a/services/core/java/com/android/server/input/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
similarity index 93%
rename from services/core/java/com/android/server/input/InputWindowHandle.java
rename to core/java/android/view/InputWindowHandle.java
index bb29bf8..621ee89 100644
--- a/services/core/java/com/android/server/input/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.input;
+package android.view;
 
 import android.graphics.Region;
 import android.view.IWindow;
@@ -34,9 +34,6 @@
     // The input application handle.
     public final InputApplicationHandle inputApplicationHandle;
 
-    // The window manager's window state.
-    public final Object windowState;
-
     // The client window.
     public final IWindow clientWindow;
 
@@ -97,9 +94,8 @@
     private native void nativeDispose();
 
     public InputWindowHandle(InputApplicationHandle inputApplicationHandle,
-            Object windowState, IWindow clientWindow, int displayId) {
+            IWindow clientWindow, int displayId) {
         this.inputApplicationHandle = inputApplicationHandle;
-        this.windowState = windowState;
         this.clientWindow = clientWindow;
         this.displayId = displayId;
     }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3d16eb8..a7a5024 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -153,6 +153,9 @@
 
     private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken);
 
+    private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject,
+            InputWindowHandle handle);
+
 
     private final CloseGuard mCloseGuard = CloseGuard.get();
     private final String mName;
@@ -1459,6 +1462,12 @@
             return this;
         }
 
+        public Transaction setInputWindowInfo(SurfaceControl sc, InputWindowHandle handle) {
+            sc.checkNotReleased();
+            nativeSetInputWindowInfo(mNativeObject, sc.mNativeObject, handle);
+            return this;
+        }
+
         @UnsupportedAppUsage
         public Transaction setMatrix(SurfaceControl sc,
                 float dsdx, float dtdx, float dtdy, float dsdy) {
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/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index b754d84..eb35587 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -16,302 +16,22 @@
 */
 package android.widget;
 
-import android.annotation.UnsupportedAppUsage;
-import android.app.AlertDialog;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageItemInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PermissionGroupInfo;
-import android.content.pm.PermissionInfo;
 import android.graphics.drawable.Drawable;
-import android.os.Parcel;
-import android.os.UserHandle;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
 
 import com.android.internal.R;
 
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 /**
- * This class contains the SecurityPermissions view implementation.
- * Initially the package's advanced or dangerous security permissions
- * are displayed under categorized
- * groups. Clicking on the additional permissions presents
- * extended information consisting of all groups and permissions.
- * To use this view define a LinearLayout or any ViewGroup and add this
- * view by instantiating AppSecurityPermissions and invoking getPermissionsView.
+ * Allows the device admin to show certain dialogs. Should be integrated into settings.
  *
+ * @deprecated
  * {@hide}
  */
+@Deprecated
 public class AppSecurityPermissions {
 
-    public static final int WHICH_NEW = 1<<2;
-    public static final int WHICH_ALL = 0xffff;
-
-    private final static String TAG = "AppSecurityPermissions";
-    private final static boolean localLOGV = false;
-    private final Context mContext;
-    private final LayoutInflater mInflater;
-    private final PackageManager mPm;
-    private final Map<String, MyPermissionGroupInfo> mPermGroups
-            = new HashMap<String, MyPermissionGroupInfo>();
-    private final List<MyPermissionGroupInfo> mPermGroupsList
-            = new ArrayList<MyPermissionGroupInfo>();
-    private final PermissionGroupInfoComparator mPermGroupComparator =
-            new PermissionGroupInfoComparator();
-    private final PermissionInfoComparator mPermComparator = new PermissionInfoComparator();
-    private final List<MyPermissionInfo> mPermsList = new ArrayList<MyPermissionInfo>();
-    private final CharSequence mNewPermPrefix;
-    private String mPackageName;
-
-    /** @hide */
-    static class MyPermissionGroupInfo extends PermissionGroupInfo {
-        CharSequence mLabel;
-
-        final ArrayList<MyPermissionInfo> mNewPermissions = new ArrayList<MyPermissionInfo>();
-        final ArrayList<MyPermissionInfo> mAllPermissions = new ArrayList<MyPermissionInfo>();
-
-        MyPermissionGroupInfo(PermissionInfo perm) {
-            name = perm.packageName;
-            packageName = perm.packageName;
-        }
-
-        MyPermissionGroupInfo(PermissionGroupInfo info) {
-            super(info);
-        }
-
-        public Drawable loadGroupIcon(Context context, PackageManager pm) {
-            if (icon != 0) {
-                return loadUnbadgedIcon(pm);
-            } else {
-                return context.getDrawable(R.drawable.ic_perm_device_info);
-            }
-        }
-    }
-
-    /** @hide */
-    private static class MyPermissionInfo extends PermissionInfo {
-        CharSequence mLabel;
-
-        /**
-         * PackageInfo.requestedPermissionsFlags for the new package being installed.
-         */
-        int mNewReqFlags;
-
-        /**
-         * PackageInfo.requestedPermissionsFlags for the currently installed
-         * package, if it is installed.
-         */
-        int mExistingReqFlags;
-
-        /**
-         * True if this should be considered a new permission.
-         */
-        boolean mNew;
-
-        MyPermissionInfo(PermissionInfo info) {
-            super(info);
-        }
-    }
-
-    /** @hide */
-    public static class PermissionItemView extends LinearLayout implements View.OnClickListener {
-        MyPermissionGroupInfo mGroup;
-        MyPermissionInfo mPerm;
-        AlertDialog mDialog;
-        private boolean mShowRevokeUI = false;
-        private String mPackageName;
-
-        public PermissionItemView(Context context, AttributeSet attrs) {
-            super(context, attrs);
-            setClickable(true);
-        }
-
-        public void setPermission(MyPermissionGroupInfo grp, MyPermissionInfo perm,
-                boolean first, CharSequence newPermPrefix, String packageName,
-                boolean showRevokeUI) {
-            mGroup = grp;
-            mPerm = perm;
-            mShowRevokeUI = showRevokeUI;
-            mPackageName = packageName;
-
-            ImageView permGrpIcon = findViewById(R.id.perm_icon);
-            TextView permNameView = findViewById(R.id.perm_name);
-
-            PackageManager pm = getContext().getPackageManager();
-            Drawable icon = null;
-            if (first) {
-                icon = grp.loadGroupIcon(getContext(), pm);
-            }
-            CharSequence label = perm.mLabel;
-            if (perm.mNew && newPermPrefix != null) {
-                // If this is a new permission, format it appropriately.
-                SpannableStringBuilder builder = new SpannableStringBuilder();
-                Parcel parcel = Parcel.obtain();
-                TextUtils.writeToParcel(newPermPrefix, parcel, 0);
-                parcel.setDataPosition(0);
-                CharSequence newStr = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
-                parcel.recycle();
-                builder.append(newStr);
-                builder.append(label);
-                label = builder;
-            }
-
-            permGrpIcon.setImageDrawable(icon);
-            permNameView.setText(label);
-            setOnClickListener(this);
-            if (localLOGV) Log.i(TAG, "Made perm item " + perm.name
-                    + ": " + label + " in group " + grp.name);
-        }
-
-        @Override
-        public void onClick(View v) {
-            if (mGroup != null && mPerm != null) {
-                if (mDialog != null) {
-                    mDialog.dismiss();
-                }
-                PackageManager pm = getContext().getPackageManager();
-                AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
-                builder.setTitle(mGroup.mLabel);
-                if (mPerm.descriptionRes != 0) {
-                    builder.setMessage(mPerm.loadDescription(pm));
-                } else {
-                    CharSequence appName;
-                    try {
-                        ApplicationInfo app = pm.getApplicationInfo(mPerm.packageName, 0);
-                        appName = app.loadLabel(pm);
-                    } catch (NameNotFoundException e) {
-                        appName = mPerm.packageName;
-                    }
-                    StringBuilder sbuilder = new StringBuilder(128);
-                    sbuilder.append(getContext().getString(
-                            R.string.perms_description_app, appName));
-                    sbuilder.append("\n\n");
-                    sbuilder.append(mPerm.name);
-                    builder.setMessage(sbuilder.toString());
-                }
-                builder.setCancelable(true);
-                builder.setIcon(mGroup.loadGroupIcon(getContext(), pm));
-                addRevokeUIIfNecessary(builder);
-                mDialog = builder.show();
-                mDialog.setCanceledOnTouchOutside(true);
-            }
-        }
-
-        @Override
-        protected void onDetachedFromWindow() {
-            super.onDetachedFromWindow();
-            if (mDialog != null) {
-                mDialog.dismiss();
-            }
-        }
-
-        private void addRevokeUIIfNecessary(AlertDialog.Builder builder) {
-            if (!mShowRevokeUI) {
-                return;
-            }
-
-            final boolean isRequired =
-                    ((mPerm.mExistingReqFlags & PackageInfo.REQUESTED_PERMISSION_REQUIRED) != 0);
-
-            if (isRequired) {
-                return;
-            }
-
-            DialogInterface.OnClickListener ocl = new DialogInterface.OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                    PackageManager pm = getContext().getPackageManager();
-                    pm.revokeRuntimePermission(mPackageName, mPerm.name,
-                            new UserHandle(mContext.getUserId()));
-                    PermissionItemView.this.setVisibility(View.GONE);
-                }
-            };
-            builder.setNegativeButton(R.string.revoke, ocl);
-            builder.setPositiveButton(R.string.ok, null);
-        }
-    }
-
-    private AppSecurityPermissions(Context context) {
-        mContext = context;
-        mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        mPm = mContext.getPackageManager();
-        // Pick up from framework resources instead.
-        mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix);
-    }
-
-    @UnsupportedAppUsage
-    public AppSecurityPermissions(Context context, String packageName) {
-        this(context);
-        mPackageName = packageName;
-        Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
-        PackageInfo pkgInfo;
-        try {
-            pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
-        } catch (NameNotFoundException e) {
-            Log.w(TAG, "Couldn't retrieve permissions for package:"+packageName);
-            return;
-        }
-        // Extract all user permissions
-        if((pkgInfo.applicationInfo != null) && (pkgInfo.applicationInfo.uid != -1)) {
-            getAllUsedPermissions(pkgInfo.applicationInfo.uid, permSet);
-        }
-        mPermsList.addAll(permSet);
-        setPermissions(mPermsList);
-    }
-
-    public AppSecurityPermissions(Context context, PackageInfo info) {
-        this(context);
-        Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
-        if(info == null) {
-            return;
-        }
-        mPackageName = info.packageName;
-
-        // Convert to a PackageInfo
-        PackageInfo installedPkgInfo = null;
-        // Get requested permissions
-        if (info.requestedPermissions != null) {
-            try {
-                installedPkgInfo = mPm.getPackageInfo(info.packageName,
-                        PackageManager.GET_PERMISSIONS);
-            } catch (NameNotFoundException e) {
-            }
-            extractPerms(info, permSet, installedPkgInfo);
-        }
-        // Get permissions related to shared user if any
-        if (info.sharedUserId != null) {
-            int sharedUid;
-            try {
-                sharedUid = mPm.getUidForSharedUser(info.sharedUserId);
-                getAllUsedPermissions(sharedUid, permSet);
-            } catch (NameNotFoundException e) {
-                Log.w(TAG, "Couldn't retrieve shared user id for: " + info.packageName);
-            }
-        }
-        // Retrieve list of permissions
-        mPermsList.addAll(permSet);
-        setPermissions(mPermsList);
-    }
-
     /**
      * Utility to retrieve a view displaying a single permission.  This provides
      * the old UI layout for permissions; it is only here for the device admin
@@ -327,197 +47,6 @@
                 description, dangerous, icon);
     }
 
-    private void getAllUsedPermissions(int sharedUid, Set<MyPermissionInfo> permSet) {
-        String sharedPkgList[] = mPm.getPackagesForUid(sharedUid);
-        if(sharedPkgList == null || (sharedPkgList.length == 0)) {
-            return;
-        }
-        for(String sharedPkg : sharedPkgList) {
-            getPermissionsForPackage(sharedPkg, permSet);
-        }
-    }
-
-    private void getPermissionsForPackage(String packageName, Set<MyPermissionInfo> permSet) {
-        try {
-            PackageInfo pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
-            extractPerms(pkgInfo, permSet, pkgInfo);
-        } catch (NameNotFoundException e) {
-            Log.w(TAG, "Couldn't retrieve permissions for package: " + packageName);
-        }
-    }
-
-    private void extractPerms(PackageInfo info, Set<MyPermissionInfo> permSet,
-            PackageInfo installedPkgInfo) {
-        String[] strList = info.requestedPermissions;
-        int[] flagsList = info.requestedPermissionsFlags;
-        if ((strList == null) || (strList.length == 0)) {
-            return;
-        }
-        for (int i=0; i<strList.length; i++) {
-            String permName = strList[i];
-            try {
-                PermissionInfo tmpPermInfo = mPm.getPermissionInfo(permName, 0);
-                if (tmpPermInfo == null) {
-                    continue;
-                }
-                int existingIndex = -1;
-                if (installedPkgInfo != null
-                        && installedPkgInfo.requestedPermissions != null) {
-                    for (int j=0; j<installedPkgInfo.requestedPermissions.length; j++) {
-                        if (permName.equals(installedPkgInfo.requestedPermissions[j])) {
-                            existingIndex = j;
-                            break;
-                        }
-                    }
-                }
-                final int existingFlags = existingIndex >= 0 ?
-                        installedPkgInfo.requestedPermissionsFlags[existingIndex] : 0;
-                if (!isDisplayablePermission(tmpPermInfo, flagsList[i], existingFlags)) {
-                    // This is not a permission that is interesting for the user
-                    // to see, so skip it.
-                    continue;
-                }
-                final String origGroupName = tmpPermInfo.group;
-                String groupName = origGroupName;
-                if (groupName == null) {
-                    groupName = tmpPermInfo.packageName;
-                    tmpPermInfo.group = groupName;
-                }
-                MyPermissionGroupInfo group = mPermGroups.get(groupName);
-                if (group == null) {
-                    PermissionGroupInfo grp = null;
-                    if (origGroupName != null) {
-                        grp = mPm.getPermissionGroupInfo(origGroupName, 0);
-                    }
-                    if (grp != null) {
-                        group = new MyPermissionGroupInfo(grp);
-                    } else {
-                        // We could be here either because the permission
-                        // didn't originally specify a group or the group it
-                        // gave couldn't be found.  In either case, we consider
-                        // its group to be the permission's package name.
-                        tmpPermInfo.group = tmpPermInfo.packageName;
-                        group = mPermGroups.get(tmpPermInfo.group);
-                        if (group == null) {
-                            group = new MyPermissionGroupInfo(tmpPermInfo);
-                        }
-                        group = new MyPermissionGroupInfo(tmpPermInfo);
-                    }
-                    mPermGroups.put(tmpPermInfo.group, group);
-                }
-                final boolean newPerm = installedPkgInfo != null
-                        && (existingFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0;
-                MyPermissionInfo myPerm = new MyPermissionInfo(tmpPermInfo);
-                myPerm.mNewReqFlags = flagsList[i];
-                myPerm.mExistingReqFlags = existingFlags;
-                // This is a new permission if the app is already installed and
-                // doesn't currently hold this permission.
-                myPerm.mNew = newPerm;
-                permSet.add(myPerm);
-            } catch (NameNotFoundException e) {
-                Log.i(TAG, "Ignoring unknown permission:"+permName);
-            }
-        }
-    }
-
-    @UnsupportedAppUsage
-    public int getPermissionCount() {
-        return getPermissionCount(WHICH_ALL);
-    }
-
-    private List<MyPermissionInfo> getPermissionList(MyPermissionGroupInfo grp, int which) {
-        if (which == WHICH_NEW) {
-            return grp.mNewPermissions;
-        } else {
-            return grp.mAllPermissions;
-        }
-    }
-
-    public int getPermissionCount(int which) {
-        int N = 0;
-        for (int i=0; i<mPermGroupsList.size(); i++) {
-            N += getPermissionList(mPermGroupsList.get(i), which).size();
-        }
-        return N;
-    }
-
-    @UnsupportedAppUsage
-    public View getPermissionsView() {
-        return getPermissionsView(WHICH_ALL, false);
-    }
-
-    public View getPermissionsViewWithRevokeButtons() {
-        return getPermissionsView(WHICH_ALL, true);
-    }
-
-    public View getPermissionsView(int which) {
-        return getPermissionsView(which, false);
-    }
-
-    private View getPermissionsView(int which, boolean showRevokeUI) {
-        LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
-        LinearLayout displayList = permsView.findViewById(R.id.perms_list);
-        View noPermsView = permsView.findViewById(R.id.no_permissions);
-
-        displayPermissions(mPermGroupsList, displayList, which, showRevokeUI);
-        if (displayList.getChildCount() <= 0) {
-            noPermsView.setVisibility(View.VISIBLE);
-        }
-
-        return permsView;
-    }
-
-    /**
-     * Utility method that displays permissions from a map containing group name and
-     * list of permission descriptions.
-     */
-    private void displayPermissions(List<MyPermissionGroupInfo> groups,
-            LinearLayout permListView, int which, boolean showRevokeUI) {
-        permListView.removeAllViews();
-
-        int spacing = (int)(8*mContext.getResources().getDisplayMetrics().density);
-
-        for (int i=0; i<groups.size(); i++) {
-            MyPermissionGroupInfo grp = groups.get(i);
-            final List<MyPermissionInfo> perms = getPermissionList(grp, which);
-            for (int j=0; j<perms.size(); j++) {
-                MyPermissionInfo perm = perms.get(j);
-                View view = getPermissionItemView(grp, perm, j == 0,
-                        which != WHICH_NEW ? mNewPermPrefix : null, showRevokeUI);
-                LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT,
-                        ViewGroup.LayoutParams.WRAP_CONTENT);
-                if (j == 0) {
-                    lp.topMargin = spacing;
-                }
-                if (j == grp.mAllPermissions.size()-1) {
-                    lp.bottomMargin = spacing;
-                }
-                if (permListView.getChildCount() == 0) {
-                    lp.topMargin *= 2;
-                }
-                permListView.addView(view, lp);
-            }
-        }
-    }
-
-    private PermissionItemView getPermissionItemView(MyPermissionGroupInfo grp,
-            MyPermissionInfo perm, boolean first, CharSequence newPermPrefix, boolean showRevokeUI) {
-        return getPermissionItemView(mContext, mInflater, grp, perm, first, newPermPrefix,
-                mPackageName, showRevokeUI);
-    }
-
-    private static PermissionItemView getPermissionItemView(Context context, LayoutInflater inflater,
-            MyPermissionGroupInfo grp, MyPermissionInfo perm, boolean first,
-            CharSequence newPermPrefix, String packageName, boolean showRevokeUI) {
-            PermissionItemView permView = (PermissionItemView)inflater.inflate(
-                (perm.flags & PermissionInfo.FLAG_COSTS_MONEY) != 0
-                        ? R.layout.app_permission_item_money : R.layout.app_permission_item,
-                null);
-        permView.setPermission(grp, perm, first, newPermPrefix, packageName, showRevokeUI);
-        return permView;
-    }
-
     private static View getPermissionItemViewOld(Context context, LayoutInflater inflater,
             CharSequence grpName, CharSequence permList, boolean dangerous, Drawable icon) {
         View permView = inflater.inflate(R.layout.app_permission_item_old, null);
@@ -536,116 +65,4 @@
         }
         return permView;
     }
-
-    private boolean isDisplayablePermission(PermissionInfo pInfo, int newReqFlags,
-            int existingReqFlags) {
-        final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
-        final boolean isNormal = (base == PermissionInfo.PROTECTION_NORMAL);
-
-        // We do not show normal permissions in the UI.
-        if (isNormal) {
-            return false;
-        }
-
-        final boolean isDangerous = (base == PermissionInfo.PROTECTION_DANGEROUS)
-                || ((pInfo.protectionLevel&PermissionInfo.PROTECTION_FLAG_PRE23) != 0);
-        final boolean isRequired =
-                ((newReqFlags&PackageInfo.REQUESTED_PERMISSION_REQUIRED) != 0);
-        final boolean isDevelopment =
-                ((pInfo.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0);
-        final boolean wasGranted =
-                ((existingReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0);
-        final boolean isGranted =
-                ((newReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0);
-
-        // Dangerous and normal permissions are always shown to the user if the permission
-        // is required, or it was previously granted
-        if (isDangerous && (isRequired || wasGranted || isGranted)) {
-            return true;
-        }
-
-        // Development permissions are only shown to the user if they are already
-        // granted to the app -- if we are installing an app and they are not
-        // already granted, they will not be granted as part of the install.
-        if (isDevelopment && wasGranted) {
-            if (localLOGV) Log.i(TAG, "Special perm " + pInfo.name
-                    + ": protlevel=0x" + Integer.toHexString(pInfo.protectionLevel));
-            return true;
-        }
-        return false;
-    }
-
-    private static class PermissionGroupInfoComparator implements Comparator<MyPermissionGroupInfo> {
-        private final Collator sCollator = Collator.getInstance();
-        @Override
-        public final int compare(MyPermissionGroupInfo a, MyPermissionGroupInfo b) {
-            return sCollator.compare(a.mLabel, b.mLabel);
-        }
-    }
-
-    private static class PermissionInfoComparator implements Comparator<MyPermissionInfo> {
-        private final Collator sCollator = Collator.getInstance();
-        PermissionInfoComparator() {
-        }
-        public final int compare(MyPermissionInfo a, MyPermissionInfo b) {
-            return sCollator.compare(a.mLabel, b.mLabel);
-        }
-    }
-
-    private void addPermToList(List<MyPermissionInfo> permList,
-            MyPermissionInfo pInfo) {
-        if (pInfo.mLabel == null) {
-            pInfo.mLabel = pInfo.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
-                    | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
-        }
-        int idx = Collections.binarySearch(permList, pInfo, mPermComparator);
-        if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+permList.size());
-        if (idx < 0) {
-            idx = -idx-1;
-            permList.add(idx, pInfo);
-        }
-    }
-
-    private void setPermissions(List<MyPermissionInfo> permList) {
-        if (permList != null) {
-            // First pass to group permissions
-            for (MyPermissionInfo pInfo : permList) {
-                if(localLOGV) Log.i(TAG, "Processing permission:"+pInfo.name);
-                if(!isDisplayablePermission(pInfo, pInfo.mNewReqFlags, pInfo.mExistingReqFlags)) {
-                    if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable");
-                    continue;
-                }
-                MyPermissionGroupInfo group = mPermGroups.get(pInfo.group);
-                if (group != null) {
-                    pInfo.mLabel = pInfo.loadSafeLabel(mPm, 20000,
-                            PackageItemInfo.SAFE_LABEL_FLAG_TRIM
-                            | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
-                    addPermToList(group.mAllPermissions, pInfo);
-                    if (pInfo.mNew) {
-                        addPermToList(group.mNewPermissions, pInfo);
-                    }
-                }
-            }
-        }
-
-        for (MyPermissionGroupInfo pgrp : mPermGroups.values()) {
-            if (pgrp.labelRes != 0 || pgrp.nonLocalizedLabel != null) {
-                pgrp.mLabel = pgrp.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
-                        | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
-            } else {
-                ApplicationInfo app;
-                try {
-                    app = mPm.getApplicationInfo(pgrp.packageName, 0);
-                    pgrp.mLabel = app.loadSafeLabel(mPm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM
-                            | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
-                } catch (NameNotFoundException e) {
-                    pgrp.mLabel = pgrp.loadSafeLabel(mPm, 20000,
-                            PackageItemInfo.SAFE_LABEL_FLAG_TRIM
-                            | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
-                }
-            }
-            mPermGroupsList.add(pgrp);
-        }
-        Collections.sort(mPermGroupsList, mPermGroupComparator);
-    }
 }
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 0baf73c..02c9542 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -130,6 +130,10 @@
     public double wakeLockPowerMah;
     public double wifiPowerMah;
 
+    //                           ****************
+    // This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto)
+    // so the ordinal values (and therefore the order) must never change.
+    //                           ****************
     public enum DrainType {
         AMBIENT_DISPLAY,
         @UnsupportedAppUsage
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 8f87f91..8bdb000 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -173,12 +173,13 @@
     }
 
     native private static void nativePreloadAppProcessHALs();
+    native private static void nativePreloadOpenGL();
 
     private static void preloadOpenGL() {
         String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
         if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false) &&
                 (driverPackageName == null || driverPackageName.isEmpty())) {
-            EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+            nativePreloadOpenGL();
         }
     }
 
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 8ace8da..5887fa7 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -135,13 +135,13 @@
         LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
 
 int register_android_server_InputApplicationHandle(JNIEnv* env) {
-    int res = jniRegisterNativeMethods(env, "com/android/server/input/InputApplicationHandle",
+    int res = jniRegisterNativeMethods(env, "android/view/InputApplicationHandle",
             gInputApplicationHandleMethods, NELEM(gInputApplicationHandleMethods));
     (void) res;  // Faked use when LOG_NDEBUG.
     LOG_FATAL_IF(res < 0, "Unable to register native methods.");
 
     jclass clazz;
-    FIND_CLASS(clazz, "com/android/server/input/InputApplicationHandle");
+    FIND_CLASS(clazz, "android/view/InputApplicationHandle");
 
     GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, clazz,
             "ptr", "J");
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 5b72241..6ecb5de 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -221,20 +221,20 @@
         LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
 
 int register_android_server_InputWindowHandle(JNIEnv* env) {
-    int res = jniRegisterNativeMethods(env, "com/android/server/input/InputWindowHandle",
+    int res = jniRegisterNativeMethods(env, "android/view/InputWindowHandle",
             gInputWindowHandleMethods, NELEM(gInputWindowHandleMethods));
     (void) res;  // Faked use when LOG_NDEBUG.
     LOG_FATAL_IF(res < 0, "Unable to register native methods.");
 
     jclass clazz;
-    FIND_CLASS(clazz, "com/android/server/input/InputWindowHandle");
+    FIND_CLASS(clazz, "android/view/InputWindowHandle");
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, clazz,
             "ptr", "J");
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle,
             clazz,
-            "inputApplicationHandle", "Lcom/android/server/input/InputApplicationHandle;");
+            "inputApplicationHandle", "Landroid/view/InputApplicationHandle;");
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.inputChannel, clazz,
             "inputChannel", "Landroid/view/InputChannel;");
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index b2d44e7..7b564ae 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -24,9 +24,13 @@
 #include <sys/system_properties.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #include <private/android_filesystem_config.h> // for AID_SYSTEM
 
+#include <sstream>
+#include <string>
+
 #include "android-base/logging.h"
 #include "android-base/properties.h"
 #include "android-base/stringprintf.h"
@@ -38,6 +42,7 @@
 #include "androidfw/AssetManager2.h"
 #include "androidfw/AttributeResolution.h"
 #include "androidfw/MutexGuard.h"
+#include "androidfw/PosixUtils.h"
 #include "androidfw/ResourceTypes.h"
 #include "core_jni_helpers.h"
 #include "jni.h"
@@ -54,6 +59,7 @@
 extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
 
 using ::android::base::StringPrintf;
+using ::android::util::ExecuteBinary;
 
 namespace android {
 
@@ -161,18 +167,20 @@
       argv[argc++] = AssetManager::IDMAP_DIR;
 
       // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
-      // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
+      // use VENDOR_OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in
+      // addition to VENDOR_OVERLAY_DIR.
       std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY,
                                                          "");
       if (!overlay_theme_path.empty()) {
-        overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path;
+        overlay_theme_path =
+          std::string(AssetManager::VENDOR_OVERLAY_DIR) + "/" + overlay_theme_path;
         if (stat(overlay_theme_path.c_str(), &st) == 0) {
           argv[argc++] = overlay_theme_path.c_str();
         }
       }
 
-      if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
-        argv[argc++] = AssetManager::OVERLAY_DIR;
+      if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
+        argv[argc++] = AssetManager::VENDOR_OVERLAY_DIR;
       }
 
       if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
@@ -200,6 +208,75 @@
   }
 }
 
+static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* env,
+                                                                        jclass /*clazz*/) {
+  // --input-directory can be given multiple times, but idmap2 expects the directory to exist
+  std::vector<std::string> input_dirs;
+  struct stat st;
+  if (stat(AssetManager::VENDOR_OVERLAY_DIR, &st) == 0) {
+    input_dirs.push_back(AssetManager::VENDOR_OVERLAY_DIR);
+  }
+
+  if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
+    input_dirs.push_back(AssetManager::PRODUCT_OVERLAY_DIR);
+  }
+
+  if (stat(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR, &st) == 0) {
+    input_dirs.push_back(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR);
+  }
+
+  if (input_dirs.empty()) {
+    LOG(WARNING) << "no directories for idmap2 to scan";
+    return env->NewObjectArray(0, g_stringClass, nullptr);
+  }
+
+  std::vector<std::string> argv{"/system/bin/idmap2",
+    "scan",
+    "--recursive",
+    "--target-package-name", "android",
+    "--target-apk-path", "/system/framework/framework-res.apk",
+    "--output-directory", "/data/resource-cache"};
+
+  for (const auto& dir : input_dirs) {
+    argv.push_back("--input-directory");
+    argv.push_back(dir);
+  }
+
+  const auto result = ExecuteBinary(argv);
+
+  if (!result) {
+      LOG(ERROR) << "failed to execute idmap2";
+      return nullptr;
+  }
+
+  if (result->status != 0) {
+    LOG(ERROR) << "idmap2: " << result->stderr;
+    return nullptr;
+  }
+
+  std::vector<std::string> idmap_paths;
+  std::istringstream input(result->stdout);
+  std::string path;
+  while (std::getline(input, path)) {
+    idmap_paths.push_back(path);
+  }
+
+  jobjectArray array = env->NewObjectArray(idmap_paths.size(), g_stringClass, nullptr);
+  if (array == nullptr) {
+    return nullptr;
+  }
+  for (size_t i = 0; i < idmap_paths.size(); i++) {
+    const std::string path = idmap_paths[i];
+    jstring java_string = env->NewStringUTF(path.c_str());
+    if (env->ExceptionCheck()) {
+      return nullptr;
+    }
+    env->SetObjectArrayElement(array, i, java_string);
+    env->DeleteLocalRef(java_string);
+  }
+  return array;
+}
+
 static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
                       uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
   env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
@@ -1405,6 +1482,8 @@
 
     // System/idmap related methods.
     {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps},
+    {"nativeCreateIdmapsForStaticOverlaysTargetingAndroid", "()[Ljava/lang/String;",
+     (void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid},
 
     // Global management/debug methods.
     {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 2f17907..fb6be6b 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -249,6 +249,24 @@
     }
 }
 
+static jobject android_view_InputChannel_nativeGetToken(JNIEnv* env, jobject obj) {
+    NativeInputChannel* nativeInputChannel =
+        android_view_InputChannel_getNativeInputChannel(env, obj);
+    if (nativeInputChannel) {
+        return javaObjectForIBinder(env, nativeInputChannel->getInputChannel()->getToken());
+    }
+    return 0;
+}
+
+static void android_view_InputChannel_nativeSetToken(JNIEnv* env, jobject obj, jobject tokenObj) {
+    NativeInputChannel* nativeInputChannel =
+        android_view_InputChannel_getNativeInputChannel(env, obj);
+    sp<IBinder> token = ibinderForJavaObject(env, tokenObj);
+    if (nativeInputChannel != nullptr) {
+        nativeInputChannel->getInputChannel()->setToken(token);
+    }
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gInputChannelMethods[] = {
@@ -267,6 +285,10 @@
             (void*)android_view_InputChannel_nativeGetName },
     { "nativeDup", "(Landroid/view/InputChannel;)V",
             (void*)android_view_InputChannel_nativeDup },
+    { "nativeGetToken", "()Landroid/os/IBinder;",
+            (void*)android_view_InputChannel_nativeGetToken },
+    { "nativeSetToken", "(Landroid/os/IBinder;)V",
+            (void*)android_view_InputChannel_nativeSetToken }
 };
 
 int register_android_view_InputChannel(JNIEnv* env) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 4eda3ab..ec9c860 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -19,6 +19,7 @@
 
 #include "android_os_Parcel.h"
 #include "android_util_Binder.h"
+#include "android_hardware_input_InputWindowHandle.h"
 #include "android/graphics/Bitmap.h"
 #include "android/graphics/GraphicsJNI.h"
 #include "android/graphics/Region.h"
@@ -324,6 +325,18 @@
     transaction->setAlpha(ctrl, alpha);
 }
 
+static void nativeSetInputWindowInfo(JNIEnv* env, jclass clazz, jlong transactionObj,
+        jlong nativeObject, jobject inputWindow) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+    sp<NativeInputWindowHandle> handle = android_server_InputWindowHandle_getHandle(
+            env, inputWindow);
+    handle->updateInfo();
+
+    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+    transaction->setInputWindowInfo(ctrl, *handle->getInfo());
+}
+
 static void nativeSetColor(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject, jfloatArray fColor) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -930,6 +943,8 @@
             (void*)nativeScreenshot },
     {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;",
             (void*)nativeCaptureLayers },
+    {"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
+     (void*)nativeSetInputWindowInfo },
 };
 
 int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp
index 258a55c..ac0e600 100644
--- a/core/jni/com_android_internal_os_ZygoteInit.cpp
+++ b/core/jni/com_android_internal_os_ZygoteInit.cpp
@@ -16,21 +16,58 @@
 
 #define LOG_TAG "Zygote"
 
+#include <EGL/egl.h>
 #include <ui/GraphicBufferMapper.h>
 
 #include "core_jni_helpers.h"
 
 namespace {
 
+// Shadow call stack (SCS) is a security mitigation that uses a separate stack
+// (the SCS) for return addresses. In versions of Android newer than P, the
+// compiler cooperates with the system to ensure that the SCS address is always
+// stored in register x18, as long as the app was compiled with a new enough
+// compiler and does not use features that rely on SP-HALs (this restriction is
+// because the SP-HALs might not preserve x18 due to potentially having been
+// compiled with an old compiler as a consequence of Treble; it generally means
+// that the app must be a system app without a UI). This struct is used to
+// temporarily store the address on the stack while preloading the SP-HALs, so
+// that such apps can use the same zygote as everything else.
+struct ScopedSCSExit {
+#ifdef __aarch64__
+    void* scs;
+
+    ScopedSCSExit() {
+        __asm__ __volatile__("str x18, [%0]" ::"r"(&scs));
+    }
+
+    ~ScopedSCSExit() {
+        __asm__ __volatile__("ldr x18, [%0]; str xzr, [%0]" ::"r"(&scs));
+    }
+#else
+    // Silence unused variable warnings in non-SCS builds.
+    ScopedSCSExit() {}
+    ~ScopedSCSExit() {}
+#endif
+};
+
 void android_internal_os_ZygoteInit_nativePreloadAppProcessHALs(JNIEnv* env, jclass) {
+    ScopedSCSExit x;
     android::GraphicBufferMapper::preloadHal();
     // Add preloading here for other HALs that are (a) always passthrough, and
     // (b) loaded by most app processes.
 }
 
+void android_internal_os_ZygoteInit_nativePreloadOpenGL(JNIEnv* env, jclass) {
+    ScopedSCSExit x;
+    eglGetDisplay(EGL_DEFAULT_DISPLAY);
+}
+
 const JNINativeMethod gMethods[] = {
     { "nativePreloadAppProcessHALs", "()V",
       (void*)android_internal_os_ZygoteInit_nativePreloadAppProcessHALs },
+    { "nativePreloadOpenGL", "()V",
+      (void*)android_internal_os_ZygoteInit_nativePreloadOpenGL },
 };
 
 }  // anonymous namespace
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 2465759..a398e49 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -85,7 +85,7 @@
   // See AssetManager.cpp for more details on overlay-subdir.
   static const char* kOverlayDir = "/system/vendor/overlay/";
   static const char* kVendorOverlayDir = "/vendor/overlay";
-  static const char* kOverlaySubdir = "/system/vendor/overlay-subdir/";
+  static const char* kVendorOverlaySubdir = "/system/vendor/overlay-subdir/";
   static const char* kSystemProductOverlayDir = "/system/product/overlay/";
   static const char* kProductOverlayDir = "/product/overlay";
   static const char* kSystemProductServicesOverlayDir = "/system/product_services/overlay/";
@@ -93,7 +93,7 @@
   static const char* kApkSuffix = ".apk";
 
   if ((android::base::StartsWith(path, kOverlayDir)
-       || android::base::StartsWith(path, kOverlaySubdir)
+       || android::base::StartsWith(path, kVendorOverlaySubdir)
        || android::base::StartsWith(path, kVendorOverlayDir)
        || android::base::StartsWith(path, kSystemProductOverlayDir)
        || android::base::StartsWith(path, kProductOverlayDir)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2976879..62896be 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -746,7 +746,7 @@
         android:description="@string/permdesc_receiveMms"
         android:protectionLevel="dangerous" />
 
-    <!-- @TestApi Allows an application to read previously received cell broadcast
+    <!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast
          messages and to register a content observer to get notifications when
          a cell broadcast has been received and added to the database. For
          emergency alerts, the database is updated immediately after the
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/config.xml b/core/res/res/values/config.xml
index 26f3370..829d6f5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -55,6 +55,8 @@
         <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_microphone</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
     </string-array>
 
@@ -87,6 +89,8 @@
     <string translatable="false" name="status_bar_mobile">mobile</string>
     <string translatable="false" name="status_bar_vpn">vpn</string>
     <string translatable="false" name="status_bar_ethernet">ethernet</string>
+    <string translatable="false" name="status_bar_microphone">microphone</string>
+    <string translatable="false" name="status_bar_camera">camera</string>
     <string translatable="false" name="status_bar_airplane">airplane</string>
 
     <!-- Flag indicating whether the surface flinger has limited
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/symbols.xml b/core/res/res/values/symbols.xml
index 4eb723e..626206b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2808,6 +2808,8 @@
   <java-symbol type="string" name="status_bar_mobile" />
   <java-symbol type="string" name="status_bar_ethernet" />
   <java-symbol type="string" name="status_bar_vpn" />
+  <java-symbol type="string" name="status_bar_microphone" />
+  <java-symbol type="string" name="status_bar_camera" />
 
   <!-- Locale picker -->
   <java-symbol type="id" name="locale_search_menu" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 8c91c37..002b6a8 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -494,6 +494,7 @@
                     Settings.Global.WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED,
                     Settings.Global.WIFI_LINK_SPEED_METRICS_ENABLED,
                     Settings.Global.WIFI_PNO_FREQUENCY_CULLING_ENABLED,
+                    Settings.Global.WIFI_PNO_RECENCY_SORTING_ENABLED,
                     Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
                     Settings.Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
                     Settings.Global.WIFI_NETWORK_SHOW_RSSI,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
index e81f678..7467114 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
@@ -41,6 +41,7 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.text.format.DateUtils;
+import android.util.StatsLog;
 
 import junit.framework.TestCase;
 
@@ -258,6 +259,36 @@
         assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS);
     }
 
+    @Test
+    public void testDrainTypesSyncedWithProto() {
+        assertEquals(BatterySipper.DrainType.AMBIENT_DISPLAY.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__AMBIENT_DISPLAY);
+        // AtomsProto has no "APP"
+        assertEquals(BatterySipper.DrainType.BLUETOOTH.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__BLUETOOTH);
+        assertEquals(BatterySipper.DrainType.CAMERA.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CAMERA);
+        assertEquals(BatterySipper.DrainType.CELL.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CELL);
+        assertEquals(BatterySipper.DrainType.FLASHLIGHT.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__FLASHLIGHT);
+        assertEquals(BatterySipper.DrainType.IDLE.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__IDLE);
+        assertEquals(BatterySipper.DrainType.MEMORY.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__MEMORY);
+        assertEquals(BatterySipper.DrainType.OVERCOUNTED.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__OVERCOUNTED);
+        assertEquals(BatterySipper.DrainType.PHONE.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__PHONE);
+        assertEquals(BatterySipper.DrainType.SCREEN.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__SCREEN);
+        assertEquals(BatterySipper.DrainType.UNACCOUNTED.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__UNACCOUNTED);
+        // AtomsProto has no "USER"
+        assertEquals(BatterySipper.DrainType.WIFI.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__WIFI);
+    }
+
     private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah,
             int uidCode, boolean isUidNull) {
         final BatterySipper sipper = mock(BatterySipper.class);
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 68f24fb..a4c5ed2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -173,6 +173,7 @@
     <assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" />
     <assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" />
 
+    <assign-permission name="android.permission.BATTERY_STATS" uid="statsd" />
     <assign-permission name="android.permission.DUMP" uid="statsd" />
     <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="statsd" />
     <assign-permission name="android.permission.STATSCOMPANION" uid="statsd" />
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index d3c8782..835b735 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -54,6 +54,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.security.InvalidKeyException;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.CompletableFuture;
@@ -307,6 +308,31 @@
         }
     }
 
+    /**
+     * List uids of all keys that are auth bound to the current user. 
+     * Only system is allowed to call this method.
+     */
+    @UnsupportedAppUsage
+    public int[] listUidsOfAuthBoundKeys() {
+        final int MAX_RESULT_SIZE = 100;
+        int[] uidsOut = new int[MAX_RESULT_SIZE];
+        try {
+            int rc = mBinder.listUidsOfAuthBoundKeys(uidsOut);
+            if (rc != NO_ERROR) {
+                Log.w(TAG, String.format("listUidsOfAuthBoundKeys failed with error code %d", rc));
+                return null;
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return null;
+        } catch (android.os.ServiceSpecificException e) {
+            Log.w(TAG, "KeyStore exception", e);
+            return null;
+        }
+        // Remove any 0 entries
+        return Arrays.stream(uidsOut).filter(x -> x > 0).toArray();
+   }
+
     public String[] list(String prefix) {
         return list(prefix, UID_SELF);
     }
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 843c146..1cb0d25 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -72,7 +72,7 @@
 
 const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
 const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
-const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
+const char* AssetManager::VENDOR_OVERLAY_DIR = "/vendor/overlay";
 const char* AssetManager::PRODUCT_OVERLAY_DIR = "/product/overlay";
 const char* AssetManager::PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay";
 const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme";
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index cdb87bc..e22e2d2 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -59,13 +59,13 @@
 public:
     static const char* RESOURCES_FILENAME;
     static const char* IDMAP_BIN;
-    static const char* OVERLAY_DIR;
+    static const char* VENDOR_OVERLAY_DIR;
     static const char* PRODUCT_OVERLAY_DIR;
     static const char* PRODUCT_SERVICES_OVERLAY_DIR;
     /*
      * If OVERLAY_THEME_DIR_PROPERTY is set, search for runtime resource overlay
-     * APKs in OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to
-     * OVERLAY_DIR.
+     * APKs in VENDOR_OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in
+     * addition to VENDOR_OVERLAY_DIR.
      */
     static const char* OVERLAY_THEME_DIR_PROPERTY;
     static const char* TARGET_PACKAGE_NAME;
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index a1a6d9f..04c4629 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -85,9 +85,6 @@
       case StatsLogValue::FLOAT:
         mElements.push_back(StatsLogValue(in->readFloat()));
         break;
-      case StatsLogValue::DOUBLE:
-        mElements.push_back(StatsLogValue(in->readDouble()));
-        break;
       case StatsLogValue::STORAGE:
         mElements.push_back(StatsLogValue());
         mElements.back().setType(StatsLogValue::STORAGE);
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
index 1cc650b..823af65 100644
--- a/media/java/android/media/AudioPresentation.java
+++ b/media/java/android/media/AudioPresentation.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.TestApi;
 import android.icu.util.ULocale;
 
 import java.lang.annotation.Retention;
@@ -172,6 +171,10 @@
         return localeLabels;
     }
 
+    private Map<ULocale, String> getULabels() {
+        return mLabels;
+    }
+
     /**
      * @return the locale corresponding to audio presentation's ISO 639-1/639-2 language code.
      */
@@ -231,17 +234,24 @@
         AudioPresentation obj = (AudioPresentation) o;
         return mPresentationId == obj.getPresentationId()
                 && mProgramId == obj.getProgramId()
-                && mLanguage == obj.getULocale()
+                && mLanguage.equals(obj.getULocale())
                 && mMasteringIndication == obj.getMasteringIndication()
                 && mAudioDescriptionAvailable == obj.hasAudioDescription()
                 && mSpokenSubtitlesAvailable == obj.hasSpokenSubtitles()
                 && mDialogueEnhancementAvailable == obj.hasDialogueEnhancement()
-                && mLabels.equals(obj.getLabels());
+                && mLabels.equals(obj.getULabels());
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(mPresentationId);
+        return Objects.hash(mPresentationId,
+                mProgramId,
+                mLanguage.hashCode(),
+                mMasteringIndication,
+                mAudioDescriptionAvailable,
+                mSpokenSubtitlesAvailable,
+                mDialogueEnhancementAvailable,
+                mLabels.hashCode());
     }
 
     /**
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 24b7f36..cdbc7b44 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -418,9 +418,6 @@
 
         /**
          * Returns the status code for the key
-         * @return one of {@link #STATUS_USABLE}, {@link #STATUS_EXPIRED},
-         * {@link #STATUS_OUTPUT_NOT_ALLOWED}, {@link #STATUS_PENDING}
-         * or {@link #STATUS_INTERNAL_ERROR}.
          */
         @KeyStatusCode
         public int getStatusCode() { return mStatusCode; }
@@ -654,13 +651,7 @@
      * can be queried using {@link #getSecurityLevel}. A session
      * ID is returned.
      *
-     * @param level the new security level, one of
-     * {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO},
-     * {@link #SECURITY_LEVEL_SW_SECURE_DECODE},
-     * {@link #SECURITY_LEVEL_HW_SECURE_CRYPTO},
-     * {@link #SECURITY_LEVEL_HW_SECURE_DECODE} or
-     * {@link #SECURITY_LEVEL_HW_SECURE_ALL}.
-     *
+     * @param level the new security level
      * @throws NotProvisionedException if provisioning is needed
      * @throws ResourceBusyException if required resources are in use
      * @throws IllegalArgumentException if the requested security level is
@@ -790,9 +781,6 @@
 
         /**
          * Get the type of the request
-         * @return one of {@link #REQUEST_TYPE_INITIAL},
-         * {@link #REQUEST_TYPE_RENEWAL}, {@link #REQUEST_TYPE_RELEASE},
-         * {@link #REQUEST_TYPE_NONE} or {@link #REQUEST_TYPE_UPDATE}
          */
         @RequestType
         public int getRequestType() { return mRequestType; }
@@ -1051,8 +1039,7 @@
      * an inactive offline license are not usable for decryption.
      *
      * @param keySetId selects the offline license
-     * @return the offline license state, one of {@link #OFFLINE_LICENSE_USABLE},
-     * {@link #OFFLINE_LICENSE_INACTIVE} or {@link #OFFLINE_LICENSE_STATE_UNKNOWN}.
+     * @return the offline license state
      * @throws IllegalArgumentException if the keySetId does not refer to an
      * offline license.
      */
@@ -1191,9 +1178,7 @@
      * enforcing compliance with HDCP requirements. Trusted enforcement of
      * HDCP policies must be handled by the DRM system.
      * <p>
-     * @return one of {@link #HDCP_LEVEL_UNKNOWN}, {@link #HDCP_NONE},
-     * {@link #HDCP_V1}, {@link #HDCP_V2}, {@link #HDCP_V2_1}, {@link #HDCP_V2_2}
-     * or {@link #HDCP_NO_DIGITAL_OUTPUT}.
+     * @return the connected HDCP level
      */
     @HdcpLevel
     public native int getConnectedHdcpLevel();
@@ -1204,9 +1189,7 @@
      * that may be connected. If multiple HDCP-capable interfaces are present,
      * it indicates the highest of the maximum HDCP levels of all interfaces.
      * <p>
-     * @return one of {@link #HDCP_LEVEL_UNKNOWN}, {@link #HDCP_NONE},
-     * {@link #HDCP_V1}, {@link #HDCP_V2}, {@link #HDCP_V2_1}, {@link #HDCP_V2_2}
-     * or {@link #HDCP_NO_DIGITAL_OUTPUT}.
+     * @return the maximum supported HDCP level
      */
     @HdcpLevel
     public native int getMaxHdcpLevel();
@@ -1296,10 +1279,7 @@
      * time a session is opened using {@link #openSession}.
      * @param sessionId the session to query.
      * <p>
-     * @return one of {@link #SECURITY_LEVEL_UNKNOWN},
-     * {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO}, {@link #SECURITY_LEVEL_SW_SECURE_DECODE},
-     * {@link #SECURITY_LEVEL_HW_SECURE_CRYPTO}, {@link #SECURITY_LEVEL_HW_SECURE_DECODE} or
-     * {@link #SECURITY_LEVEL_HW_SECURE_ALL}.
+     * @return the security level of the session
      */
     @SecurityLevel
     public native int getSecurityLevel(@NonNull byte[] sessionId);
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/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index 9ecaa03..5ab6632 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -26,7 +26,8 @@
     <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
 
     <application android:label="@string/app_name"
-                 android:usesCleartextTraffic="true">
+                 android:usesCleartextTraffic="true"
+                 android:supportsRtl="true" >
         <activity
             android:name="com.android.captiveportallogin.CaptivePortalLoginActivity"
             android:label="@string/action_bar_label"
diff --git a/packages/CaptivePortalLogin/res/layout/ssl_error_msg.xml b/packages/CaptivePortalLogin/res/layout/ssl_error_msg.xml
new file mode 100644
index 0000000..d460041
--- /dev/null
+++ b/packages/CaptivePortalLogin/res/layout/ssl_error_msg.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/ssl_error_msg"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:textAppearance="?android:attr/textAppearanceSmall"
+    android:layout_marginStart="20dip"
+    android:layout_marginEnd="20dip"
+    android:gravity="center_vertical"
+    android:layout_marginBottom="4dip"
+    android:layout_marginTop="4dip" />
+
diff --git a/packages/CaptivePortalLogin/res/layout/ssl_warning.xml b/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
new file mode 100644
index 0000000..ffd57a4
--- /dev/null
+++ b/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
@@ -0,0 +1,86 @@
+<?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:orientation="vertical" >
+
+    <!-- ssl error type -->
+    <TextView
+        android:id="@+id/ssl_error_type"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        android:text="SSL_UNKNOWN"
+        android:layout_marginStart="24dip"
+        android:layout_marginEnd="24dip"
+        android:layout_marginBottom="0dip"
+        android:layout_marginTop="24dip" />
+
+    <!-- Page info: -->
+    <TextView
+        android:id="@+id/page_info"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/page_info"
+        android:textStyle="bold"
+        android:layout_marginStart="24dip"
+        android:layout_marginEnd="24dip" />
+
+    <!-- Title: -->
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textStyle="bold"
+        android:layout_marginStart="24dip"
+        android:layout_marginEnd="24dip" />
+
+    <!-- Address: -->
+    <TextView
+        android:id="@+id/address_header"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/page_info_address"
+        android:layout_marginStart="24dip"
+        android:layout_marginEnd="24dip" />
+
+    <TextView
+        android:id="@+id/address"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dip"
+        android:layout_marginEnd="24dip" />
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingStart="4dip"
+        android:paddingEnd="4dip" >
+
+        <!-- certificate view: -->
+        <LinearLayout
+            android:id="@+id/certificate_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="16dip" >
+        </LinearLayout>
+
+    </ScrollView>
+
+</LinearLayout>
diff --git a/packages/CaptivePortalLogin/res/values-af/strings.xml b/packages/CaptivePortalLogin/res/values-af/strings.xml
index fa6f3fa..cf4dc82 100644
--- a/packages/CaptivePortalLogin/res/values-af/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-af/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Die netwerk waarby jy probeer aansluit, het sekuriteitkwessies."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Byvoorbeeld, die aanmeldbladsy behoort dalk nie aan die organisasie wat gewys word nie."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Gaan in elk geval deur blaaier voort"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Bladsy-inligting"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sekuriteitswaarskuwing"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Bekyk sertifikaat"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Hierdie sertifikaat is nie van \'n betroubare owerheid nie."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Die naam van die werf kom nie ooreen met die naam op die sertifikaat nie."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Hierdie sertifikaat het verval."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Hierdie sertifikaat is nog nie geldig nie."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Hierdie sertifikaat het \'n ongeldige datum."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Hierdie sertifikaat is ongeldig."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Onbekende sertifikaatfout."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-am/strings.xml b/packages/CaptivePortalLogin/res/values-am/strings.xml
index 36d5e19..cdcb5a5 100644
--- a/packages/CaptivePortalLogin/res/values-am/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-am/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"ለመቀላቀል እየሞከሩ ያሉት አውታረ መረብ የደህንነት ችግሮች አሉበት።"</string>
     <string name="ssl_error_example" msgid="647898534624078900">"ለምሳሌ፣ የመግቢያ ገጹ የሚታየው ድርጅት ላይሆን ይችላል።"</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"ለማንኛውም በአሳሽ በኩል ይቀጥሉ"</string>
+    <string name="ok" msgid="1509280796718850364">"እሺ"</string>
+    <string name="page_info" msgid="4048529256302257195">"የገፅ መረጃ"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"አድራሻ:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"የደህንነት ቅንብሮች"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"ምስክሮች ይመልከቱ"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"ይህ ምስክር ከታማኝ ቦታ አይደለም።"</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"የጣቢያው ስም ከምስክር ወረቀቱ ስም ጋር አይዛመድም።"</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"ይህ ምስክር ጊዜው አልፏል"</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"ይህ ምስክር ገና ትክክል አይደለም።"</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"ይህ ምስክር ትክክለኛ ቀን አለው።"</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"ይህ ምስክር ትክክል ያልሆነ ነው።"</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"ያልታወቀ የምስክር ስህተት።"</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-ar/strings.xml b/packages/CaptivePortalLogin/res/values-ar/strings.xml
index 8eb259b..7773eeb 100644
--- a/packages/CaptivePortalLogin/res/values-ar/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ar/strings.xml
@@ -11,4 +11,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"الشبكة التي تحاول الانضمام إليها بها مشاكل أمنية."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"على سبيل المثال، قد لا تنتمي صفحة تسجيل الدخول إلى المنظمة المعروضة."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"المتابعة على أي حال عبر المتصفح"</string>
+    <string name="ok" msgid="1509280796718850364">"موافق"</string>
+    <string name="page_info" msgid="4048529256302257195">"معلومات الصفحة"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"العنوان:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"تحذير أمان"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"عرض الشهادة"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"هذه الشهادة ليست من جهة موثوق بها."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"لا يتطابق اسم الموقع مع الاسم على الشهادة."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"انتهت صلاحية هذه الشهادة."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"هذه الشهادة ليست صالحة بعد."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"تشتمل هذه الشهادة على تاريخ غير صالح."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"هذه الشهادة غير صالحة."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"حدث خطأ غير معروف بالشهادة."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-bg/strings.xml b/packages/CaptivePortalLogin/res/values-bg/strings.xml
index 8ce9deb..4dd8aa0 100644
--- a/packages/CaptivePortalLogin/res/values-bg/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-bg/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Мрежата, към която опитвате да се присъедините, има проблеми със сигурността."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Например страницата за вход може да не принадлежи на показаната организация."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Продължаване през браузър въпреки това"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Данни за страницата"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Адрес:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Предупреждение относно защитата"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Преглед на сертификата"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Сертификатът не е от надежден орган."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Името на сайта не съответства на името в сертификата."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Сертификатът е изтекъл."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Сертификатът още не е валиден."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Този сертификат е с невалидна дата."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Този сертификат е невалиден."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Неизвестна грешка в сертификата."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-bn/strings.xml b/packages/CaptivePortalLogin/res/values-bn/strings.xml
index b75d76e..fb703cf 100644
--- a/packages/CaptivePortalLogin/res/values-bn/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-bn/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"আপনি যে নেটওয়ার্কে যোগ দেওয়ার চেষ্টা করছেন তাতে নিরাপত্তার সমস্যা আছে।"</string>
     <string name="ssl_error_example" msgid="647898534624078900">"উদাহরণস্বরূপ, লগ-ইন পৃষ্ঠাটি প্রদর্শিত প্রতিষ্ঠানের অন্তর্গত নাও হতে পারে৷"</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"যাই হোক না কেন ব্রাউজারের মাধ্যমে অবিরত রাখুন"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Sideinfo"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sikkerhetsadvarsel"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vis sertifikat"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sertifikatet er ikke fra en pålitelig myndighet."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Navnet på nettstedet samsvarer ikke med navnet på sertifikatet."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Sertifikatet er utløpt."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sertifikatet er ikke gyldig ennå."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dette sertifikatet har en ugyldig dato."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Dette sertifikatet er ugyldig."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Ukjent sertifikatfeil."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-ca/strings.xml b/packages/CaptivePortalLogin/res/values-ca/strings.xml
index fe189ed..a2c9ed8 100644
--- a/packages/CaptivePortalLogin/res/values-ca/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ca/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"La xarxa a què et vols connectar té problemes de seguretat."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Per exemple, la pàgina d\'inici de sessió podria no pertànyer a l\'organització que es mostra."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Continua igualment mitjançant el navegador"</string>
+    <string name="ok" msgid="1509280796718850364">"D\'acord"</string>
+    <string name="page_info" msgid="4048529256302257195">"Informació de la pàgina"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adreça:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Advertiment de seguretat"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visualitza el certificat"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Aquest certificat no és d\'una autoritat de confiança."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"El nom del lloc no coincideix amb el del certificat."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Aquest certificat ha caducat."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Aquest certificat encara no és vàlid."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Aquest certificat té una data no vàlida."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Aquest certificat no és vàlid."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Error de certificat desconegut."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-cs/strings.xml b/packages/CaptivePortalLogin/res/values-cs/strings.xml
index 09dcc5f..be649a5 100644
--- a/packages/CaptivePortalLogin/res/values-cs/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-cs/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Síť, ke které se pokoušíte připojit, má bezpečnostní problémy."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Například přihlašovací stránka nemusí patřit do zobrazované organizace."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Přesto pokračovat prostřednictvím prohlížeče"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Informace o stránce"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adresa:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Upozornění zabezpečení"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Zobrazit certifikát"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Tento certifikát nepochází od důvěryhodné autority."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Název webu se neshoduje s názvem uvedeným v certifikátu."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Platnost certifikátu vypršela."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Tento certifikát ještě není platný."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Datum tohoto certifikátu není platné."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Tento certifikát je neplatný."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Neznámá chyba certifikátu."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-da/strings.xml b/packages/CaptivePortalLogin/res/values-da/strings.xml
index dc0dd17..8183105 100644
--- a/packages/CaptivePortalLogin/res/values-da/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-da/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Der er sikkerhedsproblemer på det netværk, du forsøger at logge ind på."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Det er f.eks. ikke sikkert, at loginsiden tilhører den anførte organisation."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Fortsæt alligevel via browseren"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Sideoplysninger"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sikkerhedsadvarsel"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vis certifikat"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Dette certifikat stammer ikke fra en troværdig autoritet."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Navnet på websitet stemmer ikke overens med navnet på certifikatet."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Dette certifikat er udløbet."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Dette certifikat er endnu ikke gyldigt."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dette certifikat har en ugyldig dato."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Dette certifikat er ugyldigt."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Ukendt fejl i certifikatet."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-de/strings.xml b/packages/CaptivePortalLogin/res/values-de/strings.xml
index d8f7be9..a9b7415 100644
--- a/packages/CaptivePortalLogin/res/values-de/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-de/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Im Netzwerk, zu dem du eine Verbindung herstellen möchtest, liegen Sicherheitsprobleme vor."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Beispiel: Die Log-in-Seite gehört eventuell nicht zur angezeigten Organisation."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Trotzdem in einem Browser fortfahren"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Seiteninfo"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sicherheitswarnung"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Zertifikat ansehen"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Dieses Zertifikat wurde nicht von einer vertrauenswürdigen Stelle ausgegeben."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Name der Website stimmt nicht mit dem Namen auf dem Zertifikat überein."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Dieses Zertifikat ist abgelaufen."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Dieses Zertifikat ist noch nicht gültig."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dieses Zertifikat weist ein ungültiges Datum auf."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Dieses Zertifikat ist ungültig."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Unbekannter Zertifikatfehler"</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-el/strings.xml b/packages/CaptivePortalLogin/res/values-el/strings.xml
index cb61710..16bf6e2 100644
--- a/packages/CaptivePortalLogin/res/values-el/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-el/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Παρουσιάζονται προβλήματα ασφάλειας στο δίκτυο στο οποίο προσπαθείτε να συνδεθείτε."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Για παράδειγμα, η σελίδα σύνδεσης ενδέχεται να μην ανήκει στον οργανισμό που εμφανίζεται."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Συνέχεια ούτως ή άλλως μέσω του προγράμματος περιήγησης"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Πληροφορίες σελίδας"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Διεύθυνση:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Προειδοποίηση ασφαλείας"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Προβολή πιστοποιητικού"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Αυτό το πιστοποιητικό δεν προέρχεται από αξιόπιστη αρχή."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Το όνομα του ιστότοπου δεν αντιστοιχεί με το όνομα στο πιστοποιητικό."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Αυτό το πιστοποιητικό έχει λήξει."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Αυτό το πιστοποιητικό δεν είναι έγκυρο ακόμα."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Αυτό το πιστοποιητικό δεν έχει έγκυρη ημερομηνία."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Αυτό το πιστοποιητικό δεν είναι έγκυρο."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Άγνωστο σφάλμα πιστοποιητικού."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml b/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
index 2e8d1f0..f940299 100644
--- a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Page info"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Address:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Security warning"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"View certificate"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"This certificate isn\'t from a trusted authority."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"The name of the site doesn\'t match the name on the certificate."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"This certificate has expired."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"This certificate isn\'t valid yet."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"This certificate has an invalid date."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"This certificate is invalid."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Unknown certificate error."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml b/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
index 2e8d1f0..f940299 100644
--- a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Page info"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Address:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Security warning"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"View certificate"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"This certificate isn\'t from a trusted authority."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"The name of the site doesn\'t match the name on the certificate."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"This certificate has expired."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"This certificate isn\'t valid yet."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"This certificate has an invalid date."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"This certificate is invalid."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Unknown certificate error."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml b/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml
index 5d7ba91..c011664 100644
--- a/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"La red a la que intentas conectarte tiene problemas de seguridad."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Por ejemplo, es posible que la página de acceso no pertenezca a la organización que aparece."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar de todos modos desde el navegador"</string>
+    <string name="ok" msgid="1509280796718850364">"Aceptar"</string>
+    <string name="page_info" msgid="4048529256302257195">"Información de la página"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Dirección:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Advertencia de seguridad"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Ver certificado"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado no proviene de una autoridad confiable."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"El nombre del sitio no coincide con el nombre del certificado."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado ha expirado."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado aún no es válido."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"La fecha de este certificado no es válida."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado no es válido."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Error de certificado desconocido"</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-es/strings.xml b/packages/CaptivePortalLogin/res/values-es/strings.xml
index da2eae9..65244e7 100644
--- a/packages/CaptivePortalLogin/res/values-es/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-es/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"La red a la que intentas unirte tiene problemas de seguridad."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Por ejemplo, es posible que la página de inicio de sesión no pertenezca a la organización mostrada."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar de todos modos a través del navegador"</string>
+    <string name="ok" msgid="1509280796718850364">"Aceptar"</string>
+    <string name="page_info" msgid="4048529256302257195">"Información de la página"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Dirección:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Advertencia de seguridad"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Ver certificado"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado no procede de una entidad de certificación de confianza."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"El nombre del sitio no coincide con el del certificado."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado ha caducado."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado aún no es válido."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"La fecha de este certificado no es válida."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado no es válido."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Error de certificado desconocido"</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-et/strings.xml b/packages/CaptivePortalLogin/res/values-et/strings.xml
index 41fcb9a..e4c4c98 100644
--- a/packages/CaptivePortalLogin/res/values-et/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-et/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Võrgul, millega üritate ühenduse luua, on turvaprobleeme."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Näiteks ei pruugi sisselogimisleht kuuluda kuvatavale organisatsioonile."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Jätka siiski brauseris"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Lehe teave"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Aadress:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Turvahoiatus"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Kuva sertifikaat"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"See sertifikaat ei pärine usaldusväärselt asutuselt."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Saidi nimi ei vasta sertifikaadil olevale nimele."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"See sertifikaat on aegunud."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"See sertifikaat pole veel kehtiv."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Sellel sertifikaadil on kehtetu kuupäev."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"See sertifikaat on kehtetu."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Tundmatu sertifikaadiviga."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-fa/strings.xml b/packages/CaptivePortalLogin/res/values-fa/strings.xml
index 2e4cc51..27b9b7f 100644
--- a/packages/CaptivePortalLogin/res/values-fa/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-fa/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"شبکه‌ای که می‌خواهید به آن بپیوندید مشکلات امنیتی دارد."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"به عنوان مثال، صفحه ورود به سیستم ممکن است متعلق به سازمان نشان داده شده نباشد."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"در هر صورت از طریق مرورگر ادامه یابد"</string>
+    <string name="ok" msgid="1509280796718850364">"تأیید"</string>
+    <string name="page_info" msgid="4048529256302257195">"اطلاعات صفحه"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"آدرس:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"اخطار امنیتی"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"مشاهده گواهی"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"این گواهی از یک منبع مورد اطمینان صادر نشده است."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"نام سایت با نام موجود در گواهی مطابقت ندارد."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"این گواهی منقضی شده است."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"این گواهی هنوز معتبر نیست."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"تاریخ این گواهی نامعتبر است."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"این گواهی نامعتبر است."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"خطای ناشناخته در گواهی."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-fi/strings.xml b/packages/CaptivePortalLogin/res/values-fi/strings.xml
index 1976f7d..8086fbf 100644
--- a/packages/CaptivePortalLogin/res/values-fi/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-fi/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Verkossa, johon yrität muodostaa yhteyttä, on turvallisuusongelmia."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Kirjautumissivu ei välttämättä kuulu näytetylle organisaatiolle."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Jatka silti selaimen kautta."</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Sivun tiedot"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Osoite:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Suojausvaroitus"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Näytä varmenne"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Varmenteen myöntäjä ei ole luotettava taho."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Sivuston nimi ei vastaa varmenteessa olevaa nimeä."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Varmenne ei ole enää voimassa."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Varmenne ei ole vielä voimassa."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Varmenteen päiväys ei kelpaa."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Varmenne on virheellinen."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Tuntematon varmennevirhe."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-fr/strings.xml b/packages/CaptivePortalLogin/res/values-fr/strings.xml
index 8f98bb5..39fc569 100644
--- a/packages/CaptivePortalLogin/res/values-fr/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-fr/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Le réseau que vous essayez de rejoindre présente des problèmes de sécurité."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Par exemple, la page de connexion peut ne pas appartenir à l\'organisation représentée."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Continuer quand même dans le navigateur"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Infos sur la page"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adresse :"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Avertissement de sécurité"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Afficher le certificat"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ce certificat provient d\'une autorité non approuvée."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Le nom du site ne correspond pas au nom indiqué dans le certificat."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Le certificat a expiré."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Ce certificat n\'est pas encore valide."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"La date de ce certificat n\'est pas valide."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Ce certificat n\'est pas valide."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Erreur : Certificat inconnu."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-hi/strings.xml b/packages/CaptivePortalLogin/res/values-hi/strings.xml
index 1bacc46..d924fff 100644
--- a/packages/CaptivePortalLogin/res/values-hi/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-hi/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"आप जिस नेटवर्क में शामिल होने का प्रयास कर रहे हैं उसमें सुरक्षा समस्‍याएं हैं."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"उदाहरण के लिए, हो सकता है कि लॉगिन पृष्‍ठ दिखाए गए संगठन से संबद्ध ना हो."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"ब्राउज़र के द्वारा फिर जारी रखें"</string>
+    <string name="ok" msgid="1509280796718850364">"ठीक"</string>
+    <string name="page_info" msgid="4048529256302257195">"पृष्ठ जानकारी"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"पता:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"सुरक्षा चेतावनी"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"प्रमाणपत्र देखें"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"यह प्रमाणपत्र किसी विश्वस्त प्राधिकारी का नहीं है."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"साइट का नाम, प्रमाणपत्र के नाम से मिलान नहीं करता."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"इस प्रमाणपत्र की समय सीमा समाप्त हो गई है."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"यह प्रमाणपत्र अभी तक मान्य नहीं है."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"इस प्रमाणपत्र में एक अमान्‍य दिनांक है."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"यह प्रमाणपत्र अमान्य है."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"अज्ञात प्रमाणपत्र त्रुटि."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-hr/strings.xml b/packages/CaptivePortalLogin/res/values-hr/strings.xml
index e44cd3b..11b1dd3 100644
--- a/packages/CaptivePortalLogin/res/values-hr/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-hr/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Mreža kojoj se pokušavate pridružiti ima sigurnosne poteškoće."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Na primjer, stranica za prijavu možda ne pripada prikazanoj organizaciji."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Ipak nastavi putem preglednika"</string>
+    <string name="ok" msgid="1509280796718850364">"U redu"</string>
+    <string name="page_info" msgid="4048529256302257195">"Informacije o stranici"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adresa:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Upozorenje o sigurnosti"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Prikaži certifikat"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ovaj certifikat ne potječe iz pouzdanog izvora."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Naziv web-lokacije ne podudara se s nazivom na certifikatu."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Ovaj je certifikat istekao."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Ovaj certifikat još nije važeći."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Ovaj certifikat ima nevažeći datum."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Ovaj certifikat nije valjan."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Nepoznata pogreška certifikata."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-hu/strings.xml b/packages/CaptivePortalLogin/res/values-hu/strings.xml
index f15fb49..145e2ab 100644
--- a/packages/CaptivePortalLogin/res/values-hu/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-hu/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Biztonsági problémák vannak azzal a hálózattal, amelyhez csatlakozni szeretne."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Például lehet, hogy a bejelentkezési oldal nem a megjelenített szervezethez tartozik."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Folytatás ennek ellenére böngészőn keresztül"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Oldaladatok"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Cím:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Biztonsági figyelmeztetés"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Tanúsítvány megtekintése"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ez a tanúsítvány nem hiteles tanúsítványkibocsátótól származik."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"A webhely neve nem egyezik a tanúsítványon lévő névvel."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"A tanúsítvány lejárt."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"A tanúsítvány még nem érvényes."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"A tanúsítvány dátuma érvénytelen."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Ez a tanúsítvány érvénytelen."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Ismeretlen tanúsítványhiba."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-in/strings.xml b/packages/CaptivePortalLogin/res/values-in/strings.xml
index 10e3de6..4a335dd 100644
--- a/packages/CaptivePortalLogin/res/values-in/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-in/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Jaringan yang ingin Anda masuki mengalami masalah keamanan."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Misalnya, halaman masuk mungkin bukan milik organisasi yang ditampilkan."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Tetap lanjutkan melalui browser"</string>
+    <string name="ok" msgid="1509280796718850364">"Oke"</string>
+    <string name="page_info" msgid="4048529256302257195">"Info laman"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Alamat:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Peringatan sertifikat"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Lihat sertifikat"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sertifikat ini tidak berasal dari otoritas tepercaya."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Nama situs tidak cocok dengan nama pada sertifikat."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Sertifikat ini telah kedaluwarsa."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sertifikat ini belum valid."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Tanggal sertifikat ini tidak valid."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Sertifikat ini tidak valid."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Kesalahan sertifikat tak dikenal."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-it/strings.xml b/packages/CaptivePortalLogin/res/values-it/strings.xml
index a01a553..2cc4038 100644
--- a/packages/CaptivePortalLogin/res/values-it/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-it/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"La rete a cui stai tentando di accedere presenta problemi di sicurezza."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Ad esempio, la pagina di accesso potrebbe non appartenere all\'organizzazione indicata."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Continua comunque dal browser"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Info pagina"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Indirizzo:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Avviso di sicurezza"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visualizza certificato"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Questo certificato non proviene da un\'autorità attendibile."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Il nome del sito non corrisponde al nome nel certificato."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Il certificato è scaduto."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Questo certificato non è ancora valido."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Questo certificato presenta una data non valida."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Questo certificato non è valido."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Errore certificato sconosciuto."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-iw/strings.xml b/packages/CaptivePortalLogin/res/values-iw/strings.xml
index 8e7915d..527e692 100644
--- a/packages/CaptivePortalLogin/res/values-iw/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-iw/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"יש בעיות אבטחה ברשת שאליה אתה מנסה להתחבר."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"לדוגמה, ייתכן שדף ההתחברות אינו שייך לארגון המוצג."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"המשך בכל זאת באמצעות דפדפן"</string>
+    <string name="ok" msgid="1509280796718850364">"אישור"</string>
+    <string name="page_info" msgid="4048529256302257195">"פרטי דף"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"כתובת:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"אזהרת אבטחה"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"הצג אישור"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"אישור זה אינו מגיע מרשות אמינה."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"שם האתר לא תואם לשם באישור."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"פג תוקפו של אישור זה."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"אישור זה אינו חוקי עדיין."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"לאישור זה יש תאריך בלתי חוקי."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"אישור זה אינו חוקי."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"שגיאת אישור לא ידועה."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-ja/strings.xml b/packages/CaptivePortalLogin/res/values-ja/strings.xml
index e275b95..bcc8686 100644
--- a/packages/CaptivePortalLogin/res/values-ja/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ja/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"接続しようとしているネットワークにセキュリティの問題があります。"</string>
     <string name="ssl_error_example" msgid="647898534624078900">"たとえば、ログインページが表示されている組織に属していない可能性があります。"</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"ブラウザから続行"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"ページ情報"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"アドレス:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"セキュリティ警告"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"証明書を表示"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"この証明書は信頼できる認証機関のものではありません。"</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"サイト名と証明書上の名前が一致しません。"</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"この証明書は有効期限切れです。"</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"この証明書はまだ有効ではありません。"</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"この証明書の日付は無効です。"</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"この証明書は無効です。"</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"不明な証明書エラーです。"</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-ko/strings.xml b/packages/CaptivePortalLogin/res/values-ko/strings.xml
index 75f2b48..7a7f7e0 100644
--- a/packages/CaptivePortalLogin/res/values-ko/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ko/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"가입하려는 네트워크에 보안 문제가 있습니다."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"예를 들어 로그인 페이지가 표시된 조직에 속하지 않을 수 있습니다."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"브라우저를 통해 계속하기"</string>
+    <string name="ok" msgid="1509280796718850364">"확인"</string>
+    <string name="page_info" msgid="4048529256302257195">"페이지 정보"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"주소:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"보안 경고"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"인증서 보기"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"신뢰할 수 있는 인증 기관에서 발급한 인증서가 아닙니다."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"사이트 이름이 인증서에 있는 것과 일치하지 않습니다."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"인증서가 만료되었습니다."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"인증서가 아직 유효하지 않습니다."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"인증서 날짜가 유효하지 않습니다."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"인증서가 잘못되었습니다."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"알 수 없는 인증서 오류입니다."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-lt/strings.xml b/packages/CaptivePortalLogin/res/values-lt/strings.xml
index 17da83f..158f7ce 100644
--- a/packages/CaptivePortalLogin/res/values-lt/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-lt/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Kilo tinklo, prie kurio bandote prisijungti, problemų."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Pavyzdžiui, prisijungimo puslapis gali nepriklausyti rodomai organizacijai."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Vis tiek tęsti naudojant naršyklę"</string>
+    <string name="ok" msgid="1509280796718850364">"Gerai"</string>
+    <string name="page_info" msgid="4048529256302257195">"Puslapio informacija"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adresas:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Saugos įspėjimas"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Žiūrėti sertifikatą"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Šį sertifikatą išdavė nepatikima įstaiga."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Svetainės pavadinimas neatitinka sertifikate nurodyto pavadinimo."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Šio sertifikato galiojimo laikas baigėsi."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Šis sertifikatas dar negalioja."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Šio sertifikato data netinkama."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Šis sertifikatas netinkamas."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Nežinoma sertifikato klaida."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-lv/strings.xml b/packages/CaptivePortalLogin/res/values-lv/strings.xml
index 95b8558..a42cb22 100644
--- a/packages/CaptivePortalLogin/res/values-lv/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-lv/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Tīklam, kuram mēģināt pievienoties, ir drošības problēmas."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Piemēram, pieteikšanās lapa, iespējams, nepieder norādītajai organizācijai."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Tik un tā turpināt, izmantojot pārlūkprogrammu"</string>
+    <string name="ok" msgid="1509280796718850364">"Labi"</string>
+    <string name="page_info" msgid="4048529256302257195">"Lapas informācija"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adrese:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Drošības brīdinājums"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Skatīt sertifikātu"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Šo sertifikātu nav izsniegusi uzticama iestāde."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Vietnes nosaukums neatbilst nosaukumam sertifikātā."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Šī sertifikāta derīguma termiņš ir beidzies."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Šis sertifikāts vēl nav derīgs."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Šī sertifikāta datums nav derīgs."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Šis sertifikāts nav derīgs."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Nezināma sertifikāta kļūda."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-ms/strings.xml b/packages/CaptivePortalLogin/res/values-ms/strings.xml
index 933721a..aaa51c8 100644
--- a/packages/CaptivePortalLogin/res/values-ms/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ms/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Rangkaian yang anda cuba sertai mempunyai isu keselamatan."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Contohnya, halaman log masuk mungkin bukan milik organisasi yang ditunjukkan."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Teruskan juga melalui penyemak imbas"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Maklumat halaman"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Alamat:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Amaran keselamatan"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Lihat sijil"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sijil ini bukan daripada pihak berkuasa yang dipercayai."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Nama tapak tidak sepadan dengan nama pada sijil."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Sijil ini telah tamat tempoh."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sijil ini belum lagi sah."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Sijil ini mempunyai tarikh yang tidak sah."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Sijil ini tidak sah."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Ralat sijil tidak diketahui."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-nb/strings.xml b/packages/CaptivePortalLogin/res/values-nb/strings.xml
index 0dd5b6c..29c23ed 100644
--- a/packages/CaptivePortalLogin/res/values-nb/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-nb/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Nettverket du prøver å logge på, har sikkerhetsproblemer."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Det er for eksempel mulig at påloggingssiden kanskje ikke tilhører organisasjonen som vises."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Fortsett likevel via nettleseren"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Sideinfo"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sikkerhetsadvarsel"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vis sertifikat"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sertifikatet er ikke fra en pålitelig myndighet."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Navnet på nettstedet samsvarer ikke med navnet på sertifikatet."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Sertifikatet er utløpt."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sertifikatet er ikke gyldig ennå."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dette sertifikatet har en ugyldig dato."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Dette sertifikatet er ugyldig."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Ukjent sertifikatfeil."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-nl/strings.xml b/packages/CaptivePortalLogin/res/values-nl/strings.xml
index 1c59601..2cbca06 100644
--- a/packages/CaptivePortalLogin/res/values-nl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-nl/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Het netwerk waarmee u verbinding probeert te maken, heeft beveiligingsproblemen."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Zo hoort de weergegeven inlogpagina misschien niet bij de weergegeven organisatie."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Toch doorgaan via browser"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Pagina-informatie"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Beveiligingsmelding"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Certificaat weergeven"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Dit is geen certificaat van een vertrouwde autoriteit."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"De naam van deze site komt niet overeen met de naam op het certificaat."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Dit certificaat is verlopen."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Dit certificaat is nog niet geldig."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dit certificaat heeft een ongeldige datum."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Dit certificaat is ongeldig."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Onbekende certificaatfout."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-pl/strings.xml b/packages/CaptivePortalLogin/res/values-pl/strings.xml
index 17f20df..9ba066e 100644
--- a/packages/CaptivePortalLogin/res/values-pl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-pl/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"W sieci, z którą próbujesz się połączyć, występują problemy z zabezpieczeniami."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Na przykład strona logowania może nie należeć do wyświetlanej organizacji."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Kontynuuj mimo to w przeglądarce"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Informacje o stronie"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Ostrzeżenie zabezpieczeń"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Wyświetl certyfikat"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Certyfikat nie pochodzi od zaufanego urzędu."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Nazwa witryny nie pasuje do nazwy na certyfikacie."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Ten certyfikat wygasł."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Certyfikat nie jest jeszcze ważny."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Certyfikat ma nieprawidłową datę."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Certyfikat jest nieprawidłowy."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Nieznany błąd certyfikatu"</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml b/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml
index 94b9d60..5bef235 100644
--- a/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"A rede à qual está a tentar aceder tem problemas de segurança."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, a página de início de sessão pode não pertencer à entidade apresentada."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar mesmo assim através do navegador"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Informações da página"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Endereço:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Aviso de segurança"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Ver certificado"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado não pertence a uma autoridade fidedigna."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"O nome do Web site não corresponde ao nome constante no certificado."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado expirou."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado ainda não é válido."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Este certificado tem uma data inválida."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado é inválido."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Erro: certificado desconhecido."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-pt/strings.xml b/packages/CaptivePortalLogin/res/values-pt/strings.xml
index 3d1064c..ebe4148 100644
--- a/packages/CaptivePortalLogin/res/values-pt/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-pt/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"A rede à qual você está tentando se conectar tem problemas de segurança."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, a página de login pode não pertencer à organização mostrada."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar mesmo assim pelo navegador"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Informações da página"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Endereço:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Aviso de segurança"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visualizar certificado"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado não é de uma autoridade confiável."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"O nome do site não corresponde ao nome no certificado."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado expirou."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado ainda não é válido."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Este certificado tem uma data inválida."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado é inválido."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Erro de certificado desconhecido."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-ro/strings.xml b/packages/CaptivePortalLogin/res/values-ro/strings.xml
index cf1b6b5..e2e4eac 100644
--- a/packages/CaptivePortalLogin/res/values-ro/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ro/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Rețeaua la care încercați să vă conectați are probleme de securitate."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"De exemplu, este posibil ca pagina de conectare să nu aparțină organizației afișate."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Continuați oricum prin browser"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Informaţii pagină"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adresă:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Avertisment de securitate"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vizualizaţi certificatul"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Acest certificat nu provine de la o autoritate de încredere."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Numele acestui site nu se potriveşte cu numele de pe certificat."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Acest certificat a expirat."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Acest certificat nu este încă valid."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Acest certificat are o dată nevalidă."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Acest certificat este nevalid."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Eroare de certificat necunoscută."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-ru/strings.xml b/packages/CaptivePortalLogin/res/values-ru/strings.xml
index 6966bcd..c0153e6 100644
--- a/packages/CaptivePortalLogin/res/values-ru/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-ru/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Сеть, к которой вы хотите подключиться, небезопасна."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Например, страница входа в аккаунт может быть фиктивной."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Игнорировать и открыть браузер"</string>
+    <string name="ok" msgid="1509280796718850364">"ОК"</string>
+    <string name="page_info" msgid="4048529256302257195">"Информация о странице"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Адрес:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Угроза безопасности"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Просмотреть сертификат"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Этот сертификат получен из ненадежных источников."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Название сайта не соответствует названию в сертификате."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Срок действия сертификата истек."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Сертификат еще не действителен."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Дата этого сертификата недействительна."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Этот сертификат недействителен."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Неизвестная ошибка сертификата."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-sk/strings.xml b/packages/CaptivePortalLogin/res/values-sk/strings.xml
index 54763be..8ba24b1 100644
--- a/packages/CaptivePortalLogin/res/values-sk/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sk/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Sieť, ku ktorej sa pokúšate pripojiť, má problémy so zabezpečením"</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Napríklad prihlasovacia stránka nemusí patriť uvedenej organizácii."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Pokračovať pomocou prehliadača"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Informácie o stránke"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adresa:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Upozornenie zabezpečenia"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Zobraziť certifikát"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Tento certifikát nepochádza od dôveryhodnej autority."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Názov stránky sa nezhoduje s názvom uvedeným v certifikáte."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Platnosť certifikátu skončila."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Tento certifikát zatiaľ nie je platný."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Tento certifikát má neplatný dátum."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Tento certifikát je neplatný."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Neznáma chyba certifikátu."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-sl/strings.xml b/packages/CaptivePortalLogin/res/values-sl/strings.xml
index 7dd0b37..b7d9a8a 100644
--- a/packages/CaptivePortalLogin/res/values-sl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sl/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Omrežje, ki se mu poskušate pridružiti, ima varnostne težave."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Stran za prijavo na primer morda ne pripada prikazani organizaciji."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Vseeno nadaljuj v brskalniku"</string>
+    <string name="ok" msgid="1509280796718850364">"V redu"</string>
+    <string name="page_info" msgid="4048529256302257195">"Podatki o strani"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Naslov:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Varnostno opozorilo"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Prikaži potrdilo"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Potrdila ni izdal zaupanja vreden overitelj."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Ime spletnega mesta se ne ujema z imenom na potrdilu."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Potrdilo je poteklo."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"To potrdilo še ni veljavno."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Potrdilo ima neveljaven datum."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"To potrdilo ni veljavno."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Neznana napaka potrdila."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-sr/strings.xml b/packages/CaptivePortalLogin/res/values-sr/strings.xml
index f604289..967c8ba 100644
--- a/packages/CaptivePortalLogin/res/values-sr/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sr/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Мрежа којој покушавате да се придружите има безбедносних проблема."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"На пример, страница за пријављивање можда не припада приказаној организацији."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Ипак настави преко прегледача"</string>
+    <string name="ok" msgid="1509280796718850364">"Потврди"</string>
+    <string name="page_info" msgid="4048529256302257195">"Информације о страници"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Адреса:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Безбедносно упозорење"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Прикажи сертификат"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Овај сертификат не потиче од поузданог ауторитета."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Назив сајта се не подудара са називом на сертификату."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Овај сертификат је истекао."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Овај сертификат још увек није важећи."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Датум овог сертификата је неважећи."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Овај сертификат је неважећи."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Непозната грешка сертификата."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-sv/strings.xml b/packages/CaptivePortalLogin/res/values-sv/strings.xml
index 8cf7041..75356f0 100644
--- a/packages/CaptivePortalLogin/res/values-sv/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sv/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Nätverket du försöker ansluta till har säkerhetsproblem."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Det kan t.ex. hända att inloggningssidan inte tillhör den organisation som visas."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Fortsätt ändå via webbläsaren"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Sidinformation"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adress:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Säkerhetsvarning"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visa certifikat"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Certifikatet kommer inte från en betrodd utfärdare."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Webbplatsens namn stämmer inte med namnet på certifikatet."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Certifikatet har upphört att gälla."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Certifikatet är inte giltigt än."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Det här certifikatet har ett ogiltigt datum."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Certifikatet är ogiltigt."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Okänt certifikatfel."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-sw/strings.xml b/packages/CaptivePortalLogin/res/values-sw/strings.xml
index 1c8b6e1..feb2dde 100644
--- a/packages/CaptivePortalLogin/res/values-sw/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-sw/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Mtandao unaojaribu kujiunga nao una matatizo ya usalama."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Kwa mfano, ukurasa wa kuingia katika akaunti unaweza usiwe unamilikiwa na shirika lililoonyeshwa."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Endelea hata hivyo kupitia kivinjari"</string>
+    <string name="ok" msgid="1509280796718850364">"Sawa"</string>
+    <string name="page_info" msgid="4048529256302257195">"Maelezo ya ukurasa"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Anwani:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Ilani ya usalama"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Tazama cheti"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Cheti hiki hakijatoka kwa mamlaka inayoaminika."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Jina la tovuti halilingani na jina lililo katika cheti."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Cheti hiki kimepitwa na muda"</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Cheti bado si halali."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Cheti hiki kina tarehe batili."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Hati hii ni batili."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Hitilafu isiyojulikana ya cheti."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-th/strings.xml b/packages/CaptivePortalLogin/res/values-th/strings.xml
index 9a3a626..11a2131 100644
--- a/packages/CaptivePortalLogin/res/values-th/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-th/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"เครือข่ายที่คุณพยายามเข้าร่วมมีปัญหาด้านความปลอดภัย"</string>
     <string name="ssl_error_example" msgid="647898534624078900">"ตัวอย่างเช่น หน้าเข้าสู่ระบบอาจไม่ใช่ขององค์กรที่แสดงไว้"</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"ดำเนินการต่อผ่านเบราว์เซอร์"</string>
+    <string name="ok" msgid="1509280796718850364">"ตกลง"</string>
+    <string name="page_info" msgid="4048529256302257195">"ข้อมูลหน้าเว็บ"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"ที่อยู่:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"คำเตือนเกี่ยวกับความปลอดภัย"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"ดูใบรับรอง"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"ใบรับรองนี้ไม่ได้มาจากผู้ออกที่เชื่อถือได้"</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"ชื่อไซต์ไม่ตรงกับในใบรับรอง"</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"ใบรับรองนี้หมดอายุแล้ว"</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"ใบรับรองนี้ยังใช้งานไม่ได้"</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"ใบรับรองนี้มีวันที่ไม่ถูกต้อง"</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"ใบรับรองนี้ไม่ถูกต้อง"</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"ข้อผิดพลาดใบรับรองที่ไม่รู้จัก"</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-tl/strings.xml b/packages/CaptivePortalLogin/res/values-tl/strings.xml
index 565ef8f..07a2479 100644
--- a/packages/CaptivePortalLogin/res/values-tl/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-tl/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"May mga isyu sa seguridad ang network kung saan mo sinusubukang sumali."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Halimbawa, maaaring hindi sa organisasyong ipinapakita ang page sa pag-log in."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Magpatuloy pa rin sa pamamagitan ng browser"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Impormasyon ng pahina"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Address:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Babala sa seguridad"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Tingnan ang certificate"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ang certificate ay hindi mula sa isang pinagkakatiwalaang kinauukulan."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Ang pangalan ng site ay hindi tumutugma sa pangalan sa certificate."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Nag-expire na ang certificate na ito."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Wala pang bisa ang certificate na ito."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Ang certificate ay mayroong di-wastong petsa."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Di-wasto ang certificate na ito."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Hindi kilalang error ng certificate."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-tr/strings.xml b/packages/CaptivePortalLogin/res/values-tr/strings.xml
index 73d2455..cdedd33 100644
--- a/packages/CaptivePortalLogin/res/values-tr/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-tr/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Katılmaya çalıştığınız ağda güvenlik sorunları var."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Örneğin, giriş sayfası, gösterilen kuruluşa ait olmayabilir."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Yine de tarayıcıyla devam et"</string>
+    <string name="ok" msgid="1509280796718850364">"Tamam"</string>
+    <string name="page_info" msgid="4048529256302257195">"Sayfa bilgileri"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Güvenlik uyarısı"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Sertifikayı görüntüle"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Bu sertifika güvenilir bir yetkiliden değil."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Sitenin adı sertifika üzerindeki adla eşleşmiyor."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Bu sertifikanın süresi dolmuş."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Bu sertifika henüz geçerli değil."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Bu sertifikanın tarihi geçersiz."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Bu sertifika geçersiz."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Bilinmeyen sertifika hatası."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-uk/strings.xml b/packages/CaptivePortalLogin/res/values-uk/strings.xml
index 0e818d3..0f4cd16 100644
--- a/packages/CaptivePortalLogin/res/values-uk/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-uk/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"У мережі, до якої ви намагаєтеся під’єднатись, є проблеми з безпекою."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Наприклад, сторінка входу може не належати вказаній організації."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Усе одно продовжити у веб-переглядачі"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Інфо про стор."</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Адреса:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Застереж. про небезп."</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Переглянути сертиф."</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Сертифікат видано ненадійним центром сертифікації."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Назва сайту не збігається з назвою в сертифікаті."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Термін дії сертиф. завершився."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Цей сертифікат ще не дійсний."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Цей сертифікат має недійсну дату."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Цей сертифікат недійсний."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Помилка невідомого сертифіката."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-vi/strings.xml b/packages/CaptivePortalLogin/res/values-vi/strings.xml
index e51d2aa..9c702b9 100644
--- a/packages/CaptivePortalLogin/res/values-vi/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-vi/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Mạng mà bạn đang cố gắng tham gia có vấn đề về bảo mật."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Ví dụ, trang đăng nhập có thể không thuộc về tổ chức được hiển thị."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Vẫn tiếp tục qua trình duyệt"</string>
+    <string name="ok" msgid="1509280796718850364">"OK"</string>
+    <string name="page_info" msgid="4048529256302257195">"Thông tin trang"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Địa chỉ:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Cảnh báo bảo mật"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Xem chứng chỉ"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Chứng chỉ này không xuất phát từ tổ chức phát hành đáng tin cậy."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Tên của trang web không khớp với tên trên chứng chỉ."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Chứng chỉ này đã hết hạn."</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Chứng chỉ này chưa hợp lệ."</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Chứng chỉ này có ngày không hợp lệ."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Chứng chỉ này không hợp lệ."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Lỗi chứng chỉ không xác định."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml
index ce822e7..70c2a08 100644
--- a/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"您尝试加入的网络存在安全问题。"</string>
     <string name="ssl_error_example" msgid="647898534624078900">"例如,登录页面可能并不属于页面上显示的单位。"</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"仍然通过浏览器继续操作"</string>
+    <string name="ok" msgid="1509280796718850364">"确定"</string>
+    <string name="page_info" msgid="4048529256302257195">"网页信息"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"网址:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"安全警告"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"查看证书"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"该证书并非来自可信的授权中心。"</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"网站的名称与证书上的名称不一致。"</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"该证书已过期。"</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"该证书尚未生效。"</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"该证书的日期无效。"</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"该证书无效。"</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"未知证书错误。"</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml
index 9010e1e..df1c700 100644
--- a/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"您正在嘗試加入的網絡有安全性問題。"</string>
     <string name="ssl_error_example" msgid="647898534624078900">"例如,登入頁面並不屬於所顯示的機構。"</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"透過瀏覽器繼續"</string>
+    <string name="ok" msgid="1509280796718850364">"確定"</string>
+    <string name="page_info" msgid="4048529256302257195">"網頁資訊"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"地址:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"安全性警告"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"查看憑證"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"這個憑證並非由受信任的權威機構發出。"</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"網站名稱與憑證上的名稱不相符。"</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"這個憑證已過期。"</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"這個憑證尚未生效。"</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"此憑證的日期無效。"</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"此憑證是無效的。"</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"不明的憑證錯誤。"</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml
index 5b535e2..2a2e397 100644
--- a/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"你嘗試加入的網路有安全問題。"</string>
     <string name="ssl_error_example" msgid="647898534624078900">"例如,登入網頁中顯示的機構可能並非該網頁實際隸屬的機構。"</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"透過瀏覽器繼續"</string>
+    <string name="ok" msgid="1509280796718850364">"確定"</string>
+    <string name="page_info" msgid="4048529256302257195">"頁面資訊"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"位址:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"安全性警告"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"檢視憑證"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"這個憑證並非來自信任的授權單位。"</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"網站名稱與憑證上的名稱不相符。"</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"此憑證已過期"</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"這個憑證尚未生效。"</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"這個憑證的日期無效。"</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"這個憑證無效。"</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"不明的憑證錯誤。"</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-zu/strings.xml b/packages/CaptivePortalLogin/res/values-zu/strings.xml
index 866ba18..7943645 100644
--- a/packages/CaptivePortalLogin/res/values-zu/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-zu/strings.xml
@@ -9,4 +9,16 @@
     <string name="ssl_error_warning" msgid="6653188881418638872">"Inethiwekhi ozama ukuyijoyina inezinkinga zokuvikela."</string>
     <string name="ssl_error_example" msgid="647898534624078900">"Isibonelo, ikhasi lokungena ngemvume kungenzeka lingelenhlangano ebonisiwe."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Qhubeka noma kunjalo ngesiphequluli"</string>
+    <string name="ok" msgid="1509280796718850364">"KULUNGILE"</string>
+    <string name="page_info" msgid="4048529256302257195">"Ulwazi lekhasi"</string>
+    <string name="page_info_address" msgid="2222306609532903254">"Ikheli:"</string>
+    <string name="ssl_security_warning_title" msgid="6607795404322797541">"Isexwayiso sokuvikeleka"</string>
+    <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Buka isitifiketi"</string>
+    <string name="ssl_error_untrusted" msgid="7754507359360636447">"Lesi sitifiketi asiphumi embusweni othembekile."</string>
+    <string name="ssl_error_mismatch" msgid="3809794439740523641">"Igama lale ngosi alifani negama elikusitifiketi."</string>
+    <string name="ssl_error_expired" msgid="5739349389499575559">"Lesi sitifiketi siphelelwe yisikhathi"</string>
+    <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Lesi sitifiketi asilungile okwamanje"</string>
+    <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Lesi sitifiketi sinosuku olungalungile."</string>
+    <string name="ssl_error_invalid" msgid="9041704741505449967">"Lesi sitifiketi asilungile."</string>
+    <string name="ssl_error_unknown" msgid="5679243486524754571">"Iphutha lesitifiketi elingaziwa."</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values/strings.xml b/packages/CaptivePortalLogin/res/values/strings.xml
index f486fe4..e9698db 100644
--- a/packages/CaptivePortalLogin/res/values/strings.xml
+++ b/packages/CaptivePortalLogin/res/values/strings.xml
@@ -9,5 +9,17 @@
     <string name="ssl_error_warning">The network you&#8217;re trying to join has security issues.</string>
     <string name="ssl_error_example">For example, the login page may not belong to the organization shown.</string>
     <string name="ssl_error_continue">Continue anyway via browser</string>
+    <string name="ssl_error_untrusted">This certificate isn\'t from a trusted authority.</string>
+    <string name="ssl_error_mismatch">The name of the site doesn\'t match the name on the certificate.</string>
+    <string name="ssl_error_expired">This certificate has expired.</string>
+    <string name="ssl_error_not_yet_valid">This certificate isn\'t valid yet.</string>
+    <string name="ssl_error_date_invalid">This certificate has an invalid date.</string>
+    <string name="ssl_error_invalid">This certificate is invalid.</string>
+    <string name="ssl_error_unknown">Unknown certificate error.</string>
+    <string name="ssl_security_warning_title">Security warning</string>
+    <string name="ssl_error_view_certificate">View certificate</string>
+    <string name="ok">OK</string>
+    <string name="page_info_address">Address:</string>
+    <string name="page_info">Page info</string>
 
 </resources>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 0ba37ae..83084c5 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -20,8 +20,10 @@
 import static android.net.captiveportal.CaptivePortalProbeSpec.HTTP_LOCATION_HEADER_NAME;
 
 import android.app.Activity;
+import android.app.AlertDialog;
 import android.app.LoadedApk;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.net.CaptivePortal;
@@ -33,6 +35,7 @@
 import android.net.Proxy;
 import android.net.Uri;
 import android.net.captiveportal.CaptivePortalProbeSpec;
+import android.net.http.SslCertificate;
 import android.net.http.SslError;
 import android.net.wifi.WifiInfo;
 import android.os.Build;
@@ -42,8 +45,9 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.TypedValue;
 import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
@@ -52,8 +56,8 @@
 import android.webkit.WebChromeClient;
 import android.webkit.WebSettings;
 import android.webkit.WebView;
-import android.webkit.WebView;
 import android.webkit.WebViewClient;
+import android.widget.LinearLayout;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
@@ -276,6 +280,13 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
+        final WebView webview = (WebView) findViewById(R.id.webview);
+        if (webview != null) {
+            webview.stopLoading();
+            webview.setWebViewClient(null);
+            webview.setWebChromeClient(null);
+            webview.destroy();
+        }
         if (mNetworkCallback != null) {
             // mNetworkCallback is not null if mUrl is not null.
             mCm.unregisterNetworkCallback(mNetworkCallback);
@@ -382,6 +393,7 @@
         private static final String INTERNAL_ASSETS = "file:///android_asset/";
 
         private final String mBrowserBailOutToken = Long.toString(new Random().nextLong());
+        private final String mCertificateOutToken = Long.toString(new Random().nextLong());
         // How many Android device-independent-pixels per scaled-pixel
         // dp/sp = (px/sp) / (px/dp) = (1/sp) / (1/dp)
         private final float mDpPerSp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1,
@@ -397,6 +409,10 @@
             return mPagesLoaded > 1;
         }
 
+        private String mSslErrorTitle = null;
+        private SslErrorHandler mSslErrorHandler = null;
+        private SslError mSslError = null;
+
         @Override
         public void onPageStarted(WebView view, String urlString, Bitmap favicon) {
             if (urlString.contains(mBrowserBailOutToken)) {
@@ -473,12 +489,16 @@
             logMetricsEvent(MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR);
             final String sslErrorPage = makeSslErrorPage();
             view.loadDataWithBaseURL(INTERNAL_ASSETS, sslErrorPage, "text/HTML", "UTF-8", null);
+            mSslErrorTitle = view.getTitle() == null ? "" : view.getTitle();
+            mSslErrorHandler = handler;
+            mSslError = error;
         }
 
         private String makeSslErrorPage() {
             final String warningMsg = getString(R.string.ssl_error_warning);
             final String exampleMsg = getString(R.string.ssl_error_example);
             final String continueMsg = getString(R.string.ssl_error_continue);
+            final String certificateMsg = getString(R.string.ssl_error_view_certificate);
             return String.join("\n",
                     "<html>",
                     "<head>",
@@ -516,13 +536,18 @@
                     "      text-decoration:none;",
                     "      text-transform:uppercase;",
                     "    }",
+                    "    a.certificate {",
+                    "      margin-top:0px;",
+                    "    }",
                     "  </style>",
                     "</head>",
                     "<body>",
                     "  <p><img src=quantum_ic_warning_amber_96.png><br>",
                     "  <div class=warn>" + warningMsg + "</div>",
                     "  <div class=example>" + exampleMsg + "</div>",
-                    "  <a href=" + mBrowserBailOutToken + ">" + continueMsg + "</a>",
+                    "  <a href=" + mBrowserBailOutToken + ">" + continueMsg + "</a><br>",
+                    "  <a class=certificate href=" + mCertificateOutToken + ">" + certificateMsg +
+                            "</a>",
                     "</body>",
                     "</html>");
         }
@@ -533,8 +558,50 @@
                 startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url)));
                 return true;
             }
+            if (url.contains(mCertificateOutToken) && mSslError != null) {
+                showSslAlertDialog(mSslErrorHandler, mSslError, mSslErrorTitle);
+                return true;
+            }
             return false;
         }
+        private void showSslAlertDialog(SslErrorHandler handler, SslError error, String title) {
+            final LayoutInflater factory = LayoutInflater.from(CaptivePortalLoginActivity.this);
+            final View sslWarningView = factory.inflate(R.layout.ssl_warning, null);
+
+            // Set Security certificate
+            setViewSecurityCertificate(sslWarningView.findViewById(R.id.certificate_layout), error);
+            ((TextView) sslWarningView.findViewById(R.id.ssl_error_type))
+                    .setText(sslErrorName(error));
+            ((TextView) sslWarningView.findViewById(R.id.title)).setText(mSslErrorTitle);
+            ((TextView) sslWarningView.findViewById(R.id.address)).setText(error.getUrl());
+
+            AlertDialog sslAlertDialog = new AlertDialog.Builder(CaptivePortalLoginActivity.this)
+                    .setTitle(R.string.ssl_security_warning_title)
+                    .setView(sslWarningView)
+                    .setPositiveButton(R.string.ok, (DialogInterface dialog, int whichButton) -> {
+                        // handler.cancel is called via OnCancelListener.
+                        dialog.cancel();
+                    })
+                    .setOnCancelListener((DialogInterface dialogInterface) -> handler.cancel())
+                    .create();
+            sslAlertDialog.show();
+        }
+
+        private void setViewSecurityCertificate(LinearLayout certificateLayout, SslError error) {
+            SslCertificate cert = error.getCertificate();
+
+            View certificateView = cert.inflateCertificateView(CaptivePortalLoginActivity.this);
+            final LinearLayout placeholder = (LinearLayout) certificateView
+                    .findViewById(com.android.internal.R.id.placeholder);
+            LayoutInflater factory = LayoutInflater.from(CaptivePortalLoginActivity.this);
+
+            TextView textView = (TextView) factory.inflate(
+                    R.layout.ssl_error_msg, placeholder, false);
+            textView.setText(sslErrorMessage(error));
+            placeholder.addView(textView);
+
+            certificateLayout.addView(certificateView);
+        }
     }
 
     private class MyWebChromeClient extends WebChromeClient {
@@ -587,4 +654,18 @@
     private static String sslErrorName(SslError error) {
         return SSL_ERRORS.get(error.getPrimaryError(), "UNKNOWN");
     }
+
+    private static final SparseArray<Integer> SSL_ERROR_MSGS = new SparseArray<>();
+    static {
+        SSL_ERROR_MSGS.put(SslError.SSL_NOTYETVALID,  R.string.ssl_error_not_yet_valid);
+        SSL_ERROR_MSGS.put(SslError.SSL_EXPIRED,      R.string.ssl_error_expired);
+        SSL_ERROR_MSGS.put(SslError.SSL_IDMISMATCH,   R.string.ssl_error_mismatch);
+        SSL_ERROR_MSGS.put(SslError.SSL_UNTRUSTED,    R.string.ssl_error_untrusted);
+        SSL_ERROR_MSGS.put(SslError.SSL_DATE_INVALID, R.string.ssl_error_date_invalid);
+        SSL_ERROR_MSGS.put(SslError.SSL_INVALID,      R.string.ssl_error_invalid);
+    }
+
+    private static Integer sslErrorMessage(SslError error) {
+        return SSL_ERROR_MSGS.get(error.getPrimaryError(), R.string.ssl_error_unknown);
+    }
 }
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/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 0dbc037..2f082b9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -152,7 +152,11 @@
     private boolean mNetworkScoringUiEnabled;
     private long mMaxSpeedLabelScoreCacheAge;
 
-
+    private static final String WIFI_SECURITY_PSK = "PSK";
+    private static final String WIFI_SECURITY_EAP = "EAP";
+    private static final String WIFI_SECURITY_SAE = "SAE";
+    private static final String WIFI_SECURITY_OWE = "OWE";
+    private static final String WIFI_SECURITY_SUITE_B_192 = "SUITE_B_192";
 
     @VisibleForTesting
     Scanner mScanner;
@@ -505,13 +509,18 @@
      * {@link #updateAccessPoints(List, List)}.
      */
     private void fetchScansAndConfigsAndUpdateAccessPoints() {
-        final List<ScanResult> newScanResults = mWifiManager.getScanResults();
+        List<ScanResult> newScanResults = mWifiManager.getScanResults();
+
+        // Filter all unsupported networks from the scan result list
+        final List<ScanResult> filteredScanResults =
+                filterScanResultsByCapabilities(newScanResults);
+
         if (isVerboseLoggingEnabled()) {
-            Log.i(TAG, "Fetched scan results: " + newScanResults);
+            Log.i(TAG, "Fetched scan results: " + filteredScanResults);
         }
 
         List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
-        updateAccessPoints(newScanResults, configs);
+        updateAccessPoints(filteredScanResults, configs);
     }
 
     /** Update the internal list of access points. */
@@ -937,4 +946,49 @@
 
         mListener.onAccessPointsChanged();
     }
+
+    /**
+     * Filters unsupported networks from scan results. New WPA3 networks and OWE networks
+     * may not be compatible with the device HW/SW.
+     * @param scanResults List of scan results
+     * @return List of filtered scan results based on local device capabilities
+     */
+    private List<ScanResult> filterScanResultsByCapabilities(List<ScanResult> scanResults) {
+        if (scanResults == null) {
+            return null;
+        }
+
+        // Get and cache advanced capabilities
+        final boolean isOweSupported = mWifiManager.isOweSupported();
+        final boolean isSaeSupported = mWifiManager.isWpa3SaeSupported();
+        final boolean isSuiteBSupported = mWifiManager.isWpa3SuiteBSupported();
+
+        List<ScanResult> filteredScanResultList = new ArrayList<>();
+
+        // Iterate through the list of scan results and filter out APs which are not
+        // compatible with our device.
+        for (ScanResult scanResult : scanResults) {
+            if (scanResult.capabilities.contains(WIFI_SECURITY_PSK)) {
+                // All devices (today) support RSN-PSK or WPA-PSK
+                // Add this here because some APs may support both PSK and SAE and the check
+                // below will filter it out.
+                filteredScanResultList.add(scanResult);
+                continue;
+            }
+
+            if ((scanResult.capabilities.contains(WIFI_SECURITY_SUITE_B_192) && !isSuiteBSupported)
+                    || (scanResult.capabilities.contains(WIFI_SECURITY_SAE) && !isSaeSupported)
+                    || (scanResult.capabilities.contains(WIFI_SECURITY_OWE) && !isOweSupported)) {
+                if (isVerboseLoggingEnabled()) {
+                    Log.v(TAG, "filterScanResultsByCapabilities: Filtering SSID "
+                            + scanResult.SSID + " with capabilities: " + scanResult.capabilities);
+                }
+            } else {
+                // Safe to add
+                filteredScanResultList.add(scanResult);
+            }
+        }
+
+        return filteredScanResultList;
+    }
 }
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/drawable/stat_sys_camera.xml b/packages/SystemUI/res/drawable/stat_sys_camera.xml
new file mode 100644
index 0000000..eb3e963
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_camera.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetLeft="3dp"
+    android:insetRight="3dp">
+    <vector
+            android:width="17dp"
+            android:height="17dp"
+            android:viewportWidth="24.0"
+            android:viewportHeight="24.0">
+        <path
+            android:fillColor="#FFF"
+            android:pathData="M20,5h-3.17L15,3H9L7.17,5H4C2.9,5 2,5.9 2,7v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V7C22,5.9 21.1,5 20,5zM20,19H4V7h16V19zM12,9c-2.21,0 -4,1.79 -4,4c0,2.21 1.79,4 4,4s4,-1.79 4,-4C16,10.79 14.21,9 12,9z"/>
+    </vector>
+</inset>
diff --git a/packages/SystemUI/res/drawable/stat_sys_mic_none.xml b/packages/SystemUI/res/drawable/stat_sys_mic_none.xml
new file mode 100644
index 0000000..d6bdf9f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_mic_none.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="18dp"
+    android:height="18dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFF"
+        android:pathData="M12,14c1.66,0 3,-1.34 3,-3V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6C9,12.66 10.34,14 12,14zM11,5c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v6c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V5z"/>
+    <path
+        android:fillColor="#FFF"
+        android:pathData="M17,11c0,2.76 -2.24,5 -5,5s-5,-2.24 -5,-5H5c0,3.53 2.61,6.43 6,6.92V21h2v-3.08c3.39,-0.49 6,-3.39 6,-6.92H17z"/>
+</vector>
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/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index f554150..890bf5d 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -62,7 +62,7 @@
                 android:layout_weight="1"
                 android:layout_marginEnd="32dp"
                 android:ellipsize="marquee"
-                android:textAppearance="@style/TextAppearance.QS.TileLabel"
+                android:textAppearance="@style/TextAppearance.QS.CarrierInfo"
                 android:textColor="?android:attr/textColorPrimary"
                 android:textDirection="locale"
                 android:singleLine="true" />
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 980442c..f34161e 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -79,7 +79,7 @@
         android:padding="0dp"
         android:visibility="gone"
         android:gravity="center"
-        android:textAppearance="@style/TextAppearance.QS.TileLabel"
+        android:textAppearance="@style/TextAppearance.QS.TileLabel.Secondary"
         android:textColor="?android:attr/textColorSecondary"/>
 
     <View
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 0e41a7f..97f5f86 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -369,6 +369,7 @@
     <dimen name="qs_page_indicator_height">8dp</dimen>
     <dimen name="qs_tile_icon_size">24dp</dimen>
     <dimen name="qs_tile_text_size">12sp</dimen>
+    <dimen name="qs_carrier_info_text_size">14sp</dimen>
     <dimen name="qs_tile_divider_height">1dp</dimen>
     <dimen name="qs_panel_padding">16dp</dimen>
     <dimen name="qs_dual_tile_height">112dp</dimen>
@@ -939,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 6244e1c..e9aa1b6 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -125,7 +125,7 @@
 
     <style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
         <item name="android:textSize">@dimen/status_bar_clock_size</item>
-        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
         <item name="android:textColor">@color/status_bar_clock_color</item>
     </style>
 
@@ -135,7 +135,7 @@
 
     <style name="TextAppearance.StatusBar.Expanded.Clock">
         <item name="android:textSize">@dimen/qs_time_expanded_size</item>
-        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:textStyle">normal</item>
     </style>
@@ -240,9 +240,31 @@
 
     <style name="TextAppearance.QS.TileLabel">
         <item name="android:textSize">@dimen/qs_tile_text_size</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+    </style>
+
+    <style name="TextAppearance.QS.TileLabel.Secondary">
+        <item name="android:textSize">@dimen/qs_tile_text_size</item>
         <item name="android:fontFamily">sans-serif</item>
     </style>
 
+    <style name="TextAppearance.QS.CarrierInfo">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <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 f409902..85e99f0 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -23,22 +23,27 @@
 typealias Privacy = PrivacyType
 
 enum class PrivacyType(val nameId: Int, val iconId: Int) {
-    TYPE_CAMERA(R.string.privacy_type_camera, com.android.internal.R.drawable.ic_camera),
+    TYPE_CAMERA(R.string.privacy_type_camera, R.drawable.stat_sys_camera),
     TYPE_LOCATION(R.string.privacy_type_location, R.drawable.stat_sys_location),
-    TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.ic_mic_26dp);
+    TYPE_MICROPHONE(R.string.privacy_type_microphone, R.drawable.stat_sys_mic_none);
 
     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/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index a7329b0..ff31b26 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 4357a51..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
@@ -231,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();
@@ -251,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();
@@ -358,7 +356,6 @@
 
     public void onMenuShown(View animView) {
         setExposedMenuView(getTranslatingParentView());
-        mSwipingInProgress = false;
         mCallback.onDragCancelled(animView);
         Handler handler = getHandler();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 0e6efc8..c84f3db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -66,6 +66,8 @@
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.UiOffloadThread;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -101,7 +103,8 @@
  */
 public class PhoneStatusBarPolicy implements Callback, Callbacks,
         RotationLockControllerCallback, Listener, LocationChangeCallback,
-        ZenModeController.Callback, DeviceProvisionedListener, KeyguardMonitor.Callback {
+        ZenModeController.Callback, DeviceProvisionedListener, KeyguardMonitor.Callback,
+        PrivacyItemController.Callback {
     private static final String TAG = "PhoneStatusBarPolicy";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -120,6 +123,8 @@
     private final String mSlotHeadset;
     private final String mSlotDataSaver;
     private final String mSlotLocation;
+    private final String mSlotMicrophone;
+    private final String mSlotCamera;
 
     private final Context mContext;
     private final Handler mHandler = new Handler();
@@ -136,6 +141,7 @@
     private final DeviceProvisionedController mProvisionedController;
     private final KeyguardMonitor mKeyguardMonitor;
     private final LocationController mLocationController;
+    private final PrivacyItemController mPrivacyItemController;
     private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>();
     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
 
@@ -169,6 +175,7 @@
         mProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
         mLocationController = Dependency.get(LocationController.class);
+        mPrivacyItemController = new PrivacyItemController(mContext, this);
 
         mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
         mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
@@ -183,6 +190,8 @@
         mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
         mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
         mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
+        mSlotMicrophone = context.getString(com.android.internal.R.string.status_bar_microphone);
+        mSlotCamera = context.getString(com.android.internal.R.string.status_bar_camera);
 
         // listen for broadcasts
         IntentFilter filter = new IntentFilter();
@@ -241,6 +250,12 @@
                 context.getString(R.string.accessibility_data_saver_on));
         mIconController.setIconVisibility(mSlotDataSaver, false);
 
+        // privacy items
+        mIconController.setIcon(mSlotMicrophone, R.drawable.stat_sys_mic_none, null);
+        mIconController.setIconVisibility(mSlotMicrophone, false);
+        mIconController.setIcon(mSlotCamera, R.drawable.stat_sys_camera, null);
+        mIconController.setIconVisibility(mSlotCamera, false);
+
         mRotationLockController.addCallback(this);
         mBluetooth.addCallback(this);
         mProvisionedController.addCallback(this);
@@ -251,6 +266,7 @@
         mDataSaver.addCallback(this);
         mKeyguardMonitor.addCallback(this);
         mLocationController.addCallback(this);
+        mPrivacyItemController.setListening(true);
 
         SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
@@ -279,6 +295,7 @@
         mDataSaver.removeCallback(this);
         mKeyguardMonitor.removeCallback(this);
         mLocationController.removeCallback(this);
+        mPrivacyItemController.setListening(false);
         SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
         mContext.unregisterReceiver(mIntentReceiver);
 
@@ -798,6 +815,34 @@
         mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
     }
 
+    @Override  // PrivacyItemController.Callback
+    public void privacyChanged(List<PrivacyItem> privacyItems) {
+        updatePrivacyItems(privacyItems);
+    }
+
+    private void updatePrivacyItems(List<PrivacyItem> items) {
+        boolean showCamera = false;
+        boolean showMicrophone = false;
+        boolean showLocation = false;
+        for (PrivacyItem item : items) {
+            switch (item.getPrivacyType()) {
+                case TYPE_CAMERA:
+                    showCamera = true;
+                    break;
+                case TYPE_LOCATION:
+                    showLocation = true;
+                    break;
+                case TYPE_MICROPHONE:
+                    showMicrophone = true;
+                    break;
+            }
+        }
+
+        mIconController.setIconVisibility(mSlotCamera, showCamera);
+        mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
+        mIconController.setIconVisibility(mSlotLocation, showLocation);
+    }
+
     private final TaskStackChangeListener mTaskListener = new TaskStackChangeListener() {
         @Override
         public void onTaskStackChanged() {
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/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 8ae5872..3e07d12 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -844,6 +844,8 @@
     //   PACKAGE: App that posted the notification
     // DETAIL: Notification is expanded by user.
     //   PACKAGE: App that posted the notification
+    // COLLAPSE: Notification is collapsed by user.
+    //   PACKAGE: App that posted the notification
     // DISMISS: Notification is dismissed.
     //   PACKAGE: App that posted the notification
     //   SUBTYPE: Dismiss reason from NotificationManagerService.java
@@ -6596,6 +6598,12 @@
     // OS: Q
     NOTIFICATION_ZEN_MODE_OVERRIDING_APP = 1589;
 
+    // ACTION: User sent a direct reply
+    //    PACKAGE: App that posted the notification
+    // CATEGORY: NOTIFICATION
+    // OS: Q
+    NOTIFICATION_DIRECT_REPLY_ACTION = 1590;
+
     // ---- End Q Constants, all Q constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 4205ac7..31238df 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -166,12 +166,12 @@
         context.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
     }
 
-    @Override // from MasterSystemService
+    @Override // from AbstractMasterSystemService
     protected String getServiceSettingsProperty() {
         return Settings.Secure.AUTOFILL_SERVICE;
     }
 
-    @Override // from MasterSystemService
+    @Override // from AbstractMasterSystemService
     protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
             @NonNull ContentObserver observer) {
         resolver.registerContentObserver(Settings.Global.getUriFor(
@@ -188,7 +188,7 @@
                 UserHandle.USER_ALL);
     }
 
-    @Override // from MasterSystemService
+    @Override // from AbstractMasterSystemService
     protected void onSettingsChanged(int userId, @NonNull String property) {
         switch (property) {
             case Settings.Global.AUTOFILL_LOGGING_LEVEL:
@@ -210,25 +210,24 @@
         }
     }
 
-    @Override // from MasterSystemService
-    protected AutofillManagerServiceImpl newServiceLocked(int resolvedUserId, boolean disabled) {
+    @Override // from AbstractMasterSystemService
+    protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
+            boolean disabled) {
         return new AutofillManagerServiceImpl(this, mLock, mRequestsHistory,
                 mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi, mAutofillCompatState,
                 disabled);
     }
 
-    @Override // MasterSystemService
-    protected AutofillManagerServiceImpl removeCachedServiceLocked(int userId) {
-        final AutofillManagerServiceImpl service = super.removeCachedServiceLocked(userId);
-        if (service != null) {
-            service.destroyLocked();
-            mAutofillCompatState.removeCompatibilityModeRequests(userId);
-        }
-        return service;
+    @Override // AbstractMasterSystemService
+    protected void onServiceRemoved(@NonNull AutofillManagerServiceImpl service,
+            @UserIdInt int userId) {
+        service.destroyLocked();
+        mAutofillCompatState.removeCompatibilityModeRequests(userId);
     }
 
-    @Override // from MasterSystemService
-    protected void onServiceEnabledLocked(@NonNull AutofillManagerServiceImpl service, int userId) {
+    @Override // from AbstractMasterSystemService
+    protected void onServiceEnabledLocked(@NonNull AutofillManagerServiceImpl service,
+            @UserIdInt int userId) {
         addCompatibilityModeRequestsLocked(service, userId);
     }
 
@@ -245,7 +244,7 @@
     }
 
     // Called by Shell command.
-    void destroySessions(int userId, IResultReceiver receiver) {
+    void destroySessions(@UserIdInt int userId, IResultReceiver receiver) {
         Slog.i(TAG, "destroySessions() for userId " + userId);
         getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
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/Android.bp b/services/core/Android.bp
index 888ad1d..6174300 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -3,6 +3,7 @@
 
     aidl: {
         include_dirs: [
+            "frameworks/base/cmds/idmap2/idmap2d/aidl",
             "frameworks/native/aidl/binder",
             "frameworks/native/cmds/dumpstate/binder",
             "system/core/storaged/binder",
@@ -13,6 +14,7 @@
     srcs: [
         "java/**/*.java",
         ":dumpstate_aidl",
+        ":idmap2_aidl",
         ":netd_aidl",
         ":netd_metrics_aidl",
         ":installd_aidl",
diff --git a/services/core/java/com/android/server/AbstractMasterSystemService.java b/services/core/java/com/android/server/AbstractMasterSystemService.java
index c955daf..6cae887 100644
--- a/services/core/java/com/android/server/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/AbstractMasterSystemService.java
@@ -244,7 +244,7 @@
      */
     @GuardedBy("mLock")
     @Nullable
-    protected S peekServiceForUserLocked(int userId) {
+    protected S peekServiceForUserLocked(@UserIdInt int userId) {
         final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                 Binder.getCallingUid(), userId, false, false, null, null);
         return mServicesCache.get(resolvedUserId);
@@ -254,7 +254,7 @@
      * Updates a cached service for a given user.
      */
     @GuardedBy("mLock")
-    protected void updateCachedServiceLocked(int userId) {
+    protected void updateCachedServiceLocked(@UserIdInt int userId) {
         updateCachedServiceLocked(userId, isDisabledLocked(userId));
     }
 
@@ -262,7 +262,7 @@
      * Checks whether the service is disabled (through {@link UserManager} restrictions) for the
      * given user.
      */
-    protected boolean isDisabledLocked(int userId) {
+    protected boolean isDisabledLocked(@UserIdInt int userId) {
         return mDisabledUsers == null ? false : mDisabledUsers.get(userId);
     }
 
@@ -274,7 +274,7 @@
      * @return service for the user.
      */
     @GuardedBy("mLock")
-    protected S updateCachedServiceLocked(int userId, boolean disabled) {
+    protected S updateCachedServiceLocked(@UserIdInt int userId, boolean disabled) {
         final S service = getServiceForUserLocked(userId);
         if (service != null) {
             service.updateLocked(disabled);
@@ -304,7 +304,7 @@
      * <p>By default doesn't do anything, but can be overridden by subclasses.
      */
     @SuppressWarnings("unused")
-    protected void onServiceEnabledLocked(S service, @UserIdInt int userId) {
+    protected void onServiceEnabledLocked(@NonNull S service, @UserIdInt int userId) {
     }
 
     /**
@@ -314,15 +314,23 @@
      */
     @GuardedBy("mLock")
     @NonNull
-    protected S removeCachedServiceLocked(@UserIdInt int userId) {
+    private S removeCachedServiceLocked(@UserIdInt int userId) {
         final S service = peekServiceForUserLocked(userId);
         if (service != null) {
             mServicesCache.delete(userId);
+            onServiceRemoved(service, userId);
         }
         return service;
     }
 
     /**
+     * Called after the service is removed from the cache.
+     */
+    @SuppressWarnings("unused")
+    protected void onServiceRemoved(@NonNull S service, @UserIdInt int userId) {
+    }
+
+    /**
      * Visits all services in the cache.
      */
     @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 5814064..fa98da5 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -80,6 +80,7 @@
 import android.util.TimeUtils;
 import android.util.Xml;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsCallback;
@@ -219,6 +220,7 @@
 
     SparseIntArray mProfileOwners;
 
+    @GuardedBy("this")
     private CheckOpsDelegate mCheckOpsDelegate;
 
     /**
@@ -1589,24 +1591,28 @@
     public int checkOperation(int code, int uid, String packageName) {
         final CheckOpsDelegate delegate;
         synchronized (this) {
-            if (mCheckOpsDelegate == null) {
-                return checkOperationImpl(code, uid, packageName);
-            }
             delegate = mCheckOpsDelegate;
         }
+        if (delegate == null) {
+            return checkOperationImpl(code, uid, packageName);
+        }
         return delegate.checkOperation(code, uid, packageName,
                     AppOpsService.this::checkOperationImpl);
     }
 
     private int checkOperationImpl(int code, int uid, String packageName) {
+        verifyIncomingUid(uid);
+        verifyIncomingOp(code);
+        String resolvedPackageName = resolvePackageName(uid, packageName);
+        if (resolvedPackageName == null) {
+            return AppOpsManager.MODE_IGNORED;
+        }
+        return checkOperationUnchecked(code, uid, resolvedPackageName);
+    }
+
+    private int checkOperationUnchecked(int code, int uid, String packageName) {
         synchronized (this) {
-            verifyIncomingUid(uid);
-            verifyIncomingOp(code);
-            String resolvedPackageName = resolvePackageName(uid, packageName);
-            if (resolvedPackageName == null) {
-                return AppOpsManager.MODE_IGNORED;
-            }
-            if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
+            if (isOpRestrictedLocked(uid, code, packageName)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             code = AppOpsManager.opToSwitch(code);
@@ -1615,7 +1621,7 @@
                     && uidState.opModes.indexOfKey(code) >= 0) {
                 return uidState.opModes.get(code);
             }
-            Op op = getOpLocked(code, uid, resolvedPackageName, false, true, false);
+            Op op = getOpLocked(code, uid, packageName, false, true, false);
             if (op == null) {
                 return AppOpsManager.opToDefaultMode(code);
             }
@@ -1627,31 +1633,31 @@
     public int checkAudioOperation(int code, int usage, int uid, String packageName) {
         final CheckOpsDelegate delegate;
         synchronized (this) {
-            if (mCheckOpsDelegate == null) {
-                return checkAudioOperationImpl(code, usage, uid, packageName);
-            }
             delegate = mCheckOpsDelegate;
         }
+        if (delegate == null) {
+            return checkAudioOperationImpl(code, usage, uid, packageName);
+        }
         return delegate.checkAudioOperation(code, usage, uid, packageName,
                 AppOpsService.this::checkAudioOperationImpl);
     }
 
     private int checkAudioOperationImpl(int code, int usage, int uid, String packageName) {
+        boolean suspended;
+        try {
+            suspended = isPackageSuspendedForUser(packageName, uid);
+        } catch (IllegalArgumentException ex) {
+            // Package not found.
+            suspended = false;
+        }
+
+        if (suspended) {
+            Slog.i(TAG, "Audio disabled for suspended package=" + packageName
+                    + " for uid=" + uid);
+            return AppOpsManager.MODE_IGNORED;
+        }
+
         synchronized (this) {
-            boolean suspended;
-            try {
-                suspended = isPackageSuspendedForUser(packageName, uid);
-            } catch (IllegalArgumentException ex) {
-                // Package not found.
-                suspended = false;
-            }
-
-            if (suspended) {
-                Slog.i(TAG, "Audio disabled for suspended package=" + packageName
-                        + " for uid=" + uid);
-                return AppOpsManager.MODE_IGNORED;
-            }
-
             final int mode = checkRestrictionLocked(code, usage, uid, packageName);
             if (mode != AppOpsManager.MODE_ALLOWED) {
                 return mode;
@@ -1754,11 +1760,11 @@
     public int noteOperation(int code, int uid, String packageName) {
         final CheckOpsDelegate delegate;
         synchronized (this) {
-            if (mCheckOpsDelegate == null) {
-                return noteOperationImpl(code, uid, packageName);
-            }
             delegate = mCheckOpsDelegate;
         }
+        if (delegate == null) {
+            return noteOperationImpl(code, uid, packageName);
+        }
         return delegate.noteOperation(code, uid, packageName,
                 AppOpsService.this::noteOperationImpl);
     }
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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 771d376..aa96082 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4820,6 +4820,10 @@
             String packageName, IBinder token, String resultWho,
             int requestCode, Intent[] intents, String[] resolvedTypes,
             int flags, Bundle bOptions, int userId) {
+
+        // NOTE: The service lock isn't held in this method because nothing in the method requires
+        // the service lock to be held.
+
         enforceNotIsolatedCaller("getIntentSender");
         // Refuse possible leaked file descriptors
         if (intents != null) {
@@ -4851,43 +4855,41 @@
             }
         }
 
-        synchronized(this) {
-            int callingUid = Binder.getCallingUid();
-            int origUserId = userId;
-            userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
-                    type == ActivityManager.INTENT_SENDER_BROADCAST,
-                    ALLOW_NON_FULL, "getIntentSender", null);
-            if (origUserId == UserHandle.USER_CURRENT) {
-                // We don't want to evaluate this until the pending intent is
-                // actually executed.  However, we do want to always do the
-                // security checking for it above.
-                userId = UserHandle.USER_CURRENT;
-            }
-            try {
-                if (callingUid != 0 && callingUid != SYSTEM_UID) {
-                    final int uid = AppGlobals.getPackageManager().getPackageUid(packageName,
-                            MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callingUid));
-                    if (!UserHandle.isSameApp(callingUid, uid)) {
-                        String msg = "Permission Denial: getIntentSender() from pid="
-                            + Binder.getCallingPid()
-                            + ", uid=" + Binder.getCallingUid()
-                            + ", (need uid=" + uid + ")"
-                            + " is not allowed to send as package " + packageName;
-                        Slog.w(TAG, msg);
-                        throw new SecurityException(msg);
-                    }
+        int callingUid = Binder.getCallingUid();
+        int origUserId = userId;
+        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
+                type == ActivityManager.INTENT_SENDER_BROADCAST,
+                ALLOW_NON_FULL, "getIntentSender", null);
+        if (origUserId == UserHandle.USER_CURRENT) {
+            // We don't want to evaluate this until the pending intent is
+            // actually executed.  However, we do want to always do the
+            // security checking for it above.
+            userId = UserHandle.USER_CURRENT;
+        }
+        try {
+            if (callingUid != 0 && callingUid != SYSTEM_UID) {
+                final int uid = AppGlobals.getPackageManager().getPackageUid(packageName,
+                        MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callingUid));
+                if (!UserHandle.isSameApp(callingUid, uid)) {
+                    String msg = "Permission Denial: getIntentSender() from pid="
+                        + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid()
+                        + ", (need uid=" + uid + ")"
+                        + " is not allowed to send as package " + packageName;
+                    Slog.w(TAG, msg);
+                    throw new SecurityException(msg);
                 }
+            }
 
-                if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
-                    return mAtmInternal.getIntentSender(type, packageName, callingUid, userId,
-                            token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
-                }
-                return mPendingIntentController.getIntentSender(type, packageName, callingUid,
-                        userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
-                        bOptions);
-            } catch (RemoteException e) {
-                throw new SecurityException(e);
+            if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
+                return mAtmInternal.getIntentSender(type, packageName, callingUid, userId,
+                        token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
             }
+            return mPendingIntentController.getIntentSender(type, packageName, callingUid,
+                    userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
+                    bOptions);
+        } catch (RemoteException e) {
+            throw new SecurityException(e);
         }
     }
 
@@ -7002,7 +7004,7 @@
         mCoreSettingsObserver = new CoreSettingsObserver(this);
         mActivityTaskManager.installSystemProviders();
         mDevelopmentSettingsObserver = new DevelopmentSettingsObserver();
-        GlobalSettingsToPropertiesMapper.start(mContext.getContentResolver());
+        SettingsToPropertiesMapper.start(mContext.getContentResolver());
 
         // Now that the settings provider is published we can consider sending
         // in a rescue party.
diff --git a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
deleted file mode 100644
index 1366c21..0000000
--- a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.am;
-
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Build;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Slog;
-import android.view.ThreadedRenderer;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-
-/**
- * Maps global system settings to system properties.
- * <p>The properties are dynamically updated when settings change.
- */
-class GlobalSettingsToPropertiesMapper {
-
-    private static final String TAG = "GlobalSettingsToPropertiesMapper";
-
-    // List mapping entries in the following format:
-    // {Settings.Global.SETTING_NAME, "system_property_name"}
-    // Important: Property being added should be whitelisted by SELinux policy or have one of the
-    // already whitelisted prefixes in system_server.te, e.g. sys.
-    private static final String[][] sGlobalSettingsMapping = new String[][] {
-        {Settings.Global.SYS_VDSO, "sys.vdso"},
-        {Settings.Global.FPS_DEVISOR, ThreadedRenderer.DEBUG_FPS_DIVISOR},
-        {Settings.Global.DISPLAY_PANEL_LPM, "sys.display_panel_lpm"},
-        {Settings.Global.SYS_UIDCPUPOWER, "sys.uidcpupower"},
-        {Settings.Global.SYS_TRACED, "sys.traced.enable_override"},
-    };
-
-
-    private final ContentResolver mContentResolver;
-    private final String[][] mGlobalSettingsMapping;
-
-    @VisibleForTesting
-    GlobalSettingsToPropertiesMapper(ContentResolver contentResolver,
-            String[][] globalSettingsMapping) {
-        mContentResolver = contentResolver;
-        mGlobalSettingsMapping = globalSettingsMapping;
-    }
-
-    void updatePropertiesFromGlobalSettings() {
-        for (String[] entry : mGlobalSettingsMapping) {
-            final String settingName = entry[0];
-            final String propName = entry[1];
-            Uri settingUri = Settings.Global.getUriFor(settingName);
-            Preconditions.checkNotNull(settingUri, "Setting " + settingName + " not found");
-            ContentObserver co = new ContentObserver(null) {
-                @Override
-                public void onChange(boolean selfChange) {
-                    updatePropertyFromSetting(settingName, propName);
-                }
-            };
-            updatePropertyFromSetting(settingName, propName);
-            mContentResolver.registerContentObserver(settingUri, false, co);
-        }
-    }
-
-    public static void start(ContentResolver contentResolver) {
-        new GlobalSettingsToPropertiesMapper(contentResolver, sGlobalSettingsMapping)
-                .updatePropertiesFromGlobalSettings();
-    }
-
-    private String getGlobalSetting(String name) {
-        return Settings.Global.getString(mContentResolver, name);
-    }
-
-    private void setProperty(String key, String value) {
-        // Check if need to clear the property
-        if (value == null) {
-            // It's impossible to remove system property, therefore we check previous value to
-            // avoid setting an empty string if the property wasn't set.
-            if (TextUtils.isEmpty(systemPropertiesGet(key))) {
-                return;
-            }
-            value = "";
-        }
-        try {
-            systemPropertiesSet(key, value);
-        } catch (Exception e) {
-            // Failure to set a property can be caused by SELinux denial. This usually indicates
-            // that the property wasn't whitelisted in sepolicy.
-            // No need to report it on all user devices, only on debug builds.
-            if (Build.IS_DEBUGGABLE) {
-                Slog.wtf(TAG, "Unable to set property " + key + " value '" + value + "'", e);
-            } else {
-                Slog.e(TAG, "Unable to set property " + key + " value '" + value + "'", e);
-            }
-        }
-    }
-
-    @VisibleForTesting
-    protected String systemPropertiesGet(String key) {
-        return SystemProperties.get(key);
-    }
-
-    @VisibleForTesting
-    protected void systemPropertiesSet(String key, String value) {
-        SystemProperties.set(key, value);
-    }
-
-    @VisibleForTesting
-    void updatePropertyFromSetting(String settingName, String propName) {
-        String settingValue = getGlobalSetting(settingName);
-        setProperty(propName, settingValue);
-    }
-}
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 79c98e5..5208ca5 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -27,4 +27,4 @@
 michaelwr@google.com
 narayan@google.com
 
-per-file GlobalSettingsToPropertiesMapper.java = omakoto@google.com, svetoslavganov@google.com, yamasani@google.com
+per-file SettingsToPropertiesMapper.java = omakoto@google.com, svetoslavganov@google.com, yamasani@google.com
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
new file mode 100644
index 0000000..a5848ca
--- /dev/null
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashSet;
+
+/**
+ * Maps system settings to system properties.
+ * <p>The properties are dynamically updated when settings change.
+ */
+class SettingsToPropertiesMapper {
+
+    private static final String TAG = "SettingsToPropertiesMapper";
+
+    private static final String SYSTEM_PROPERTY_PREFIX = "persist.device_config.";
+
+    private static final String RESET_PERFORMED_PROPERTY = "device_config.reset_performed";
+
+    private static final String RESET_RECORD_FILE_PATH =
+            "/data/server_configurable_flags/reset_flags";
+
+    private static final String SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX = "^[\\w\\.\\-@:]*$";
+
+    private static final String SYSTEM_PROPERTY_INVALID_SUBSTRING = "..";
+
+    private static final int SYSTEM_PROPERTY_MAX_LENGTH = 92;
+
+    // experiment flags added to Global.Settings(before new "Config" provider table is available)
+    // will be added under this category.
+    private static final String GLOBAL_SETTINGS_CATEGORY = "global_settings";
+
+    // Add the global setting you want to push to native level as experiment flag into this list.
+    //
+    // NOTE: please grant write permission system property prefix
+    // with format persist.experiment.[experiment_category_name]. in system_server.te and grant read
+    // permission in the corresponding .te file your feature belongs to.
+    @VisibleForTesting
+    static final String[] sGlobalSettings = new String[] {
+    };
+
+    @VisibleForTesting
+    static final String[] sDeviceConfigScopes = new String[] {
+    };
+
+    private final String[] mGlobalSettings;
+
+    private final String[] mDeviceConfigScopes;
+
+    private final ContentResolver mContentResolver;
+
+    @VisibleForTesting
+    protected SettingsToPropertiesMapper(ContentResolver contentResolver,
+            String[] globalSettings,
+            String[] deviceConfigScopes) {
+        mContentResolver = contentResolver;
+        mGlobalSettings = globalSettings;
+        mDeviceConfigScopes = deviceConfigScopes;
+    }
+
+    @VisibleForTesting
+    void updatePropertiesFromSettings() {
+        for (String globalSetting : mGlobalSettings) {
+            Uri settingUri = Settings.Global.getUriFor(globalSetting);
+            String propName = makePropertyName(GLOBAL_SETTINGS_CATEGORY, globalSetting);
+            if (settingUri == null) {
+                log("setting uri is null for globalSetting " + globalSetting);
+                continue;
+            }
+            if (propName == null) {
+                log("invalid prop name for globalSetting " + globalSetting);
+                continue;
+            }
+
+            ContentObserver co = new ContentObserver(null) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    updatePropertyFromSetting(globalSetting, propName, true);
+                }
+            };
+
+            // only updating on starting up when no native flags reset is performed during current
+            // booting.
+            if (!isNativeFlagsResetPerformed()) {
+                updatePropertyFromSetting(globalSetting, propName, true);
+            }
+            mContentResolver.registerContentObserver(settingUri, false, co);
+        }
+
+        // TODO: address sDeviceConfigScopes after DeviceConfig APIs are available.
+    }
+
+    public static SettingsToPropertiesMapper start(ContentResolver contentResolver) {
+        SettingsToPropertiesMapper mapper =  new SettingsToPropertiesMapper(
+                contentResolver, sGlobalSettings, sDeviceConfigScopes);
+        mapper.updatePropertiesFromSettings();
+        return mapper;
+    }
+
+    /**
+     * If native level flags reset has been performed as an attempt to recover from a crash loop
+     * during current device booting.
+     * @return
+     */
+    public boolean isNativeFlagsResetPerformed() {
+        String value = systemPropertiesGet(RESET_PERFORMED_PROPERTY);
+        return "true".equals(value);
+    }
+
+    /**
+     * return an array of native flag categories under which flags got reset during current device
+     * booting.
+     * @return
+     */
+    public String[] getResetNativeCategories() {
+        if (!isNativeFlagsResetPerformed()) {
+            return new String[0];
+        }
+
+        String content = getResetFlagsFileContent();
+        if (TextUtils.isEmpty(content)) {
+            return new String[0];
+        }
+
+        String[] property_names = content.split(";");
+        HashSet<String> categories = new HashSet<>();
+        for (String property_name : property_names) {
+            String[] segments = property_name.split("\\.");
+            if (segments.length < 3) {
+                log("failed to extract category name from property " + property_name);
+                continue;
+            }
+            categories.add(segments[2]);
+        }
+        return categories.toArray(new String[0]);
+    }
+
+    /**
+     * system property name constructing rule: "persist.device_config.[category_name].[flag_name]".
+     * If the name contains invalid characters or substrings for system property name,
+     * will return null.
+     * @param categoryName
+     * @param flagName
+     * @return
+     */
+    @VisibleForTesting
+    static String makePropertyName(String categoryName, String flagName) {
+        String propertyName = SYSTEM_PROPERTY_PREFIX + categoryName + "." + flagName;
+
+        if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX)
+                || propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) {
+            return null;
+        }
+
+        return propertyName;
+    }
+
+    private String getSetting(String name, boolean isGlobalSetting) {
+        if (isGlobalSetting) {
+            return Settings.Global.getString(mContentResolver, name);
+        } else {
+            // TODO: complete the code after DeviceConfig APIs implemented.
+            return null;
+        }
+    }
+
+    private void setProperty(String key, String value) {
+        // Check if need to clear the property
+        if (value == null) {
+            // It's impossible to remove system property, therefore we check previous value to
+            // avoid setting an empty string if the property wasn't set.
+            if (TextUtils.isEmpty(systemPropertiesGet(key))) {
+                return;
+            }
+            value = "";
+        } else if (value.length() > SYSTEM_PROPERTY_MAX_LENGTH) {
+            log(value + " exceeds system property max length.");
+            return;
+        }
+
+        try {
+            systemPropertiesSet(key, value);
+        } catch (Exception e) {
+            // Failure to set a property can be caused by SELinux denial. This usually indicates
+            // that the property wasn't whitelisted in sepolicy.
+            // No need to report it on all user devices, only on debug builds.
+            log("Unable to set property " + key + " value '" + value + "'", e);
+        }
+    }
+
+    private static void log(String msg, Exception e) {
+        if (Build.IS_DEBUGGABLE) {
+            Slog.wtf(TAG, msg, e);
+        } else {
+            Slog.e(TAG, msg, e);
+        }
+    }
+
+    private static void log(String msg) {
+        if (Build.IS_DEBUGGABLE) {
+            Slog.wtf(TAG, msg);
+        } else {
+            Slog.e(TAG, msg);
+        }
+    }
+
+    @VisibleForTesting
+    protected String systemPropertiesGet(String key) {
+        return SystemProperties.get(key);
+    }
+
+    @VisibleForTesting
+    protected void systemPropertiesSet(String key, String value) {
+        SystemProperties.set(key, value);
+    }
+
+    @VisibleForTesting
+    protected String getResetFlagsFileContent() {
+        String content = null;
+        try {
+            File reset_flag_file = new File(RESET_RECORD_FILE_PATH);
+            BufferedReader br = new BufferedReader(new FileReader(reset_flag_file));
+            content = br.readLine();
+
+            br.close();
+        } catch (IOException ioe) {
+            log("failed to read file " + RESET_RECORD_FILE_PATH, ioe);
+        }
+        return content;
+    }
+
+    @VisibleForTesting
+    void updatePropertyFromSetting(String settingName, String propName, boolean isGlobalSetting) {
+        String settingValue = getSetting(settingName, isGlobalSetting);
+        setProperty(propName, settingValue);
+    }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index c20079e..3a31c9c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -72,6 +72,8 @@
 import android.view.IInputFilterHost;
 import android.view.IWindow;
 import android.view.InputChannel;
+import android.view.InputApplicationHandle;
+import android.view.InputWindowHandle;
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.KeyEvent;
@@ -197,7 +199,7 @@
     private static native boolean nativeHasKeys(long ptr,
             int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
     private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,
-            InputWindowHandle inputWindowHandle, int displayId);
+            int displayId);
     private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
     private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
     private static native int nativeInjectInputEvent(long ptr, InputEvent event,
@@ -486,8 +488,7 @@
         }
 
         InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
-        // Register channel for monitor.
-        nativeRegisterInputChannel(mPtr, inputChannels[0], null, displayId);
+        nativeRegisterInputChannel(mPtr, inputChannels[0], displayId);
         inputChannels[0].dispose(); // don't need to retain the Java object reference
         return inputChannels[1];
     }
@@ -498,14 +499,17 @@
      * @param inputWindowHandle The handle of the input window associated with the
      * input channel, or null if none.
      */
-    public void registerInputChannel(InputChannel inputChannel,
-            InputWindowHandle inputWindowHandle) {
+    public void registerInputChannel(InputChannel inputChannel, IBinder token) {
         if (inputChannel == null) {
             throw new IllegalArgumentException("inputChannel must not be null.");
         }
 
-        // Register channel for normal.
-        nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, Display.INVALID_DISPLAY);
+        if (token == null) {
+            token = new Binder();
+        }
+        inputChannel.setToken(token);
+
+        nativeRegisterInputChannel(mPtr, inputChannel, Display.INVALID_DISPLAY);
     }
 
     /**
@@ -1791,15 +1795,15 @@
     }
 
     // Native callback.
-    private void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
-        mWindowManagerCallbacks.notifyInputChannelBroken(inputWindowHandle);
+    private void notifyInputChannelBroken(IBinder token) {
+        mWindowManagerCallbacks.notifyInputChannelBroken(token);
     }
 
     // Native callback.
     private long notifyANR(InputApplicationHandle inputApplicationHandle,
-            InputWindowHandle inputWindowHandle, String reason) {
+            IBinder token, String reason) {
         return mWindowManagerCallbacks.notifyANR(
-                inputApplicationHandle, inputWindowHandle, reason);
+                inputApplicationHandle, token, reason);
     }
 
     // Native callback.
@@ -1830,13 +1834,13 @@
     }
 
     // Native callback.
-    private long interceptKeyBeforeDispatching(InputWindowHandle focus,
+    private long interceptKeyBeforeDispatching(IBinder focus,
             KeyEvent event, int policyFlags) {
         return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
     }
 
     // Native callback.
-    private KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
+    private KeyEvent dispatchUnhandledKey(IBinder focus,
             KeyEvent event, int policyFlags) {
         return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
     }
@@ -1987,19 +1991,19 @@
 
         public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered);
 
-        public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle);
+        public void notifyInputChannelBroken(IBinder token);
 
         public long notifyANR(InputApplicationHandle inputApplicationHandle,
-                InputWindowHandle inputWindowHandle, String reason);
+                IBinder token, String reason);
 
         public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
 
         public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags);
 
-        public long interceptKeyBeforeDispatching(InputWindowHandle focus,
+        public long interceptKeyBeforeDispatching(IBinder token,
                 KeyEvent event, int policyFlags);
 
-        public KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
+        public KeyEvent dispatchUnhandledKey(IBinder token,
                 KeyEvent event, int policyFlags);
 
         public int getPointerLayer();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1c7572e..6195ed9 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,
@@ -885,6 +886,9 @@
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
                     r.recordDirectReplied();
+                    mMetricsLogger.write(r.getLogMaker()
+                            .setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
+                            .setType(MetricsEvent.TYPE_ACTION));
                     reportUserInteraction(r);
                 }
             }
@@ -1160,6 +1164,7 @@
                     mConditionProviders.onUserSwitched(userId);
                     mListeners.onUserSwitched(userId);
                     mZenModeHelper.onUserSwitched(userId);
+                    mPreferencesHelper.onUserSwitched(userId);
                 }
                 // assistant is the only thing that cares about managed profiles specifically
                 mAssistants.onUserSwitched(userId);
@@ -1188,6 +1193,7 @@
                     mConditionProviders.onUserUnlocked(userId);
                     mListeners.onUserUnlocked(userId);
                     mZenModeHelper.onUserUnlocked(userId);
+                    mPreferencesHelper.onUserUnlocked(userId);
                 }
             }
         }
@@ -1982,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) {
@@ -2525,6 +2531,19 @@
         }
 
         @Override
+        public int getAppsBypassingDndCount(int userId) {
+            checkCallerIsSystem();
+            return mPreferencesHelper.getAppsBypassingDndCount(userId);
+        }
+
+        @Override
+        public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(
+                String pkg, int userId) {
+            checkCallerIsSystem();
+            return mPreferencesHelper.getNotificationChannelsBypassingDnd(pkg, userId);
+        }
+
+        @Override
         public boolean areChannelsBypassingDnd() {
             return mPreferencesHelper.areChannelsBypassingDnd();
         }
@@ -4521,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();
@@ -5529,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);
@@ -5542,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);
             }
@@ -5559,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;
@@ -6561,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();
@@ -6592,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);
@@ -6607,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/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 8fce5e3..fd65ebe 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -111,7 +111,6 @@
     // pkg => PackagePreferences
     private final ArrayMap<String, PackagePreferences> mRestoredWithoutUids = new ArrayMap<>();
 
-
     private final Context mContext;
     private final PackageManager mPm;
     private final RankingHandler mRankingHandler;
@@ -120,7 +119,6 @@
     private SparseBooleanArray mBadgingEnabled;
     private boolean mAreChannelsBypassingDnd;
 
-
     public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
             ZenModeHelper zenHelper) {
         mContext = context;
@@ -129,11 +127,7 @@
         mPm = pm;
 
         updateBadgingEnabled();
-
-        mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state &
-                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
-        updateChannelsBypassingDnd();
-
+        syncChannelsBypassingDnd(mContext.getUserId());
     }
 
     public void readXml(XmlPullParser parser, boolean forRestore)
@@ -525,6 +519,7 @@
                 // but the system can
                 if (group.isBlocked() != oldGroup.isBlocked()) {
                     group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
+                    updateChannelsBypassingDnd(mContext.getUserId());
                 }
                 if (group.canOverlayApps() != oldGroup.canOverlayApps()) {
                     group.lockFields(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY);
@@ -571,6 +566,7 @@
 
             // Apps are allowed to downgrade channel importance if the user has not changed any
             // fields on this channel yet.
+            final int previousExistingImportance = existing.getImportance();
             if (existing.getUserLockedFields() == 0 &&
                     channel.getImportance() < existing.getImportance()) {
                 existing.setImportance(channel.getImportance());
@@ -582,8 +578,9 @@
                 boolean bypassDnd = channel.canBypassDnd();
                 existing.setBypassDnd(bypassDnd);
 
-                if (bypassDnd != mAreChannelsBypassingDnd) {
-                    updateChannelsBypassingDnd();
+                if (bypassDnd != mAreChannelsBypassingDnd
+                        || previousExistingImportance != existing.getImportance()) {
+                    updateChannelsBypassingDnd(mContext.getUserId());
                 }
             }
 
@@ -613,7 +610,7 @@
 
         r.channels.put(channel.getId(), channel);
         if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
-            updateChannelsBypassingDnd();
+            updateChannelsBypassingDnd(mContext.getUserId());
         }
         MetricsLogger.action(getChannelLog(channel, pkg).setType(
                 com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
@@ -663,8 +660,9 @@
             MetricsLogger.action(getChannelLog(updatedChannel, pkg));
         }
 
-        if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd) {
-            updateChannelsBypassingDnd();
+        if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
+                || channel.getImportance() != updatedChannel.getImportance()) {
+            updateChannelsBypassingDnd(mContext.getUserId());
         }
         updateConfig();
     }
@@ -701,7 +699,7 @@
             MetricsLogger.action(lm);
 
             if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
-                updateChannelsBypassingDnd();
+                updateChannelsBypassingDnd(mContext.getUserId());
             }
         }
     }
@@ -859,6 +857,27 @@
     }
 
     /**
+     * Gets all notification channels associated with the given pkg and userId that can bypass dnd
+     */
+    public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg,
+            int userId) {
+        List<NotificationChannel> channels = new ArrayList<>();
+        synchronized (mPackagePreferences) {
+            final PackagePreferences r = mPackagePreferences.get(
+                    packagePreferencesKey(pkg, userId));
+            // notifications from this package aren't blocked
+            if (r != null && r.importance != IMPORTANCE_NONE) {
+                for (NotificationChannel channel : r.channels.values()) {
+                    if (channelIsLive(r, channel) && channel.canBypassDnd()) {
+                        channels.add(channel);
+                    }
+                }
+            }
+        }
+        return new ParceledListSlice<>(channels);
+    }
+
+    /**
      * True for pre-O apps that only have the default channel, or pre O apps that have no
      * channels yet. This method will create the default channel for pre-O apps that don't have it.
      * Should never be true for O+ targeting apps, but that's enforced on boot/when an app
@@ -922,18 +941,62 @@
         return count;
     }
 
-    public void updateChannelsBypassingDnd() {
+    /**
+     * Returns the number of apps that have at least one notification channel that can bypass DND
+     * for given particular user
+     */
+    public int getAppsBypassingDndCount(int userId) {
+        int count = 0;
         synchronized (mPackagePreferences) {
-            final int numPackagePreferencess = mPackagePreferences.size();
-            for (int PackagePreferencesIndex = 0; PackagePreferencesIndex < numPackagePreferencess;
-                    PackagePreferencesIndex++) {
-                final PackagePreferences r = mPackagePreferences.valueAt(PackagePreferencesIndex);
-                final int numChannels = r.channels.size();
+            final int numPackagePreferences = mPackagePreferences.size();
+            for (int i = 0; i < numPackagePreferences; i++) {
+                final PackagePreferences r = mPackagePreferences.valueAt(i);
+                // Package isn't associated with this userId or notifications from this package are
+                // blocked
+                if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
+                    continue;
+                }
 
-                for (int channelIndex = 0; channelIndex < numChannels; channelIndex++) {
-                    NotificationChannel channel = r.channels.valueAt(channelIndex);
-                    if (!channel.isDeleted() && channel.canBypassDnd()) {
-                        // If any channel bypasses DND, synchronize state and return early.
+                for (NotificationChannel channel : r.channels.values()) {
+                    if (channelIsLive(r, channel) && channel.canBypassDnd()) {
+                        count++;
+                        break;
+                    }
+                }
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before
+     * updating
+     * @param userId
+     */
+    private void syncChannelsBypassingDnd(int userId) {
+        mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state
+                & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1;
+        updateChannelsBypassingDnd(userId);
+    }
+
+    /**
+     * Updates the user's NotificationPolicy based on whether the given userId
+     * has channels bypassing DND
+     * @param userId
+     */
+    private void updateChannelsBypassingDnd(int userId) {
+        synchronized (mPackagePreferences) {
+            final int numPackagePreferences = mPackagePreferences.size();
+            for (int i = 0; i < numPackagePreferences; i++) {
+                final PackagePreferences r = mPackagePreferences.valueAt(i);
+                // Package isn't associated with this userId or notifications from this package are
+                // blocked
+                if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) {
+                    continue;
+                }
+
+                for (NotificationChannel channel : r.channels.values()) {
+                    if (channelIsLive(r, channel) && channel.canBypassDnd()) {
                         if (!mAreChannelsBypassingDnd) {
                             mAreChannelsBypassingDnd = true;
                             updateZenPolicy(true);
@@ -943,7 +1006,6 @@
                 }
             }
         }
-
         // If no channels bypass DND, update the zen policy once to disable DND bypass.
         if (mAreChannelsBypassingDnd) {
             mAreChannelsBypassingDnd = false;
@@ -951,6 +1013,22 @@
         }
     }
 
+    private boolean channelIsLive(PackagePreferences pkgPref, NotificationChannel channel) {
+        // Channel is in a group that's blocked
+        if (!TextUtils.isEmpty(channel.getGroup())) {
+            if (pkgPref.groups.get(channel.getGroup()).isBlocked()) {
+                return false;
+            }
+        }
+
+        // Channel is deleted or is blocked
+        if (channel.isDeleted() || channel.getImportance() == IMPORTANCE_NONE) {
+            return false;
+        }
+
+        return true;
+    }
+
     public void updateZenPolicy(boolean areChannelsBypassingDnd) {
         NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
         mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
@@ -1329,6 +1407,20 @@
         return packageChannels;
     }
 
+    /**
+     * Called when user switches
+     */
+    public void onUserSwitched(int userId) {
+        syncChannelsBypassingDnd(userId);
+    }
+
+    /**
+     * Called when user is unlocked
+     */
+    public void onUserUnlocked(int userId) {
+        syncChannelsBypassingDnd(userId);
+    }
+
     public void onUserRemoved(int userId) {
         synchronized (mPackagePreferences) {
             int N = mPackagePreferences.size();
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 807c343..731e6bc 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -16,36 +16,46 @@
 
 package com.android.server.om;
 
+import static android.content.Context.IDMAP_SERVICE;
+import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+
 import static com.android.server.om.OverlayManagerService.DEBUG;
 import static com.android.server.om.OverlayManagerService.TAG;
 
 import android.annotation.NonNull;
 import android.content.om.OverlayInfo;
 import android.content.pm.PackageInfo;
+import android.os.IBinder;
+import android.os.IIdmap2;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.Slog;
 
-import com.android.server.pm.Installer.InstallerException;
+import com.android.internal.os.BackgroundThread;
 import com.android.server.pm.Installer;
 
-import java.io.DataInputStream;
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
 
 /**
  * Handle the creation and deletion of idmap files.
  *
  * The actual work is performed by the idmap binary, launched through Installer
- * and installd.
+ * and installd (or idmap2).
  *
  * Note: this class is subclassed in the OMS unit tests, and hence not marked as final.
  */
 class IdmapManager {
+    private static final boolean FEATURE_FLAG_IDMAP2 = false;
+
     private final Installer mInstaller;
+    private IIdmap2 mIdmap2Service;
 
     IdmapManager(final Installer installer) {
         mInstaller = installer;
+        if (FEATURE_FLAG_IDMAP2) {
+            connectToIdmap2d();
+        }
     }
 
     boolean createIdmap(@NonNull final PackageInfo targetPackage,
@@ -59,8 +69,12 @@
         final String targetPath = targetPackage.applicationInfo.getBaseCodePath();
         final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
         try {
-            mInstaller.idmap(targetPath, overlayPath, sharedGid);
-        } catch (InstallerException e) {
+            if (FEATURE_FLAG_IDMAP2) {
+                mIdmap2Service.createIdmap(targetPath, overlayPath, userId);
+            } else {
+                mInstaller.idmap(targetPath, overlayPath, sharedGid);
+            }
+        } catch (Exception e) {
             Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
                     + overlayPath + ": " + e.getMessage());
             return false;
@@ -69,13 +83,16 @@
     }
 
     boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
-        // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
         if (DEBUG) {
             Slog.d(TAG, "remove idmap for " + oi.baseCodePath);
         }
         try {
-            mInstaller.removeIdmap(oi.baseCodePath);
-        } catch (InstallerException e) {
+            if (FEATURE_FLAG_IDMAP2) {
+                mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
+            } else {
+                mInstaller.removeIdmap(oi.baseCodePath);
+            }
+        } catch (Exception e) {
             Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage());
             return false;
         }
@@ -83,19 +100,58 @@
     }
 
     boolean idmapExists(@NonNull final OverlayInfo oi) {
-        // unused OverlayInfo.userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
-        return new File(getIdmapPath(oi.baseCodePath)).isFile();
+        return new File(getIdmapPath(oi.baseCodePath, oi.userId)).isFile();
     }
 
     boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) {
-        // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
-        return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath())).isFile();
+        return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath(), userId))
+            .isFile();
     }
 
-    private String getIdmapPath(@NonNull final String baseCodePath) {
-        final StringBuilder sb = new StringBuilder("/data/resource-cache/");
-        sb.append(baseCodePath.substring(1).replace('/', '@'));
-        sb.append("@idmap");
-        return sb.toString();
+    private @NonNull String getIdmapPath(@NonNull final String overlayPackagePath,
+            final int userId) {
+        if (FEATURE_FLAG_IDMAP2) {
+            try {
+                return mIdmap2Service.getIdmapPath(overlayPackagePath, userId);
+            } catch (Exception e) {
+                Slog.w(TAG, "failed to get idmap path for " + overlayPackagePath + ": "
+                        + e.getMessage());
+                return "";
+            }
+        } else {
+            final StringBuilder sb = new StringBuilder("/data/resource-cache/");
+            sb.append(overlayPackagePath.substring(1).replace('/', '@'));
+            sb.append("@idmap");
+            return sb.toString();
+        }
+    }
+
+    private void connectToIdmap2d() {
+        IBinder binder = ServiceManager.getService(IDMAP_SERVICE);
+        if (binder != null) {
+            try {
+                binder.linkToDeath(new IBinder.DeathRecipient() {
+                    @Override
+                    public void binderDied() {
+                        Slog.w(TAG, "service '" + IDMAP_SERVICE + "' died; reconnecting...");
+                        connectToIdmap2d();
+                    }
+
+                }, 0);
+            } catch (RemoteException e) {
+                binder = null;
+            }
+        }
+        if (binder != null) {
+            mIdmap2Service = IIdmap2.Stub.asInterface(binder);
+            if (DEBUG) {
+                Slog.d(TAG, "service '" + IDMAP_SERVICE + "' connected");
+            }
+        } else {
+            Slog.w(TAG, "service '" + IDMAP_SERVICE + "' not found; trying again...");
+            BackgroundThread.getHandler().postDelayed(() -> {
+                connectToIdmap2d();
+            }, SECOND_IN_MILLIS);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 36bf83d..572d368 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -179,19 +179,13 @@
 
     List<OverlayInfo> getOverlaysForTarget(@NonNull final String targetPackageName,
             final int userId) {
-        // Static RROs targeting "android" are loaded from AssetManager, and so they should be
-        // ignored in OverlayManagerService.
         return selectWhereTarget(targetPackageName, userId)
-                .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName())))
                 .map(SettingsItem::getOverlayInfo)
                 .collect(Collectors.toList());
     }
 
     ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
-        // Static RROs targeting "android" are loaded from AssetManager, and so they should be
-        // ignored in OverlayManagerService.
         return selectWhereUser(userId)
-                .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName())))
                 .map(SettingsItem::getOverlayInfo)
                 .collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new,
                         Collectors.toList()));
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 275f3dc..b490381 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -362,7 +362,7 @@
         }
 
         private static boolean shouldShowHiddenApp(ApplicationInfo appInfo) {
-            if (appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
+            if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
                 return false;
             }
             return true;
diff --git a/services/core/java/com/android/server/role/RemoteRoleControllerService.java b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
index b670291..7d34270 100644
--- a/services/core/java/com/android/server/role/RemoteRoleControllerService.java
+++ b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
@@ -16,10 +16,10 @@
 
 package com.android.server.role;
 
-import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
 import android.app.role.IRoleManagerCallback;
 import android.app.role.RoleManagerCallback;
 import android.content.ComponentName;
@@ -34,6 +34,7 @@
 import android.rolecontrollerservice.RoleControllerService;
 import android.util.Slog;
 
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.util.ArrayDeque;
@@ -44,9 +45,13 @@
  */
 public class RemoteRoleControllerService {
 
+    static final boolean DEBUG = false;
     private static final String LOG_TAG = RemoteRoleControllerService.class.getSimpleName();
 
     @NonNull
+    private static final Handler sCallbackHandler = BackgroundThread.getHandler();
+
+    @NonNull
     private final Connection mConnection;
 
     public RemoteRoleControllerService(@UserIdInt int userId, @NonNull Context context) {
@@ -87,6 +92,16 @@
                 service.onClearRoleHolders(roleName, callbackDelegate), callback));
     }
 
+    /**
+     * Performs granting of default roles and permissions and appops
+     *
+     * @see RoleControllerService#onGrantDefaultRoles(RoleManagerCallback)
+     */
+    public void onGrantDefaultRoles(@NonNull IRoleManagerCallback callback) {
+        mConnection.enqueueCall(
+                new Connection.Call(IRoleControllerService::onGrantDefaultRoles, callback));
+    }
+
     private static final class Connection implements ServiceConnection {
 
         private static final long UNBIND_DELAY_MILLIS = 15 * 1000;
@@ -106,9 +121,6 @@
         private final Queue<Call> mPendingCalls = new ArrayDeque<>();
 
         @NonNull
-        private final Handler mMainHandler = Handler.getMain();
-
-        @NonNull
         private final Runnable mUnbindRunnable = this::unbind;
 
         Connection(@UserIdInt int userId, @NonNull Context context) {
@@ -116,14 +128,14 @@
             mContext = context;
         }
 
-        @MainThread
         @Override
+        @WorkerThread
         public void onServiceConnected(@NonNull ComponentName name, @NonNull IBinder service) {
             mService = IRoleControllerService.Stub.asInterface(service);
             executePendingCalls();
         }
 
-        @MainThread
+        @WorkerThread
         private void executePendingCalls() {
             while (!mPendingCalls.isEmpty()) {
                 Call call = mPendingCalls.poll();
@@ -132,26 +144,33 @@
             scheduleUnbind();
         }
 
-        @MainThread
         @Override
+        @WorkerThread
         public void onServiceDisconnected(@NonNull ComponentName name) {
             mService = null;
         }
 
-        @MainThread
         @Override
+        @WorkerThread
         public void onBindingDied(@NonNull ComponentName name) {
             unbind();
         }
 
         public void enqueueCall(@NonNull Call call) {
-            mMainHandler.post(PooledLambda.obtainRunnable(this::executeCall, call));
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "Enqueue " + call);
+            }
+            sCallbackHandler.executeOrSendMessage(PooledLambda.obtainMessage(
+                    Connection::executeCall, this, call));
         }
 
-        @MainThread
+        @WorkerThread
         private void executeCall(@NonNull Call call) {
             ensureBound();
             if (mService == null) {
+                if (DEBUG) {
+                    Slog.i(LOG_TAG, "Delaying until service connected: " + call);
+                }
                 mPendingCalls.offer(call);
                 return;
             }
@@ -159,24 +178,28 @@
             scheduleUnbind();
         }
 
-        @MainThread
+        @WorkerThread
         private void ensureBound() {
-            mMainHandler.removeCallbacks(mUnbindRunnable);
+            sCallbackHandler.removeCallbacks(mUnbindRunnable);
             if (!mBound) {
                 Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE);
                 intent.setPackage(mContext.getPackageManager()
                         .getPermissionControllerPackageName());
+                // Use direct handler to ensure onServiceConnected callback happens in the same
+                // call frame, as required by onGrantDefaultRoles
+                //
+                // Note that as a result, onServiceConnected may happen not on main thread!
                 mBound = mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
-                        UserHandle.of(mUserId));
+                        sCallbackHandler, UserHandle.of(mUserId));
             }
         }
 
         private void scheduleUnbind() {
-            mMainHandler.removeCallbacks(mUnbindRunnable);
-            mMainHandler.postDelayed(mUnbindRunnable, UNBIND_DELAY_MILLIS);
+            sCallbackHandler.removeCallbacks(mUnbindRunnable);
+            sCallbackHandler.postDelayed(mUnbindRunnable, UNBIND_DELAY_MILLIS);
         }
 
-        @MainThread
+        @WorkerThread
         private void unbind() {
             if (mBound) {
                 mService = null;
@@ -196,9 +219,6 @@
             private final IRoleManagerCallback mCallback;
 
             @NonNull
-            private final Handler mMainHandler = Handler.getMain();
-
-            @NonNull
             private final Runnable mTimeoutRunnable = () -> notifyCallback(false);
 
             private boolean mCallbackNotified;
@@ -209,10 +229,13 @@
                 mCallback = callback;
             }
 
-            @MainThread
+            @WorkerThread
             public void execute(IRoleControllerService service) {
+                if (DEBUG) {
+                    Slog.i(LOG_TAG, "Executing " + this);
+                }
                 try {
-                    mMainHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MILLIS);
+                    sCallbackHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MILLIS);
                     mCallExecutor.execute(service, new CallbackDelegate());
                 } catch (RemoteException e) {
                     Slog.e(LOG_TAG, "Error calling RoleControllerService", e);
@@ -220,13 +243,13 @@
                 }
             }
 
-            @MainThread
+            @WorkerThread
             private void notifyCallback(boolean success) {
                 if (mCallbackNotified) {
                     return;
                 }
                 mCallbackNotified = true;
-                mMainHandler.removeCallbacks(mTimeoutRunnable);
+                sCallbackHandler.removeCallbacks(mTimeoutRunnable);
                 try {
                     if (success) {
                         mCallback.onSuccess();
@@ -239,10 +262,15 @@
                 }
             }
 
+            @Override
+            public String toString() {
+                return "Call with callback: " + mCallback;
+            }
+
             @FunctionalInterface
             public interface CallExecutor {
 
-                @MainThread
+                @WorkerThread
                 void execute(IRoleControllerService service, IRoleManagerCallback callbackDelegate)
                         throws RemoteException;
             }
@@ -251,13 +279,14 @@
 
                 @Override
                 public void onSuccess() throws RemoteException {
-                    mMainHandler.post(PooledLambda.obtainRunnable(Call.this::notifyCallback, true));
+                    sCallbackHandler.sendMessage(PooledLambda.obtainMessage(
+                            Call::notifyCallback, Call.this, true));
                 }
 
                 @Override
                 public void onFailure() throws RemoteException {
-                    mMainHandler.post(PooledLambda.obtainRunnable(Call.this::notifyCallback,
-                            false));
+                    sCallbackHandler.sendMessage(PooledLambda.obtainMessage(
+                            Call::notifyCallback, Call.this, false));
                 }
             }
         }
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index ded075d..d01e762 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -45,6 +45,10 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Service for role management.
@@ -105,17 +109,37 @@
     public void onStart() {
         publishBinderService(Context.ROLE_SERVICE, new Stub());
         //TODO add watch for new user creation and run default grants for them
+        //TODO add package update watch to detect PermissionController upgrade and run def. grants
     }
 
     @Override
     public void onStartUser(@UserIdInt int userId) {
         synchronized (mLock) {
             //TODO only call into PermissionController if it or system upgreaded (for boot time)
-            // (add package changes watch;
-            //     we can detect upgrade using build fingerprint and app version)
             getUserStateLocked(userId);
-            //TODO call permission grant policy here
+        }
+        //TODO consider calling grants only when certain conditions are met
+        // such as OS or PermissionController upgrade
+        if (RemoteRoleControllerService.DEBUG) {
             Slog.i(LOG_TAG, "Granting default permissions...");
+            CompletableFuture<Void> result = new CompletableFuture<>();
+            getControllerService(userId).onGrantDefaultRoles(
+                    new IRoleManagerCallback.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            result.complete(null);
+                        }
+
+                        @Override
+                        public void onFailure() {
+                            result.completeExceptionally(new RuntimeException());
+                        }
+                    });
+            try {
+                result.get(5, TimeUnit.SECONDS);
+            } catch (InterruptedException | ExecutionException | TimeoutException e) {
+                Slog.e(LOG_TAG, "Failed to grant defaults for user " + userId, e);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 70220d8..3050409 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -47,6 +47,7 @@
 import android.net.NetworkStats;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.WifiActivityEnergyInfo;
+import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
 import android.os.Bundle;
@@ -87,6 +88,8 @@
 import com.android.internal.app.procstats.IProcessStats;
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.os.BinderCallsStats.ExportedCallStat;
 import com.android.internal.os.KernelCpuSpeedReader;
 import com.android.internal.os.KernelCpuThreadReader;
@@ -205,6 +208,10 @@
     @Nullable
     private final KernelCpuThreadReader mKernelCpuThreadReader;
 
+    private BatteryStatsHelper mBatteryStatsHelper = null;
+    private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
+    private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS;
+
     private static IThermalService sThermalService;
     private File mBaseDir =
             new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
@@ -1449,6 +1456,73 @@
         pulledData.add(e);
     }
 
+    private BatteryStatsHelper getBatteryStatsHelper() {
+        if (mBatteryStatsHelper == null) {
+            final long callingToken = Binder.clearCallingIdentity();
+            try {
+                // clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly().
+                mBatteryStatsHelper = new BatteryStatsHelper(mContext, false);
+            } finally {
+                Binder.restoreCallingIdentity(callingToken);
+            }
+            mBatteryStatsHelper.create((Bundle) null);
+        }
+        long currentTime = SystemClock.elapsedRealtime();
+        if (currentTime - mBatteryStatsHelperTimestampMs >= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) {
+            // Load BatteryStats and do all the calculations.
+            mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.USER_ALL);
+            // Calculations are done so we don't need to save the raw BatteryStats data in RAM.
+            mBatteryStatsHelper.clearStats();
+            mBatteryStatsHelperTimestampMs = currentTime;
+        }
+        return mBatteryStatsHelper;
+    }
+
+    private void pullDeviceCalculatedPowerUse(int tagId,
+            long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+        BatteryStatsHelper bsHelper = getBatteryStatsHelper();
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+        e.writeFloat((float) bsHelper.getComputedPower());
+        pulledData.add(e);
+    }
+
+    private void pullDeviceCalculatedPowerBlameUid(int tagId,
+            long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+        final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+        if (sippers == null) {
+            return;
+        }
+        for (BatterySipper bs : sippers) {
+            if (bs.drainType != bs.drainType.APP) {
+                continue;
+            }
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+            e.writeInt(bs.uidObj.getUid());
+            e.writeFloat((float) bs.totalPowerMah);
+            pulledData.add(e);
+        }
+    }
+
+    private void pullDeviceCalculatedPowerBlameOther(int tagId,
+            long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+        final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+        if (sippers == null) {
+            return;
+        }
+        for (BatterySipper bs : sippers) {
+            if (bs.drainType == bs.drainType.APP) {
+                continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid().
+            }
+            if (bs.drainType == bs.drainType.USER) {
+                continue; // This is not supported. We purposefully calculate over USER_ALL.
+            }
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+            e.writeInt(bs.drainType.ordinal());
+            e.writeFloat((float) bs.totalPowerMah);
+            pulledData.add(e);
+        }
+    }
+
     private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
         mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
@@ -1674,6 +1748,18 @@
                 pullCpuTimePerThreadFreq(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.DEVICE_CALCULATED_POWER_USE: {
+                pullDeviceCalculatedPowerUse(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
+            case StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID: {
+                pullDeviceCalculatedPowerBlameUid(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
+            case StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER: {
+                pullDeviceCalculatedPowerBlameOther(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 111c921..92944a0 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -95,6 +95,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
 import android.view.IApplicationToken;
+import android.view.InputApplicationHandle;
 import android.view.RemoteAnimationDefinition;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
@@ -105,7 +106,6 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
-import com.android.server.input.InputApplicationHandle;
 import com.android.server.policy.WindowManagerPolicy.StartingSurface;
 import com.android.server.wm.WindowManagerService.H;
 
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index ce8c979..7ed078a 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -36,7 +36,7 @@
 import android.view.View;
 
 import com.android.internal.util.Preconditions;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputWindowHandle;
 import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
 import java.util.concurrent.atomic.AtomicReference;
 
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 5483602..a379266 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -57,8 +57,8 @@
 
 import com.android.internal.view.IDragAndDropPermissions;
 import com.android.server.LocalServices;
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputApplicationHandle;
+import android.view.InputWindowHandle;
 
 import java.util.ArrayList;
 
@@ -223,7 +223,7 @@
             mDragApplicationHandle.dispatchingTimeoutNanos =
                     WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
 
-            mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
+            mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
                     display.getDisplayId());
             mDragWindowHandle.name = "drag";
             mDragWindowHandle.inputChannel = mServerChannel;
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 585a4f5..49bedc9 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -23,8 +23,8 @@
 import android.view.InputChannel;
 import android.view.WindowManager;
 
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputApplicationHandle;
+import android.view.InputWindowHandle;
 
 import java.io.PrintWriter;
 
@@ -63,7 +63,7 @@
         mApplicationHandle.dispatchingTimeoutNanos =
                 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
 
-        mWindowHandle = new InputWindowHandle(mApplicationHandle, null, null, displayId);
+        mWindowHandle = new InputWindowHandle(mApplicationHandle, null, displayId);
         mWindowHandle.name = name;
         mWindowHandle.inputChannel = mServerChannel;
         mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index f823caa..92ea1a9 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -8,16 +8,19 @@
 
 import android.app.ActivityManager;
 import android.os.Debug;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 
-import com.android.server.input.InputApplicationHandle;
+import android.view.InputApplicationHandle;
 import com.android.server.input.InputManagerService;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputWindowHandle;
+import android.view.InputChannel;
 
 import java.io.PrintWriter;
+import java.util.HashMap;
 
 final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
     private final WindowManagerService mService;
@@ -48,13 +51,13 @@
      * Called by the InputManager.
      */
     @Override
-    public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
-        if (inputWindowHandle == null) {
+    public void notifyInputChannelBroken(IBinder token) {
+        if (token == null) {
             return;
         }
 
         synchronized (mService.mGlobalLock) {
-            WindowState windowState = (WindowState) inputWindowHandle.windowState;
+            WindowState windowState = mService.windowForClientLocked(null, token, false);
             if (windowState != null) {
                 Slog.i(TAG_WM, "WINDOW DIED " + windowState);
                 windowState.removeIfPossible();
@@ -70,13 +73,13 @@
      */
     @Override
     public long notifyANR(InputApplicationHandle inputApplicationHandle,
-            InputWindowHandle inputWindowHandle, String reason) {
+            IBinder token, String reason) {
         AppWindowToken appWindowToken = null;
         WindowState windowState = null;
         boolean aboveSystem = false;
         synchronized (mService.mGlobalLock) {
-            if (inputWindowHandle != null) {
-                windowState = (WindowState) inputWindowHandle.windowState;
+            if (token != null) {
+                windowState = mService.windowForClientLocked(null, token, false);
                 if (windowState != null) {
                     appWindowToken = windowState.mAppToken;
                 }
@@ -188,8 +191,8 @@
      */
     @Override
     public long interceptKeyBeforeDispatching(
-            InputWindowHandle focus, KeyEvent event, int policyFlags) {
-        WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
+            IBinder focus, KeyEvent event, int policyFlags) {
+        WindowState windowState = mService.windowForClientLocked(null, focus, false);
         return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
     }
 
@@ -199,8 +202,8 @@
      */
     @Override
     public KeyEvent dispatchUnhandledKey(
-            InputWindowHandle focus, KeyEvent event, int policyFlags) {
-        WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
+            IBinder focus, KeyEvent event, int policyFlags) {
+        WindowState windowState = mService.windowForClientLocked(null, focus, false);
         return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
     }
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 0e4ab53..61bc4e4 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -42,9 +42,12 @@
 import android.util.Slog;
 import android.view.InputChannel;
 import android.view.InputEventReceiver;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+import android.view.InputApplicationHandle;
+import android.view.InputWindowHandle;
 
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.input.InputWindowHandle;
+import com.android.server.input.InputManagerService;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index c4fbee9..6627c2d 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -43,6 +43,7 @@
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
+import android.view.InputWindowHandle;
 import android.view.IRecentsAnimationController;
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationTarget;
@@ -50,7 +51,6 @@
 import android.view.SurfaceControl.Transaction;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
-import com.android.server.input.InputWindowHandle;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 import com.android.server.wm.utils.InsetUtils;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 66ebc9b..7182ad6 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -49,8 +49,9 @@
 import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputApplicationHandle;
+import android.view.InputWindowHandle;
+import com.android.server.wm.WindowManagerService.H;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -265,7 +266,7 @@
         mDragApplicationHandle.dispatchingTimeoutNanos =
                 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
 
-        mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
+        mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
                 display.getDisplayId());
         mDragWindowHandle.name = TAG;
         mDragWindowHandle.inputChannel = mServerChannel;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index f2d3dca..51567a0 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -29,7 +29,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.input.InputManagerService;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputWindowHandle;
 
 /**
  * Controller for task positioning by drag.
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2b5076a..a117cf3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -191,7 +191,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
-import com.android.server.input.InputWindowHandle;
+import android.view.InputWindowHandle;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 import com.android.server.wm.utils.InsetUtils;
@@ -718,7 +718,7 @@
         mLastRequestedHeight = 0;
         mLayer = 0;
         mInputWindowHandle = new InputWindowHandle(
-                mAppToken != null ? mAppToken.mInputApplicationHandle : null, this, c,
+                mAppToken != null ? mAppToken.mInputApplicationHandle : null, c,
                     getDisplayId());
     }
 
@@ -2047,7 +2047,7 @@
             // Create dummy event receiver that simply reports all events as handled.
             mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
         }
-        mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
+        mService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
     }
 
     void disposeInputChannel() {
@@ -2059,6 +2059,7 @@
         // unregister server channel first otherwise it complains about broken channel
         if (mInputChannel != null) {
             mService.mInputManager.unregisterInputChannel(mInputChannel);
+
             mInputChannel.dispose();
             mInputChannel = null;
         }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 3943dba..e2db807 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -42,6 +42,8 @@
 #include <utils/Trace.h>
 #include <utils/SortedVector.h>
 
+#include <binder/IServiceManager.h>
+
 #include <input/PointerController.h>
 #include <input/SpriteController.h>
 
@@ -63,6 +65,7 @@
 #include "android_hardware_input_InputApplicationHandle.h"
 #include "android_hardware_input_InputWindowHandle.h"
 #include "android_hardware_display_DisplayViewport.h"
+#include "android_util_Binder.h"
 
 #include <vector>
 
@@ -153,15 +156,6 @@
             getInputApplicationHandleObjLocalRef(env);
 }
 
-static jobject getInputWindowHandleObjLocalRef(JNIEnv* env,
-        const sp<InputWindowHandle>& inputWindowHandle) {
-    if (inputWindowHandle == nullptr) {
-        return nullptr;
-    }
-    return static_cast<NativeInputWindowHandle*>(inputWindowHandle.get())->
-            getInputWindowHandleObjLocalRef(env);
-}
-
 static void loadSystemIconAsSpriteWithPointerIcon(JNIEnv* env, jobject contextObj, int32_t style,
         PointerIcon* outPointerIcon, SpriteIcon* outSpriteIcon) {
     status_t status = android_view_PointerIcon_loadSystemIcon(env,
@@ -216,8 +210,7 @@
 
     void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
 
-    status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
-            const sp<InputWindowHandle>& inputWindowHandle, int32_t displayId);
+    status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel, int32_t displayId);
     status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
 
     void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId);
@@ -253,17 +246,17 @@
             uint32_t policyFlags);
     virtual void notifyConfigurationChanged(nsecs_t when);
     virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
-            const sp<InputWindowHandle>& inputWindowHandle,
+            const sp<IBinder>& token,
             const std::string& reason);
-    virtual void notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle);
+    virtual void notifyInputChannelBroken(const sp<IBinder>& token);
     virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig);
     virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
     virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags);
     virtual nsecs_t interceptKeyBeforeDispatching(
-            const sp<InputWindowHandle>& inputWindowHandle,
+            const sp<IBinder>& token,
             const KeyEvent* keyEvent, uint32_t policyFlags);
-    virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle,
+    virtual bool dispatchUnhandledKey(const sp<IBinder>& token,
             const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent);
     virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
     virtual bool checkInjectEventsPermissionNonReentrant(
@@ -442,11 +435,10 @@
 }
 
 status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
-        const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle,
-                int32_t displayId) {
+        const sp<InputChannel>& inputChannel, int32_t displayId) {
     ATRACE_CALL();
-    return mInputManager->getDispatcher()->registerInputChannel(inputChannel, inputWindowHandle,
-            displayId);
+    return mInputManager->getDispatcher()->registerInputChannel(
+            inputChannel, displayId);
 }
 
 status_t NativeInputManager::unregisterInputChannel(JNIEnv* /* env */,
@@ -657,7 +649,7 @@
 }
 
 nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
-        const sp<InputWindowHandle>& inputWindowHandle, const std::string& reason) {
+        const sp<IBinder>& token, const std::string& reason) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
     ALOGD("notifyANR");
 #endif
@@ -667,12 +659,11 @@
 
     jobject inputApplicationHandleObj =
             getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);
-    jobject inputWindowHandleObj =
-            getInputWindowHandleObjLocalRef(env, inputWindowHandle);
+    jobject tokenObj = javaObjectForIBinder(env, token);
     jstring reasonObj = env->NewStringUTF(reason.c_str());
 
     jlong newTimeout = env->CallLongMethod(mServiceObj,
-                gServiceClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj,
+                gServiceClassInfo.notifyANR, inputApplicationHandleObj, tokenObj,
                 reasonObj);
     if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
         newTimeout = 0; // abort dispatch
@@ -681,12 +672,11 @@
     }
 
     env->DeleteLocalRef(reasonObj);
-    env->DeleteLocalRef(inputWindowHandleObj);
     env->DeleteLocalRef(inputApplicationHandleObj);
     return newTimeout;
 }
 
-void NativeInputManager::notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle) {
+void NativeInputManager::notifyInputChannelBroken(const sp<IBinder>& token) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
     ALOGD("notifyInputChannelBroken");
 #endif
@@ -694,14 +684,11 @@
 
     JNIEnv* env = jniEnv();
 
-    jobject inputWindowHandleObj =
-            getInputWindowHandleObjLocalRef(env, inputWindowHandle);
-    if (inputWindowHandleObj) {
+    jobject tokenObj = javaObjectForIBinder(env, token);
+    if (tokenObj) {
         env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyInputChannelBroken,
-                inputWindowHandleObj);
+                tokenObj);
         checkAndClearExceptionFromCallback(env, "notifyInputChannelBroken");
-
-        env->DeleteLocalRef(inputWindowHandleObj);
     }
 }
 
@@ -1061,7 +1048,7 @@
 }
 
 nsecs_t NativeInputManager::interceptKeyBeforeDispatching(
-        const sp<InputWindowHandle>& inputWindowHandle,
+        const sp<IBinder>& token,
         const KeyEvent* keyEvent, uint32_t policyFlags) {
     ATRACE_CALL();
     // Policy:
@@ -1072,13 +1059,14 @@
     if (policyFlags & POLICY_FLAG_TRUSTED) {
         JNIEnv* env = jniEnv();
 
-        // Note: inputWindowHandle may be null.
-        jobject inputWindowHandleObj = getInputWindowHandleObjLocalRef(env, inputWindowHandle);
+        // Token may be null
+        jobject tokenObj = javaObjectForIBinder(env, token);
+
         jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
         if (keyEventObj) {
             jlong delayMillis = env->CallLongMethod(mServiceObj,
                     gServiceClassInfo.interceptKeyBeforeDispatching,
-                    inputWindowHandleObj, keyEventObj, policyFlags);
+                    tokenObj, keyEventObj, policyFlags);
             bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
             android_view_KeyEvent_recycle(env, keyEventObj);
             env->DeleteLocalRef(keyEventObj);
@@ -1092,12 +1080,11 @@
         } else {
             ALOGE("Failed to obtain key event object for interceptKeyBeforeDispatching.");
         }
-        env->DeleteLocalRef(inputWindowHandleObj);
     }
     return result;
 }
 
-bool NativeInputManager::dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle,
+bool NativeInputManager::dispatchUnhandledKey(const sp<IBinder>& token,
         const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) {
     ATRACE_CALL();
     // Policy:
@@ -1106,13 +1093,13 @@
     if (policyFlags & POLICY_FLAG_TRUSTED) {
         JNIEnv* env = jniEnv();
 
-        // Note: inputWindowHandle may be null.
-        jobject inputWindowHandleObj = getInputWindowHandleObjLocalRef(env, inputWindowHandle);
+        // Note: tokenObj may be null.
+        jobject tokenObj = javaObjectForIBinder(env, token);
         jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
         if (keyEventObj) {
             jobject fallbackKeyEventObj = env->CallObjectMethod(mServiceObj,
                     gServiceClassInfo.dispatchUnhandledKey,
-                    inputWindowHandleObj, keyEventObj, policyFlags);
+                    tokenObj, keyEventObj, policyFlags);
             if (checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey")) {
                 fallbackKeyEventObj = nullptr;
             }
@@ -1131,7 +1118,6 @@
         } else {
             ALOGE("Failed to obtain key event object for dispatchUnhandledKey.");
         }
-        env->DeleteLocalRef(inputWindowHandleObj);
     }
     return result;
 }
@@ -1316,7 +1302,7 @@
 }
 
 static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
-        jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jint displayId) {
+        jlong ptr, jobject inputChannelObj, jint displayId) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
@@ -1325,12 +1311,10 @@
         throwInputChannelNotInitialized(env);
         return;
     }
+    bool monitor = inputChannel->getToken() == nullptr && displayId != ADISPLAY_ID_NONE;
 
-    sp<InputWindowHandle> inputWindowHandle =
-            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
+    status_t status = im->registerInputChannel(env, inputChannel, displayId);
 
-    status_t status = im->registerInputChannel(
-            env, inputChannel, inputWindowHandle, displayId);
     if (status) {
         std::string message;
         message += StringPrintf("Failed to register input channel.  status=%d", status);
@@ -1339,7 +1323,7 @@
     }
 
     // If inputWindowHandle is null and displayId >= 0, treat inputChannel as monitor.
-    if (inputWindowHandle != nullptr || displayId == ADISPLAY_ID_NONE) {
+    if (!monitor) {
         android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
                 handleInputChannelDisposed, im);
     }
@@ -1640,7 +1624,7 @@
     { "nativeHasKeys", "(JII[I[Z)Z",
             (void*) nativeHasKeys },
     { "nativeRegisterInputChannel",
-            "(JLandroid/view/InputChannel;Lcom/android/server/input/InputWindowHandle;I)V",
+            "(JLandroid/view/InputChannel;I)V",
             (void*) nativeRegisterInputChannel },
     { "nativeUnregisterInputChannel", "(JLandroid/view/InputChannel;)V",
             (void*) nativeUnregisterInputChannel },
@@ -1650,9 +1634,9 @@
             (void*) nativeInjectInputEvent },
     { "nativeToggleCapsLock", "(JI)V",
             (void*) nativeToggleCapsLock },
-    { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;I)V",
+    { "nativeSetInputWindows", "(J[Landroid/view/InputWindowHandle;I)V",
             (void*) nativeSetInputWindows },
-    { "nativeSetFocusedApplication", "(JILcom/android/server/input/InputApplicationHandle;)V",
+    { "nativeSetFocusedApplication", "(JILandroid/view/InputApplicationHandle;)V",
             (void*) nativeSetFocusedApplication },
     { "nativeSetFocusedDisplay", "(JI)V",
             (void*) nativeSetFocusedDisplay },
@@ -1731,11 +1715,11 @@
             "notifySwitch", "(JII)V");
 
     GET_METHOD_ID(gServiceClassInfo.notifyInputChannelBroken, clazz,
-            "notifyInputChannelBroken", "(Lcom/android/server/input/InputWindowHandle;)V");
+            "notifyInputChannelBroken", "(Landroid/os/IBinder;)V");
 
     GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz,
             "notifyANR",
-            "(Lcom/android/server/input/InputApplicationHandle;Lcom/android/server/input/InputWindowHandle;Ljava/lang/String;)J");
+            "(Landroid/view/InputApplicationHandle;Landroid/os/IBinder;Ljava/lang/String;)J");
 
     GET_METHOD_ID(gServiceClassInfo.filterInputEvent, clazz,
             "filterInputEvent", "(Landroid/view/InputEvent;I)Z");
@@ -1748,11 +1732,11 @@
 
     GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeDispatching, clazz,
             "interceptKeyBeforeDispatching",
-            "(Lcom/android/server/input/InputWindowHandle;Landroid/view/KeyEvent;I)J");
+            "(Landroid/os/IBinder;Landroid/view/KeyEvent;I)J");
 
     GET_METHOD_ID(gServiceClassInfo.dispatchUnhandledKey, clazz,
             "dispatchUnhandledKey",
-            "(Lcom/android/server/input/InputWindowHandle;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;");
+            "(Landroid/os/IBinder;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;");
 
     GET_METHOD_ID(gServiceClassInfo.checkInjectEventsPermission, clazz,
             "checkInjectEventsPermission", "(II)Z");
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
index dc3cd62..fcfd246 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -19,6 +19,7 @@
 import static android.content.Context.INTELLIGENCE_MANAGER_SERVICE;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.content.ComponentName;
 import android.content.Context;
@@ -59,14 +60,14 @@
         super(context, UserManager.DISALLOW_INTELLIGENCE_CAPTURE);
     }
 
-    @Override // from MasterSystemService
+    @Override // from AbstractMasterSystemService
     protected String getServiceSettingsProperty() {
         // TODO(b/111276913): STOPSHIP temporary settings, until it's set by resourcs + cmd
         return "intel_service";
     }
 
-    @Override // from MasterSystemService
-    protected IntelligencePerUserService newServiceLocked(int resolvedUserId,
+    @Override // from AbstractMasterSystemService
+    protected IntelligencePerUserService newServiceLocked(@UserIdInt int resolvedUserId,
             boolean disabled) {
         return new IntelligencePerUserService(this, mLock, resolvedUserId);
     }
@@ -79,12 +80,9 @@
     }
 
     @Override // from AbstractMasterSystemService
-    protected IntelligencePerUserService removeCachedServiceLocked(int userId) {
-        final IntelligencePerUserService service = super.removeCachedServiceLocked(userId);
-        if (service != null) {
-            service.destroyLocked();
-        }
-        return service;
+    protected void onServiceRemoved(@NonNull IntelligencePerUserService service,
+            @UserIdInt int userId) {
+        service.destroyLocked();
     }
 
     private ActivityManagerInternal getAmInternal() {
diff --git a/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java b/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
deleted file mode 100644
index c162c3b..0000000
--- a/services/tests/servicestests/src/com/android/server/am/GlobalSettingsToPropertiesMapperTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.am;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import android.content.ContentResolver;
-import android.provider.Settings;
-import android.test.mock.MockContentResolver;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.test.FakeSettingsProvider;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Tests for {@link GlobalSettingsToPropertiesMapper}
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:GlobalSettingsToPropertiesMapperTest
- */
-@SmallTest
-public class GlobalSettingsToPropertiesMapperTest {
-    private static final String[][] TEST_MAPPING = new String[][] {
-        {Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "TestProperty"}
-    };
-
-    private TestMapper mTestMapper;
-    private MockContentResolver mMockContentResolver;
-
-    @Before
-    public void setup() {
-        // Use FakeSettingsProvider to not affect global state
-        mMockContentResolver = new MockContentResolver(getInstrumentation().getTargetContext());
-        mMockContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
-        mTestMapper = new TestMapper(mMockContentResolver);
-    }
-
-    @Test
-    public void testUpdatePropertiesFromGlobalSettings() {
-        Settings.Global.putString(mMockContentResolver,
-                Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue");
-
-        mTestMapper.updatePropertiesFromGlobalSettings();
-        String propValue = mTestMapper.systemPropertiesGet("TestProperty");
-        assertEquals("testValue", propValue);
-
-        Settings.Global.putString(mMockContentResolver,
-                Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
-        mTestMapper.updatePropertyFromSetting(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
-                "TestProperty");
-        propValue = mTestMapper.systemPropertiesGet("TestProperty");
-        assertEquals("testValue2", propValue);
-
-        Settings.Global.putString(mMockContentResolver,
-                Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
-        mTestMapper.updatePropertyFromSetting(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
-                "TestProperty");
-        propValue = mTestMapper.systemPropertiesGet("TestProperty");
-        assertEquals("", propValue);
-    }
-
-    @Test
-    public void testUpdatePropertiesFromGlobalSettings_PropertyAndSettingNotPresent() {
-        // Test that empty property will not not be set if setting is not set
-        mTestMapper.updatePropertiesFromGlobalSettings();
-        String propValue = mTestMapper.systemPropertiesGet("TestProperty");
-        assertNull("Property should not be set if setting is null", propValue);
-    }
-
-    private static class TestMapper extends GlobalSettingsToPropertiesMapper {
-        private final Map<String, String> mProps = new HashMap<>();
-
-        TestMapper(ContentResolver contentResolver) {
-            super(contentResolver, TEST_MAPPING);
-        }
-
-        @Override
-        protected String systemPropertiesGet(String key) {
-            Preconditions.checkNotNull(key);
-            return mProps.get(key);
-        }
-
-        @Override
-        protected void systemPropertiesSet(String key, String value) {
-            Preconditions.checkNotNull(value);
-            Preconditions.checkNotNull(key);
-            mProps.put(key, value);
-        }
-    }
-}
-
diff --git a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
new file mode 100644
index 0000000..d965f8a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.text.TextUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.test.FakeSettingsProvider;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tests for {@link SettingsToPropertiesMapper}
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SettingsToPropertiesMapperTest {
+    private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\.\\-@:]*$";
+    private static final String[] TEST_MAPPING = new String[] {
+            Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS
+    };
+
+    private TestMapper mTestMapper;
+    private MockContentResolver mMockContentResolver;
+
+    @Before
+    public void setupForEach() {
+        // Use FakeSettingsProvider to not affect global state
+        mMockContentResolver = new MockContentResolver(InstrumentationRegistry.getContext());
+        mMockContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        mTestMapper = new TestMapper(mMockContentResolver);
+    }
+
+    @Test
+    public void validateRegisteredGlobalSettings() {
+        HashSet<String> hashSet = new HashSet<>();
+        for (String globalSetting : SettingsToPropertiesMapper.sGlobalSettings) {
+            if (hashSet.contains(globalSetting)) {
+                Assert.fail("globalSetting "
+                        + globalSetting
+                        + " is registered more than once in "
+                        + "SettingsToPropertiesMapper.sGlobalSettings.");
+            }
+            hashSet.add(globalSetting);
+            if (TextUtils.isEmpty(globalSetting)) {
+                Assert.fail("empty globalSetting registered.");
+            }
+            if (!globalSetting.matches(NAME_VALID_CHARACTERS_REGEX)) {
+                Assert.fail(globalSetting + " contains invalid characters. "
+                        + "Only alphanumeric characters, '.', '-', '@', ':' and '_' are valid.");
+            }
+        }
+    }
+
+    @Test
+    public void testUpdatePropertiesFromSettings() {
+        Settings.Global.putString(mMockContentResolver,
+                Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue");
+
+        String systemPropertyName = "persist.device_config.global_settings."
+                + "sqlite_compatibility_wal_flags";
+
+        mTestMapper.updatePropertiesFromSettings();
+        String propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+        Assert.assertEquals("testValue", propValue);
+
+        Settings.Global.putString(mMockContentResolver,
+                Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2");
+        mTestMapper.updatePropertyFromSetting(
+                Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
+                systemPropertyName,
+                true);
+        propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+        Assert.assertEquals("testValue2", propValue);
+
+        Settings.Global.putString(mMockContentResolver,
+                Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null);
+        mTestMapper.updatePropertyFromSetting(
+                Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
+                systemPropertyName,
+                true);
+        propValue = mTestMapper.systemPropertiesGet(systemPropertyName);
+        Assert.assertEquals("", propValue);
+    }
+
+    @Test
+    public void testMakePropertyName() {
+        try {
+            Assert.assertEquals("persist.device_config.test_category.test_flag",
+                    SettingsToPropertiesMapper.makePropertyName("test_category", "test_flag"));
+        } catch (Exception e) {
+            Assert.fail("Unexpected exception: " + e.getMessage());
+        }
+
+        try {
+            Assert.assertEquals(null,
+                    SettingsToPropertiesMapper.makePropertyName("test_category!!!", "test_flag"));
+        } catch (Exception e) {
+            Assert.fail("Unexpected exception: " + e.getMessage());
+        }
+
+        try {
+            Assert.assertEquals(null,
+                    SettingsToPropertiesMapper.makePropertyName("test_category", ".test_flag"));
+        } catch (Exception e) {
+            Assert.fail("Unexpected exception: " + e.getMessage());
+        }
+    }
+
+    @Test
+    public void testUpdatePropertiesFromSettings_PropertyAndSettingNotPresent() {
+        // Test that empty property will not not be set if setting is not set
+        mTestMapper.updatePropertiesFromSettings();
+        String propValue = mTestMapper.systemPropertiesGet("TestProperty");
+        Assert.assertNull("Property should not be set if setting is null", propValue);
+    }
+
+    @Test
+    public void testIsNativeFlagsResetPerformed() {
+        mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
+        Assert.assertTrue(mTestMapper.isNativeFlagsResetPerformed());
+
+        mTestMapper.systemPropertiesSet("device_config.reset_performed", "false");
+        Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed());
+
+        mTestMapper.systemPropertiesSet("device_config.reset_performed", "");
+        Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed());
+    }
+
+    @Test
+    public void testGetResetNativeCategories() {
+        mTestMapper.systemPropertiesSet("device_config.reset_performed", "");
+        Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0);
+
+        mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
+        mTestMapper.setFileContent("");
+        Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0);
+
+        mTestMapper.systemPropertiesSet("device_config.reset_performed", "true");
+        mTestMapper.setFileContent("persist.device_config.category1.flag;"
+                + "persist.device_config.category2.flag;persist.device_config.category3.flag;"
+                + "persist.device_config.category3.flag2");
+        List<String> categories = Arrays.asList(mTestMapper.getResetNativeCategories());
+        Assert.assertEquals(3, categories.size());
+        Assert.assertTrue(categories.contains("category1"));
+        Assert.assertTrue(categories.contains("category2"));
+        Assert.assertTrue(categories.contains("category3"));
+    }
+
+    private static class TestMapper extends SettingsToPropertiesMapper {
+        private final Map<String, String> mProps = new HashMap<>();
+
+        private String mFileContent = "";
+
+        TestMapper(ContentResolver contentResolver) {
+            super(contentResolver, TEST_MAPPING, new String[] {});
+        }
+
+        @Override
+        protected String systemPropertiesGet(String key) {
+            Preconditions.checkNotNull(key);
+            return mProps.get(key);
+        }
+
+        @Override
+        protected void systemPropertiesSet(String key, String value) {
+            Preconditions.checkNotNull(value);
+            Preconditions.checkNotNull(key);
+            mProps.put(key, value);
+        }
+
+        protected void setFileContent(String fileContent) {
+            mFileContent = fileContent;
+        }
+
+        @Override
+        protected String getResetFlagsFileContent() {
+            return mFileContent;
+        }
+    }
+}
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/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 3fe381b..1a218b2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -27,7 +27,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -98,7 +97,7 @@
     private static final UserHandle USER = UserHandle.of(0);
     private static final int UID_O = 1111;
     private static final String SYSTEM_PKG = "android";
-    private static final int SYSTEM_UID= 1000;
+    private static final int SYSTEM_UID = 1000;
     private static final UserHandle USER2 = UserHandle.of(10);
     private static final String TEST_CHANNEL_ID = "test_channel_id";
     private static final String TEST_AUTHORITY = "test";
@@ -1091,6 +1090,158 @@
     }
 
     @Test
+    public void testGetChannelsBypassingDndCount_noChannelsBypassing() throws Exception {
+        assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+                USER.getIdentifier()).getList().size());
+    }
+
+    @Test
+    public void testGetChannelsBypassingDnd_noChannelsForUserIdBypassing()
+            throws Exception {
+        int user = 9;
+        NotificationChannel channel = new NotificationChannel("id", "name",
+                NotificationManager.IMPORTANCE_MAX);
+        channel.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG_N_MR1, 111, channel, true, true);
+
+        assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+                user).getList().size());
+    }
+
+    @Test
+    public void testGetChannelsBypassingDndCount_oneChannelBypassing_groupBlocked() {
+        int user = USER.getIdentifier();
+        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+        NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+                NotificationManager.IMPORTANCE_MAX);
+        channel1.setBypassDnd(true);
+        channel1.setGroup(ncg.getId());
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg,  /* fromTargetApp */ true);
+        mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+
+        assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+                user).getList().size());
+
+        // disable group
+        ncg.setBlocked(true);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg,  /* fromTargetApp */ false);
+        assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+                user).getList().size());
+    }
+
+    @Test
+    public void testGetChannelsBypassingDndCount_multipleChannelsBypassing() {
+        int user = USER.getIdentifier();
+        NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+                NotificationManager.IMPORTANCE_MAX);
+        NotificationChannel channel2 = new NotificationChannel("id2", "name2",
+                NotificationManager.IMPORTANCE_MAX);
+        NotificationChannel channel3 = new NotificationChannel("id3", "name3",
+                NotificationManager.IMPORTANCE_MAX);
+        channel1.setBypassDnd(true);
+        channel2.setBypassDnd(true);
+        channel3.setBypassDnd(true);
+        // has DND access, so can set bypassDnd attribute
+        mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+        mHelper.createNotificationChannel(PKG_N_MR1, user, channel2, true, true);
+        mHelper.createNotificationChannel(PKG_N_MR1, user, channel3, true, true);
+        assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+                user).getList().size());
+
+        // block notifications from this app
+        mHelper.setEnabled(PKG_N_MR1, user, false);
+        assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+                user).getList().size());
+
+        // re-enable notifications from this app
+        mHelper.setEnabled(PKG_N_MR1, user, true);
+        assertEquals(3, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+                user).getList().size());
+
+        // setBypassDnd false for some channels
+        channel1.setBypassDnd(false);
+        channel2.setBypassDnd(false);
+        assertEquals(1, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+                user).getList().size());
+
+        // setBypassDnd false for rest of the channels
+        channel3.setBypassDnd(false);
+        assertEquals(0, mHelper.getNotificationChannelsBypassingDnd(PKG_N_MR1,
+                user).getList().size());
+    }
+
+    @Test
+    public void testGetAppsBypassingDndCount_noAppsBypassing() throws Exception {
+        assertEquals(0, mHelper.getAppsBypassingDndCount(USER.getIdentifier()));
+    }
+
+    @Test
+    public void testGetAppsBypassingDndCount_noAppsForUserIdBypassing() throws Exception {
+        int user = 9;
+        NotificationChannel channel = new NotificationChannel("id", "name",
+                NotificationManager.IMPORTANCE_MAX);
+        channel.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG_N_MR1, 111, channel, true, true);
+
+        assertEquals(0, mHelper.getAppsBypassingDndCount(user));
+    }
+
+    @Test
+    public void testGetAppsBypassingDndCount_oneChannelBypassing_groupBlocked() {
+        int user = USER.getIdentifier();
+        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+        NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+                NotificationManager.IMPORTANCE_MAX);
+        channel1.setBypassDnd(true);
+        channel1.setGroup(ncg.getId());
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg,  /* fromTargetApp */ true);
+        mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+
+        assertEquals(1, mHelper.getAppsBypassingDndCount(user));
+
+        // disable group
+        ncg.setBlocked(true);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, user, ncg,  /* fromTargetApp */ false);
+        assertEquals(0, mHelper.getAppsBypassingDndCount(user));
+    }
+
+    @Test
+    public void testGetAppsBypassingDndCount_oneAppBypassing() {
+        int user = USER.getIdentifier();
+        NotificationChannel channel1 = new NotificationChannel("id1", "name1",
+                NotificationManager.IMPORTANCE_MAX);
+        NotificationChannel channel2 = new NotificationChannel("id2", "name2",
+                NotificationManager.IMPORTANCE_MAX);
+        NotificationChannel channel3 = new NotificationChannel("id3", "name3",
+                NotificationManager.IMPORTANCE_MAX);
+        channel1.setBypassDnd(true);
+        channel2.setBypassDnd(true);
+        channel3.setBypassDnd(true);
+        // has DND access, so can set bypassDnd attribute
+        mHelper.createNotificationChannel(PKG_N_MR1, user, channel1, true, /*has DND access*/ true);
+        mHelper.createNotificationChannel(PKG_N_MR1, user, channel2, true, true);
+        mHelper.createNotificationChannel(PKG_N_MR1, user, channel3, true, true);
+        assertEquals(1, mHelper.getAppsBypassingDndCount(user));
+
+        // block notifications from this app
+        mHelper.setEnabled(PKG_N_MR1, user, false);
+        assertEquals(0, mHelper.getAppsBypassingDndCount(user)); // no apps can bypass dnd
+
+        // re-enable notifications from this app
+        mHelper.setEnabled(PKG_N_MR1, user, true);
+        assertEquals(1, mHelper.getAppsBypassingDndCount(user));
+
+        // setBypassDnd false for some channels
+        channel1.setBypassDnd(false);
+        channel2.setBypassDnd(false);
+        assertEquals(1, mHelper.getAppsBypassingDndCount(user));
+
+        // setBypassDnd false for rest of the channels
+        channel3.setBypassDnd(false);
+        assertEquals(0, mHelper.getAppsBypassingDndCount(user));
+    }
+
+    @Test
     public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception {
         // create notification channel that can't bypass dnd
         // expected result: areChannelsBypassingDnd = false
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/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 85357bb..906d64c 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -58,12 +58,18 @@
     case Instruction::Op::kInvokeVirtual:
       out << "kInvokeVirtual";
       return out;
+    case Instruction::Op::kInvokeDirect:
+      out << "kInvokeDirect";
+      return out;
     case Instruction::Op::kBindLabel:
       out << "kBindLabel";
       return out;
     case Instruction::Op::kBranchEqz:
       out << "kBranchEqz";
       return out;
+    case Instruction::Op::kNew:
+      out << "kNew";
+      return out;
   }
 }
 
@@ -167,6 +173,8 @@
   ir::Type* type = Alloc<ir::Type>();
   type->descriptor = GetOrAddString(descriptor);
   types_by_descriptor_[descriptor] = type;
+  type->orig_index = dex_file_->types_indexes.AllocateIndex();
+  dex_file_->types_map[type->orig_index] = type;
   return type;
 }
 
@@ -223,9 +231,10 @@
       decl_->prototype->param_types != nullptr ? decl_->prototype->param_types->types.size() : 0;
   code->registers = num_registers_ + num_args;
   code->ins_count = num_args;
-  code->outs_count = decl_->prototype->return_type == dex_->GetOrAddType("V") ? 0 : 1;
   EncodeInstructions();
   code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size());
+  size_t const return_count = decl_->prototype->return_type == dex_->GetOrAddType("V") ? 0 : 1;
+  code->outs_count = std::max(return_count, max_args_);
   method->code = code;
 
   class_->direct_methods.push_back(method);
@@ -277,22 +286,26 @@
     case Instruction::Op::kMove:
       return EncodeMove(instruction);
     case Instruction::Op::kInvokeVirtual:
-      return EncodeInvokeVirtual(instruction);
+      return EncodeInvoke(instruction, art::Instruction::INVOKE_VIRTUAL);
+    case Instruction::Op::kInvokeDirect:
+      return EncodeInvoke(instruction, art::Instruction::INVOKE_DIRECT);
     case Instruction::Op::kBindLabel:
       return BindLabel(instruction.args()[0]);
     case Instruction::Op::kBranchEqz:
       return EncodeBranch(art::Instruction::IF_EQZ, instruction);
+    case Instruction::Op::kNew:
+      return EncodeNew(instruction);
   }
 }
 
 void MethodBuilder::EncodeReturn(const Instruction& instruction, ::art::Instruction::Code opcode) {
   DCHECK(!instruction.dest().has_value());
   if (instruction.args().size() == 0) {
-    buffer_.push_back(art::Instruction::RETURN_VOID);
+    Encode10x(art::Instruction::RETURN_VOID);
   } else {
     DCHECK_EQ(1, instruction.args().size());
     size_t source = RegisterValue(instruction.args()[0]);
-    buffer_.push_back(opcode | source << 8);
+    Encode11x(opcode, source);
   }
 }
 
@@ -307,37 +320,43 @@
   if (source.is_immediate()) {
     // TODO: support more registers
     DCHECK_LT(RegisterValue(*instruction.dest()), 16);
-    DCHECK_LT(source.value(), 16);
-    buffer_.push_back(art::Instruction::CONST_4 | (source.value() << 12) |
-                      (RegisterValue(*instruction.dest()) << 8));
+    Encode11n(art::Instruction::CONST_4, RegisterValue(*instruction.dest()), source.value());
   } else if (source.is_string()) {
     constexpr size_t kMaxRegisters = 256;
     DCHECK_LT(RegisterValue(*instruction.dest()), kMaxRegisters);
     DCHECK_LT(source.value(), 65536);  // make sure we don't need a jumbo string
-    buffer_.push_back(::art::Instruction::CONST_STRING | (RegisterValue(*instruction.dest()) << 8));
-    buffer_.push_back(source.value());
+    Encode21c(::art::Instruction::CONST_STRING, RegisterValue(*instruction.dest()), source.value());
   } else {
     UNIMPLEMENTED(FATAL);
   }
 }
 
-void MethodBuilder::EncodeInvokeVirtual(const Instruction& instruction) {
-  DCHECK_EQ(Instruction::Op::kInvokeVirtual, instruction.opcode());
+void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode) {
+  constexpr size_t kMaxArgs = 5;
 
-  // TODO: support more than one argument (i.e. the this argument) and change this to DCHECK_GE
-  DCHECK_EQ(1, instruction.args().size());
+  CHECK_LE(instruction.args().size(), kMaxArgs);
 
-  const Value& this_arg = instruction.args()[0];
-
-  size_t real_reg = RegisterValue(this_arg) & 0xf;
-  buffer_.push_back(1 << 12 | art::Instruction::INVOKE_VIRTUAL);
-  buffer_.push_back(instruction.method_id());
-  buffer_.push_back(real_reg);
-
-  if (instruction.dest().has_value()) {
-    real_reg = RegisterValue(*instruction.dest());
-    buffer_.push_back(real_reg << 8 | art::Instruction::MOVE_RESULT);
+  uint8_t arguments[kMaxArgs]{};
+  for (size_t i = 0; i < instruction.args().size(); ++i) {
+    CHECK(instruction.args()[i].is_variable());
+    arguments[i] = RegisterValue(instruction.args()[i]);
   }
+
+  Encode35c(opcode,
+            instruction.args().size(),
+            instruction.method_id(),
+            arguments[0],
+            arguments[1],
+            arguments[2],
+            arguments[3],
+            arguments[4]);
+
+  // If there is a return value, add a move-result instruction
+  if (instruction.dest().has_value()) {
+    Encode11x(art::Instruction::MOVE_RESULT, RegisterValue(*instruction.dest()));
+  }
+
+  max_args_ = std::max(max_args_, instruction.args().size());
 }
 
 // Encodes a conditional branch that tests a single argument.
@@ -350,9 +369,21 @@
   CHECK(branch_target.is_label());
 
   size_t instruction_offset = buffer_.size();
-  buffer_.push_back(op | (RegisterValue(test_value) << 8));
-  size_t field_offset = buffer_.size();
-  buffer_.push_back(LabelValue(branch_target, instruction_offset, field_offset));
+  size_t field_offset = buffer_.size() + 1;
+  Encode21c(
+      op, RegisterValue(test_value), LabelValue(branch_target, instruction_offset, field_offset));
+}
+
+void MethodBuilder::EncodeNew(const Instruction& instruction) {
+  DCHECK_EQ(Instruction::Op::kNew, instruction.opcode());
+  DCHECK(instruction.dest().has_value());
+  DCHECK(instruction.dest()->is_variable());
+  DCHECK_EQ(1, instruction.args().size());
+
+  const Value& type = instruction.args()[0];
+  DCHECK_LT(RegisterValue(*instruction.dest()), 256);
+  DCHECK(type.is_type());
+  Encode21c(::art::Instruction::NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value());
 }
 
 size_t MethodBuilder::RegisterValue(const Value& value) const {
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 31414c8..adf82bf 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -112,6 +112,7 @@
   static constexpr Value Immediate(size_t value) { return Value{value, Kind::kImmediate}; }
   static constexpr Value String(size_t value) { return Value{value, Kind::kString}; }
   static constexpr Value Label(size_t id) { return Value{id, Kind::kLabel}; }
+  static constexpr Value Type(size_t id) { return Value{id, Kind::kType}; }
 
   bool is_register() const { return kind_ == Kind::kLocalRegister; }
   bool is_parameter() const { return kind_ == Kind::kParameter; }
@@ -119,11 +120,12 @@
   bool is_immediate() const { return kind_ == Kind::kImmediate; }
   bool is_string() const { return kind_ == Kind::kString; }
   bool is_label() const { return kind_ == Kind::kLabel; }
+  bool is_type() const { return kind_ == Kind::kType; }
 
   size_t value() const { return value_; }
 
  private:
-  enum class Kind { kLocalRegister, kParameter, kImmediate, kString, kLabel };
+  enum class Kind { kLocalRegister, kParameter, kImmediate, kString, kLabel, kType };
 
   const size_t value_;
   const Kind kind_;
@@ -139,7 +141,16 @@
  public:
   // The operation performed by this instruction. These are virtual instructions that do not
   // correspond exactly to DEX instructions.
-  enum class Op { kReturn, kReturnObject, kMove, kInvokeVirtual, kBindLabel, kBranchEqz };
+  enum class Op {
+    kReturn,
+    kReturnObject,
+    kMove,
+    kInvokeVirtual,
+    kInvokeDirect,
+    kBindLabel,
+    kBranchEqz,
+    kNew
+  };
 
   ////////////////////////
   // Named Constructors //
@@ -160,6 +171,12 @@
                                           Value this_arg, T... args) {
     return Instruction{Op::kInvokeVirtual, method_id, dest, this_arg, args...};
   }
+  // For direct calls (basically, constructors).
+  template <typename... T>
+  static inline Instruction InvokeDirect(size_t method_id, std::optional<const Value> dest,
+                                         Value this_arg, T... args) {
+    return Instruction{Op::kInvokeDirect, method_id, dest, this_arg, args...};
+  }
 
   ///////////////
   // Accessors //
@@ -189,6 +206,12 @@
 // Needed for CHECK_EQ, DCHECK_EQ, etc.
 std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode);
 
+// Keeps track of information needed to manipulate or call a method.
+struct MethodDeclData {
+  size_t id;
+  ir::MethodDecl* decl;
+};
+
 // Tools to help build methods and their bodies.
 class MethodBuilder {
  public:
@@ -216,6 +239,8 @@
   // const/4
   void BuildConst4(Value target, int value);
   void BuildConstString(Value target, const std::string& value);
+  template <typename... T>
+  void BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args);
 
   // TODO: add builders for more instructions
 
@@ -229,8 +254,55 @@
   void EncodeReturn(const Instruction& instruction, ::art::Instruction::Code opcode);
 
   void EncodeMove(const Instruction& instruction);
-  void EncodeInvokeVirtual(const Instruction& instruction);
+  void EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode);
   void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
+  void EncodeNew(const Instruction& instruction);
+
+  // Low-level instruction format encoding. See
+  // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
+  // formats.
+
+  inline void Encode10x(art::Instruction::Code opcode) {
+    // 00|op
+    buffer_.push_back(opcode);
+  }
+
+  inline void Encode11x(art::Instruction::Code opcode, uint8_t a) {
+    // aa|op
+    buffer_.push_back((a << 8) | opcode);
+  }
+
+  inline void Encode11n(art::Instruction::Code opcode, uint8_t a, int8_t b) {
+    // b|a|op
+
+    // Make sure the fields are in bounds (4 bits for a, 4 bits for b).
+    CHECK_LT(a, 16);
+    CHECK_LE(-8, b);
+    CHECK_LT(b, 8);
+
+    buffer_.push_back(((b & 0xf) << 12) | (a << 8) | opcode);
+  }
+
+  inline void Encode21c(art::Instruction::Code opcode, uint8_t a, uint16_t b) {
+    // aa|op|bbbb
+    buffer_.push_back((a << 8) | opcode);
+    buffer_.push_back(b);
+  }
+
+  inline void Encode35c(art::Instruction::Code opcode, size_t a, uint16_t b, uint8_t c, uint8_t d,
+                        uint8_t e, uint8_t f, uint8_t g) {
+    // a|g|op|bbbb|f|e|d|c
+
+    CHECK_LE(a, 5);
+    CHECK_LT(c, 16);
+    CHECK_LT(d, 16);
+    CHECK_LT(e, 16);
+    CHECK_LT(f, 16);
+    CHECK_LT(g, 16);
+    buffer_.push_back((a << 12) | (g << 8) | opcode);
+    buffer_.push_back(b);
+    buffer_.push_back((f << 12) | (e << 8) | (d << 4) | c);
+  }
 
   // Converts a register or parameter to its DEX register number.
   size_t RegisterValue(const Value& value) const;
@@ -270,6 +342,10 @@
   };
 
   std::vector<LabelData> labels_;
+
+  // During encoding, keep track of the largest number of arguments needed, so we can use it for our
+  // outs count
+  size_t max_args_{0};
 };
 
 // A helper to build class definitions.
@@ -289,12 +365,6 @@
   ir::Class* const class_;
 };
 
-// Keeps track of information needed to manipulate or call a method.
-struct MethodDeclData {
-  size_t id;
-  ir::MethodDecl* decl;
-};
-
 // Builds Dex files from scratch.
 class DexBuilder {
  public:
@@ -363,6 +433,17 @@
   std::map<Prototype, ir::Proto*> proto_map_;
 };
 
+template <typename... T>
+void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args) {
+  MethodDeclData constructor_data{dex_->GetOrDeclareMethod(type, "<init>", constructor)};
+  // allocate the object
+  ir::Type* type_def = dex_->GetOrAddType(type.descriptor());
+  AddInstruction(
+      Instruction::OpWithArgs(Instruction::Op::kNew, target, Value::Type(type_def->orig_index)));
+  // call the constructor
+  AddInstruction(Instruction::InvokeDirect(constructor_data.id, /*dest=*/{}, target, args...));
+};
+
 }  // namespace dex
 }  // namespace startop
 
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index 2ccdc6d5..e20f3a9 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -50,6 +50,14 @@
   }
 
   @Test
+  public void returnInteger5() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("returnInteger5");
+    Assert.assertEquals(5, method.invoke(null));
+  }
+
+  @Test
   public void returnParam() throws Exception {
     ClassLoader loader = loadDexFile("simple.dex");
     Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index 063a0cf..e2bf43bc 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -53,6 +53,19 @@
   }
   return5.Encode();
 
+  // int return5() { return 5; }
+  auto integer_type{TypeDescriptor::FromClassname("java.lang.Integer")};
+  auto returnInteger5{cbuilder.CreateMethod("returnInteger5", Prototype{integer_type})};
+  [&](MethodBuilder& method) {
+    Value five{method.MakeRegister()};
+    method.BuildConst4(five, 5);
+    Value object{method.MakeRegister()};
+    method.BuildNew(
+        object, integer_type, Prototype{TypeDescriptor::Void(), TypeDescriptor::Int()}, five);
+    method.BuildReturn(object, /*is_object=*/true);
+  }(returnInteger5);
+  returnInteger5.Encode();
+
   // // int returnParam(int x) { return x; }
   auto returnParam{cbuilder.CreateMethod("returnParam",
                                          Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fbc54ae6..b744770 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1320,18 +1320,13 @@
     public static final String KEY_MMS_CLOSE_CONNECTION_BOOL = "mmsCloseConnection";
 
     /**
-     * If carriers require differentiate un-provisioned status: cold sim or out of credit sim
-     * a package name and activity name can be provided to launch a supported carrier application
-     * that check the sim provisioning status
-     * The first element is the package name and the second element is the activity name
-     * of the provisioning app
-     * example:
-     * <item>com.google.android.carrierPackageName</item>
-     * <item>com.google.android.carrierPackageName.CarrierActivityName</item>
-     * The ComponentName of the carrier activity that can setup the device and activate with the
-     * network as part of the Setup Wizard flow.
+     * The flatten {@link android.content.ComponentName componentName} of the activity that can
+     * setup the device and activate with the network per carrier requirements.
+     *
+     * e.g, com.google.android.carrierPackageName/.CarrierActivityName
      * @hide
      */
+    @SystemApi
     public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
 
     /**
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 0c8d581..3c5ad84 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -233,34 +233,35 @@
     public static final int LISTEN_CARRIER_NETWORK_CHANGE                   = 0x00010000;
 
     /**
-     *  Listen for changes to the sim voice activation state
-     *  @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
-     *  @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
-     *  @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
-     *  @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
-     *  @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
-     *  {@more}
-     *  Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
-     *  fully activated
+     * Listen for changes to the sim voice activation state
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+     * {@more}
+     * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
+     * fully activated
      *
-     *  @see #onVoiceActivationStateChanged
-     *  @hide
+     * @see #onVoiceActivationStateChanged
+     * @hide
      */
+    @SystemApi
     public static final int LISTEN_VOICE_ACTIVATION_STATE                   = 0x00020000;
 
     /**
-     *  Listen for changes to the sim data activation state
-     *  @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
-     *  @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
-     *  @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
-     *  @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
-     *  @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
-     *  {@more}
-     *  Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
-     *  fully activated
+     * Listen for changes to the sim data activation state
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+     * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+     * {@more}
+     * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
+     * fully activated
      *
-     *  @see #onDataActivationStateChanged
-     *  @hide
+     * @see #onDataActivationStateChanged
+     * @hide
      */
     public static final int LISTEN_DATA_ACTIVATION_STATE                   = 0x00040000;
 
@@ -569,8 +570,8 @@
      * @param state is the current SIM voice activation state
      * @hide
      */
-    public void onVoiceActivationStateChanged(int state) {
-
+    @SystemApi
+    public void onVoiceActivationStateChanged(@TelephonyManager.SimActivationState int state) {
     }
 
     /**
@@ -578,8 +579,7 @@
      * @param state is the current SIM data activation state
      * @hide
      */
-    public void onDataActivationStateChanged(int state) {
-
+    public void onDataActivationStateChanged(@TelephonyManager.SimActivationState int state) {
     }
 
     /**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 31770ec..b5cbf3b 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2290,7 +2290,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
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index c9cf473..e06c372 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -292,7 +292,7 @@
      * Create an instance of ImsManager for the subscription id specified.
      *
      * @param context
-     * @param subId The ID of the subscription that this ImsManager will use.
+     * @param subId The ID of the subscription that this ImsMmTelManager will use.
      * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
      * @throws IllegalArgumentException if the subscription is invalid or
      *         the subscription ID is not an active subscription.
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
new file mode 100644
index 0000000..916e282
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -0,0 +1,252 @@
+/*
+ * 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.telephony.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsConfigCallback;
+import android.telephony.ims.stub.ImsConfigImplBase;
+
+import com.android.internal.telephony.ITelephony;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Manages IMS provisioning and configuration parameters, as well as callbacks for apps to listen
+ * to changes in these configurations.
+ *
+ * Note: IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
+ * applications and may vary.
+ * @hide
+ */
+@SystemApi
+public class ProvisioningManager {
+
+    /**
+     * Callback for IMS provisioning changes.
+     */
+    public static class Callback {
+
+        private static class CallbackBinder extends IImsConfigCallback.Stub {
+
+            private final Callback mLocalConfigurationCallback;
+            private Executor mExecutor;
+
+            private CallbackBinder(Callback localConfigurationCallback) {
+                mLocalConfigurationCallback = localConfigurationCallback;
+            }
+
+            @Override
+            public final void onIntConfigChanged(int item, int value) {
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() ->
+                                mLocalConfigurationCallback.onProvisioningIntChanged(item, value)));
+            }
+
+            @Override
+            public final void onStringConfigChanged(int item, String value) {
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() ->
+                                mLocalConfigurationCallback.onProvisioningStringChanged(item,
+                                        value)));
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final CallbackBinder mBinder = new CallbackBinder(this);
+
+        /**
+         * Called when a provisioning item has changed.
+         * @param item the IMS provisioning key constant, as defined by the OEM.
+         * @param value the new integer value of the IMS provisioning key.
+         */
+        public void onProvisioningIntChanged(int item, int value) {
+            // Base Implementation
+        }
+
+        /**
+         * Called when a provisioning item has changed.
+         * @param item the IMS provisioning key constant, as defined by the OEM.
+         * @param value the new String value of the IMS configuration constant.
+         */
+        public void onProvisioningStringChanged(int item, String value) {
+            // Base Implementation
+        }
+
+        /**@hide*/
+        public final IImsConfigCallback getBinder() {
+            return mBinder;
+        }
+
+        /**@hide*/
+        public void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+    }
+
+    private int mSubId;
+
+    /**
+     * Create a new {@link ProvisioningManager} for the subscription specified.
+     * @param context The context that this manager will use.
+     * @param subId The ID of the subscription that this ProvisioningManager will use.
+     * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+     * @throws IllegalArgumentException if the subscription is invalid or
+     *         the subscription ID is not an active subscription.
+     */
+    public static ProvisioningManager createForSubscriptionId(Context context, int subId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)
+                || !getSubscriptionManager(context).isActiveSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid subscription ID");
+        }
+
+        return new ProvisioningManager(subId);
+    }
+
+    private ProvisioningManager(int subId) {
+        mSubId = subId;
+    }
+
+    /**
+     * Register a new {@link Callback} to listen to changes to changes in
+     * IMS provisioning. Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
+     * Subscription changed events and call
+     * {@link #unregisterProvisioningChangedCallback(Callback)} to clean up after a
+     * subscription is removed.
+     * @param executor The {@link Executor} to call the callback methods on
+     * @param callback The provisioning callbackto be registered.
+     * @see #unregisterProvisioningChangedCallback(Callback)
+     * @see SubscriptionManager.OnSubscriptionsChangedListener
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void registerProvisioningChangedCallback(@CallbackExecutor Executor executor,
+            @NonNull Callback callback) {
+        callback.setExecutor(executor);
+        try {
+            getITelephony().registerImsProvisioningChangedCallback(mSubId,
+                    callback.getBinder());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Unregister an existing {@link Callback}. Ensure to call this method when cleaning
+     * up to avoid memory leaks or when the subscription is removed.
+     * @param callback The existing {@link Callback} to be removed.
+     * @see #registerProvisioningChangedCallback(Executor, Callback)
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void unregisterProvisioningChangedCallback(@NonNull Callback callback) {
+        try {
+            getITelephony().unregisterImsProvisioningChangedCallback(mSubId,
+                    callback.getBinder());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query for the integer value associated with the provided key.
+     * @param key An integer that represents the provisioning key, which is defined by the OEM.
+     * @return an integer value for the provided key.
+     * @throws IllegalArgumentException if the key provided was invalid.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public int getProvisioningIntValue(int key) {
+        try {
+            return getITelephony().getImsProvisioningInt(mSubId, key);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query for the String value associated with the provided key.
+     * @param key An integer that represents the provisioning key, which is defined by the OEM.
+     * @return a String value for the provided key, or {@code null} if the key doesn't exist.
+     * @throws IllegalArgumentException if the key provided was invalid.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public String getProvisioningStringValue(int key) {
+        try {
+            return getITelephony().getImsProvisioningString(mSubId, key);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the integer value associated with the provided key.
+     * @param key An integer that represents the provisioning key, which is defined by the OEM.
+     * @param value a integer value for the provided key.
+     * @return the result of setting the configuration value.
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public @ImsConfigImplBase.SetConfigResult int setProvisioningIntValue(int key, int value) {
+        try {
+            return getITelephony().setImsProvisioningInt(mSubId, key, value);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the String value associated with the provided key.
+     *
+     * @param key An integer that represents the provisioning key, which is defined by the OEM.
+     * @param value a String value for the provided key.
+     * @return the result of setting the configuration value.
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public @ImsConfigImplBase.SetConfigResult int setProvisioningStringValue(int key,
+            String value) {
+        try {
+            return getITelephony().setImsProvisioningString(mSubId, key, value);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    private static SubscriptionManager getSubscriptionManager(Context context) {
+        SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
+        if (manager == null) {
+            throw new RuntimeException("Could not find SubscriptionManager.");
+        }
+        return manager;
+    }
+
+    private static ITelephony getITelephony() {
+        ITelephony binder = ITelephony.Stub.asInterface(
+                ServiceManager.getService(Context.TELEPHONY_SERVICE));
+        if (binder == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+        return binder;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index dcd7ea7..321bfff 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -16,9 +16,9 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.content.Context;
-import android.content.Intent;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.telephony.ims.aidl.IImsConfig;
@@ -28,6 +28,8 @@
 import com.android.ims.ImsConfig;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.HashMap;
 
@@ -215,41 +217,6 @@
     }
 
     /**
-     * Callback that the framework uses for receiving Configuration change updates.
-     * {@hide}
-     */
-    public static class Callback extends IImsConfigCallback.Stub {
-
-        @Override
-        public final void onIntConfigChanged(int item, int value) throws RemoteException {
-            onConfigChanged(item, value);
-        }
-
-        @Override
-        public final void onStringConfigChanged(int item, String value) throws RemoteException {
-            onConfigChanged(item, value);
-        }
-
-        /**
-         * Called when the IMS configuration has changed.
-         * @param item the IMS configuration key constant, as defined in ImsConfig.
-         * @param value the new integer value of the IMS configuration constant.
-         */
-        public void onConfigChanged(int item, int value) {
-            // Base Implementation
-        }
-
-        /**
-         * Called when the IMS configuration has changed.
-         * @param item the IMS configuration key constant, as defined in ImsConfig.
-         * @param value the new String value of the IMS configuration constant.
-         */
-        public void onConfigChanged(int item, String value) {
-            // Base Implementation
-        }
-    }
-
-    /**
      * The configuration requested resulted in an unknown result. This may happen if the
      * IMS configurations are unavailable.
      */
@@ -263,6 +230,16 @@
      */
     public static final int CONFIG_RESULT_FAILED =  1;
 
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CONFIG_RESULT_", value = {
+            CONFIG_RESULT_SUCCESS,
+            CONFIG_RESULT_FAILED
+    })
+    public @interface SetConfigResult {}
+
     private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
     ImsConfigStub mImsConfigStub;
 
@@ -279,17 +256,16 @@
     }
 
     /**
-     * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration
-     * changes.
+     * Adds a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks
+     * notified when a value in the configuration changes.
      * @param c callback to add.
      */
     private void addImsConfigCallback(IImsConfigCallback c) {
         mCallbacks.register(c);
     }
     /**
-     * Removes a {@link Callback} to the list of callbacks notified when a value in the
-     * configuration changes.
-     *
+     * Removes a {@link android.telephony.ims.ProvisioningManager.Callback} to the list of callbacks
+     * notified when a value in the configuration changes.
      * @param c callback to remove.
      */
     private void removeImsConfigCallback(IImsConfigCallback c) {
@@ -370,10 +346,9 @@
      *
      * @param item an integer key.
      * @param value an integer containing the configuration value.
-     * @return the result of setting the configuration value, defined as either
-     * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+     * @return the result of setting the configuration value.
      */
-    public int setConfig(int item, int value) {
+    public @SetConfigResult int setConfig(int item, int value) {
         // Base Implementation - To be overridden.
         return CONFIG_RESULT_FAILED;
     }
@@ -383,10 +358,9 @@
      *
      * @param item an integer key.
      * @param value a String containing the new configuration value.
-     * @return Result of setting the configuration value, defined as either
-     * {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
+     * @return Result of setting the configuration value.
      */
-    public int setConfig(int item, String value) {
+    public @SetConfigResult int setConfig(int item, String value) {
         // Base Implementation - To be overridden.
         return CONFIG_RESULT_FAILED;
     }
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index 90e9880..71a2174 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -16,12 +16,17 @@
 
 package com.android.ims;
 
-import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.telephony.Rlog;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.aidl.IImsConfig;
-import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.aidl.IImsConfigCallback;
+
+import java.util.concurrent.Executor;
 
 /**
  * Provides APIs to get/set the IMS service feature/capability/parameters.
@@ -29,8 +34,10 @@
  * 1) Items provisioned by the operator.
  * 2) Items configured by user. Mainly service feature class.
  *
+ * @deprecated Use {@link  ProvisioningManager} to change these configurations in the ImsService.
  * @hide
  */
+@Deprecated
 public class ImsConfig {
     private static final String TAG = "ImsConfig";
     private boolean DBG = true;
@@ -46,7 +53,7 @@
 
     /**
      * Broadcast action: the configuration was changed
-     * @deprecated Use {@link ImsConfig#addConfigCallback(ImsConfigImplBase.Callback)} instead.
+     * @deprecated Use {@link android.telephony.ims.ProvisioningManager.Callback} instead.
      * @hide
      */
     public static final String ACTION_IMS_CONFIG_CHANGED =
@@ -673,13 +680,25 @@
     }
 
     /**
-     * Adds a {@link ImsConfigImplBase.Callback} to the ImsService to notify when a Configuration
+     * Adds a {@link ProvisioningManager.Callback} to the ImsService to notify when a Configuration
      * item has changed.
      *
-     * Make sure to call {@link #removeConfigCallback(ImsConfigImplBase.Callback)} when finished
+     * Make sure to call {@link #removeConfigCallback(IImsConfigCallback)} when finished
      * using this callback.
      */
-    public void addConfigCallback(ImsConfigImplBase.Callback callback) throws ImsException {
+    public void addConfigCallback(ProvisioningManager.Callback callback) throws ImsException {
+        callback.setExecutor(getThreadExecutor());
+        addConfigCallback(callback.getBinder());
+    }
+
+    /**
+     * Adds a {@link IImsConfigCallback} to the ImsService to notify when a Configuration
+     * item has changed.
+     *
+     * Make sure to call {@link #removeConfigCallback(IImsConfigCallback)} when finished
+     * using this callback.
+     */
+    public void addConfigCallback(IImsConfigCallback callback) throws ImsException {
         if (DBG) Rlog.d(TAG, "addConfigCallback: " + callback);
         try {
             miConfig.addImsConfigCallback(callback);
@@ -690,10 +709,9 @@
     }
 
     /**
-     * Removes a {@link ImsConfigImplBase.Callback} from the ImsService that was previously added
-     * by {@link #addConfigCallback(ImsConfigImplBase.Callback)}.
+     * Removes an existing {@link IImsConfigCallback} from the ImsService.
      */
-    public void removeConfigCallback(ImsConfigImplBase.Callback callback) throws ImsException {
+    public void removeConfigCallback(IImsConfigCallback callback) throws ImsException {
         if (DBG) Rlog.d(TAG, "removeConfigCallback: " + callback);
         try {
             miConfig.removeImsConfigCallback(callback);
@@ -709,4 +727,11 @@
     public boolean isBinderAlive() {
         return miConfig.asBinder().isBinderAlive();
     }
+
+    private Executor getThreadExecutor() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        return new HandlerExecutor(new Handler(Looper.myLooper()));
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 3aaa323..85a9cf5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -40,6 +40,7 @@
 import android.telephony.VisualVoicemailSmsFilterSettings;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
@@ -1569,24 +1570,24 @@
     /**
      * Adds an IMS registration status callback for the subscription id specified.
      */
-    oneway void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+    void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
             String callingPackage);
      /**
       * Removes an existing IMS registration status callback for the subscription specified.
       */
-    oneway void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+    void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
             String callingPackage);
 
     /**
      * Adds an IMS MmTel capabilities callback for the subscription specified.
      */
-    oneway void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+    void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
             String callingPackage);
 
     /**
      * Removes an existing IMS MmTel capabilities callback for the subscription specified.
      */
-    oneway void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+    void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
             String callingPackage);
 
     /**
@@ -1691,4 +1692,34 @@
      * Return a list of certs in hex string from loaded carrier privileges access rules.
      */
     List<String> getCertsFromCarrierPrivilegeAccessRules(int subId);
+
+    /**
+     * Register an IMS provisioning change callback with Telephony.
+     */
+    void registerImsProvisioningChangedCallback(int subId, IImsConfigCallback callback);
+
+    /**
+     * unregister an existing IMS provisioning change callback.
+     */
+    void unregisterImsProvisioningChangedCallback(int subId, IImsConfigCallback callback);
+
+    /**
+     * Return an integer containing the provisioning value for the specified provisioning key.
+     */
+    int getImsProvisioningInt(int subId, int key);
+
+    /**
+     * return a String containing the provisioning value for the provisioning key specified.
+     */
+    String getImsProvisioningString(int subId, int key);
+
+    /**
+     * Set the integer provisioning value for the provisioning key specified.
+     */
+    int setImsProvisioningInt(int subId, int key, int value);
+
+    /**
+     * Set the String provisioning value for the provisioning key specified.
+     */
+    int setImsProvisioningString(int subId, int key, String value);
 }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1fd68ec..7ca3c53 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -61,9 +61,7 @@
 
     ParceledListSlice getPrivilegedConfiguredNetworks();
 
-    WifiConfiguration getMatchingWifiConfig(in ScanResult scanResult);
-
-    List<WifiConfiguration> getAllMatchingWifiConfigs(in ScanResult scanResult);
+    List<WifiConfiguration> getAllMatchingWifiConfigs(in List<ScanResult> scanResult);
 
     List<OsuProvider> getMatchingOsuProviders(in ScanResult scanResult);
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 954b51f..4b6f5fa 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1073,42 +1073,27 @@
     }
 
     /**
-     * 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.
      *
@@ -1119,6 +1104,7 @@
      * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) {
         try {
             return mService.getMatchingOsuProviders(scanResult);
@@ -4256,27 +4242,21 @@
 
     /**
      * @return true if this device supports WPA3-Personal SAE
-     * @hide
      */
-    @SystemApi
     public boolean isWpa3SaeSupported() {
         return isFeatureSupported(WIFI_FEATURE_WPA3_SAE);
     }
 
     /**
      * @return true if this device supports WPA3-Enterprise Suite-B-192
-     * @hide
      */
-    @SystemApi
     public boolean isWpa3SuiteBSupported() {
         return isFeatureSupported(WIFI_FEATURE_WPA3_SUITE_B);
     }
 
     /**
      * @return true if this device supports Wi-Fi Enhanced Open (OWE)
-     * @hide
      */
-    @SystemApi
     public boolean isOweSupported() {
         return isFeatureSupported(WIFI_FEATURE_OWE);
     }
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..6cdef50 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,17 +81,34 @@
         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();
+    }
+
+    @Override
     public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) {
         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..ecf65c9 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 single ScanResult.
+     */
+    @Test
+    public void testGetMatchingOsuProviders() throws Exception {
+        mWifiManager.getMatchingOsuProviders(new ScanResult());
+
+        verify(mWifiService).getMatchingOsuProviders(any(ScanResult.class));
+    }
 }