Merge "Add car_secondary_icon_size and car_padding_0, and fix various existing values."
diff --git a/api/current.txt b/api/current.txt
index ea6f190..99ea60f 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -29000,11 +29000,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();
@@ -43326,6 +43329,7 @@
 
   public class PhoneStateListener {
     ctor public PhoneStateListener();
+    ctor public PhoneStateListener(java.util.concurrent.Executor);
     method public void onCallForwardingIndicatorChanged(boolean);
     method public void onCallStateChanged(int, java.lang.String);
     method public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2c2df81..8eb5507 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";
@@ -5461,6 +5462,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 +5537,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 +6604,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/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index eb498f5..a981997 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -370,11 +370,9 @@
     // This skips the uid map if it's an empty config.
     if (it->second->getNumMetrics() > 0) {
         uint64_t uidMapToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP);
-        if (it->second->hashStringInReport()) {
-            mUidMap->appendUidMap(dumpTimeStampNs, key, &str_set, proto);
-        } else {
-            mUidMap->appendUidMap(dumpTimeStampNs, key, nullptr, proto);
-        }
+        mUidMap->appendUidMap(
+                dumpTimeStampNs, key, it->second->hashStringInReport() ? &str_set : nullptr,
+                it->second->versionStringsInReport(), it->second->installerInReport(), proto);
         proto->end(uidMapToken);
     }
 
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 27685fc..7fa05be 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -787,21 +787,24 @@
 }
 
 Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
-                                      const vector<String16>& app) {
+                                      const vector<String16>& version_string,
+                                      const vector<String16>& app,
+                                      const vector<String16>& installer) {
     ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::informAllUidData was called");
-    mUidMap->updateMap(getElapsedRealtimeNs(), uid, version, app);
+    mUidMap->updateMap(getElapsedRealtimeNs(), uid, version, version_string, app, installer);
     VLOG("StatsService::informAllUidData succeeded");
 
     return Status::ok();
 }
 
-Status StatsService::informOnePackage(const String16& app, int32_t uid, int64_t version) {
+Status StatsService::informOnePackage(const String16& app, int32_t uid, int64_t version,
+                                      const String16& version_string, const String16& installer) {
     ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::informOnePackage was called");
-    mUidMap->updateApp(getElapsedRealtimeNs(), app, uid, version);
+    mUidMap->updateApp(getElapsedRealtimeNs(), app, uid, version, version_string, installer);
     return Status::ok();
 }
 
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 4a5f05f..cd4d601 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -73,8 +73,10 @@
     virtual Status informAlarmForSubscriberTriggeringFired();
 
     virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
-                                    const vector<String16>& app);
-    virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version);
+                                    const vector<String16>& version_string,
+                                    const vector<String16>& app, const vector<String16>& installer);
+    virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version,
+                                    const String16& version_string, const String16& installer);
     virtual Status informOnePackageRemoved(const String16& app, int32_t uid);
     virtual Status informDeviceShutdown();
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 53d9673..244974d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -192,6 +192,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.
@@ -3198,3 +3201,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/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 4244d5b..ac34f47 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -77,6 +77,8 @@
             mActivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, mNoReportMetricIds);
 
     mHashStringsInReport = config.hash_strings_in_metric_report();
+    mVersionStringsInReport = config.version_strings_in_metric_report();
+    mInstallerInReport = config.installer_in_metric_report();
 
     if (config.allowed_log_source_size() == 0) {
         mConfigValid = false;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index a4672b6..a31efbd 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -83,6 +83,14 @@
         return mHashStringsInReport;
     };
 
+    inline bool versionStringsInReport() const {
+        return mVersionStringsInReport;
+    };
+
+    inline bool installerInReport() const {
+        return mInstallerInReport;
+    };
+
     void refreshTtl(const int64_t currentTimestampNs) {
         if (mTtlNs > 0) {
             mTtlEndNs = currentTimestampNs + mTtlNs;
@@ -126,6 +134,8 @@
     bool mConfigValid = false;
 
     bool mHashStringsInReport = false;
+    bool mVersionStringsInReport = false;
+    bool mInstallerInReport = false;
 
     const int64_t mTtlNs;
     int64_t mTtlEndNs;
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 37a0067..59f3f04 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -49,6 +49,10 @@
 const int FIELD_ID_SNAPSHOT_PACKAGE_UID = 3;
 const int FIELD_ID_SNAPSHOT_PACKAGE_DELETED = 4;
 const int FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH = 5;
+const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING = 6;
+const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH = 7;
+const int FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER = 8;
+const int FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH = 9;
 const int FIELD_ID_SNAPSHOT_TIMESTAMP = 1;
 const int FIELD_ID_SNAPSHOT_PACKAGE_INFO = 2;
 const int FIELD_ID_SNAPSHOTS = 1;
@@ -60,6 +64,10 @@
 const int FIELD_ID_CHANGE_NEW_VERSION = 5;
 const int FIELD_ID_CHANGE_PREV_VERSION = 6;
 const int FIELD_ID_CHANGE_PACKAGE_HASH = 7;
+const int FIELD_ID_CHANGE_NEW_VERSION_STRING = 8;
+const int FIELD_ID_CHANGE_PREV_VERSION_STRING = 9;
+const int FIELD_ID_CHANGE_NEW_VERSION_STRING_HASH = 10;
+const int FIELD_ID_CHANGE_PREV_VERSION_STRING_HASH = 11;
 
 UidMap::UidMap() : mBytesUsed(0) {}
 
@@ -104,7 +112,8 @@
 }
 
 void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
-                       const vector<int64_t>& versionCode, const vector<String16>& packageName) {
+                       const vector<int64_t>& versionCode, const vector<String16>& versionString,
+                       const vector<String16>& packageName, const vector<String16>& installer) {
     vector<wp<PackageInfoListener>> broadcastList;
     {
         lock_guard<mutex> lock(mMutex);  // Exclusively lock for updates.
@@ -121,7 +130,9 @@
         mMap.clear();
         for (size_t j = 0; j < uid.size(); j++) {
             string package = string(String8(packageName[j]).string());
-            mMap[std::make_pair(uid[j], package)] = AppData(versionCode[j]);
+            mMap[std::make_pair(uid[j], package)] =
+                    AppData(versionCode[j], string(String8(versionString[j]).string()),
+                            string(String8(installer[j]).string()));
         }
 
         for (const auto& kv : deletedApps) {
@@ -150,23 +161,30 @@
 }
 
 void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid,
-                       const int64_t& versionCode) {
+                       const int64_t& versionCode, const String16& versionString,
+                       const String16& installer) {
     vector<wp<PackageInfoListener>> broadcastList;
     string appName = string(String8(app_16).string());
     {
         lock_guard<mutex> lock(mMutex);
         int32_t prevVersion = 0;
+        string prevVersionString = "";
+        string newVersionString = string(String8(versionString).string());
         bool found = false;
         auto it = mMap.find(std::make_pair(uid, appName));
         if (it != mMap.end()) {
             found = true;
             prevVersion = it->second.versionCode;
+            prevVersionString = it->second.versionString;
             it->second.versionCode = versionCode;
+            it->second.versionString = newVersionString;
+            it->second.installer = string(String8(installer).string());
             it->second.deleted = false;
         }
         if (!found) {
             // Otherwise, we need to add an app at this uid.
-            mMap[std::make_pair(uid, appName)] = AppData(versionCode);
+            mMap[std::make_pair(uid, appName)] =
+                    AppData(versionCode, newVersionString, string(String8(installer).string()));
         } else {
             // Only notify the listeners if this is an app upgrade. If this app is being installed
             // for the first time, then we don't notify the listeners.
@@ -174,7 +192,8 @@
             // app after deletion.
             getListenerListCopyLocked(&broadcastList);
         }
-        mChanges.emplace_back(false, timestamp, appName, uid, versionCode, prevVersion);
+        mChanges.emplace_back(false, timestamp, appName, uid, versionCode, newVersionString,
+                              prevVersion, prevVersionString);
         mBytesUsed += kBytesChangeRecord;
         ensureBytesUsedBelowLimit();
         StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
@@ -226,10 +245,12 @@
         lock_guard<mutex> lock(mMutex);
 
         int64_t prevVersion = 0;
+        string prevVersionString = "";
         auto key = std::make_pair(uid, app);
         auto it = mMap.find(key);
         if (it != mMap.end() && !it->second.deleted) {
             prevVersion = it->second.versionCode;
+            prevVersionString = it->second.versionString;
             it->second.deleted = true;
             mDeletedApps.push_back(key);
         }
@@ -240,7 +261,7 @@
             mMap.erase(oldest);
             StatsdStats::getInstance().noteUidMapAppDeletionDropped();
         }
-        mChanges.emplace_back(true, timestamp, app, uid, 0, prevVersion);
+        mChanges.emplace_back(true, timestamp, app, uid, 0, "", prevVersion, prevVersionString);
         mBytesUsed += kBytesChangeRecord;
         ensureBytesUsedBelowLimit();
         StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
@@ -315,8 +336,9 @@
     return mBytesUsed;
 }
 
-void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key,
-                          std::set<string> *str_set, ProtoOutputStream* proto) {
+void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set,
+                          bool includeVersionStrings, bool includeInstaller,
+                          ProtoOutputStream* proto) {
     lock_guard<mutex> lock(mMutex);  // Lock for updates
 
     for (const ChangeRecord& record : mChanges) {
@@ -330,8 +352,22 @@
                 str_set->insert(record.package);
                 proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_PACKAGE_HASH,
                              (long long)Hash64(record.package));
+                if (includeVersionStrings) {
+                    str_set->insert(record.versionString);
+                    proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_NEW_VERSION_STRING_HASH,
+                                 (long long)Hash64(record.versionString));
+                    str_set->insert(record.prevVersionString);
+                    proto->write(FIELD_TYPE_UINT64 | FIELD_ID_CHANGE_PREV_VERSION_STRING_HASH,
+                                 (long long)Hash64(record.prevVersionString));
+                }
             } else {
                 proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package);
+                if (includeVersionStrings) {
+                    proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_NEW_VERSION_STRING,
+                                 record.versionString);
+                    proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PREV_VERSION_STRING,
+                                 record.prevVersionString);
+                }
             }
 
             proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid);
@@ -354,8 +390,26 @@
             str_set->insert(kv.first.second);
             proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH,
                          (long long)Hash64(kv.first.second));
+            if (includeVersionStrings) {
+                str_set->insert(kv.second.versionString);
+                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH,
+                             (long long)Hash64(kv.second.versionString));
+            }
+            if (includeInstaller) {
+                str_set->insert(kv.second.installer);
+                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH,
+                             (long long)Hash64(kv.second.installer));
+            }
         } else {
             proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
+            if (includeVersionStrings) {
+                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING,
+                             kv.second.versionString);
+            }
+            if (includeInstaller) {
+                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER,
+                             kv.second.installer);
+            }
         }
 
         proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
@@ -391,8 +445,9 @@
 
     for (const auto& kv : mMap) {
         if (!kv.second.deleted) {
-            dprintf(out, "%s, v%" PRId64 " (%i)\n", kv.first.second.c_str(), kv.second.versionCode,
-                    kv.first.first);
+            dprintf(out, "%s, v%" PRId64 ", %s, %s (%i)\n", kv.first.second.c_str(),
+                    kv.second.versionCode, kv.second.versionString.c_str(),
+                    kv.second.installer.c_str(), kv.first.first);
         }
     }
 }
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 4598369..75ff507 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -44,12 +44,16 @@
 
 struct AppData {
     int64_t versionCode;
+    string versionString;
+    string installer;
     bool deleted;
 
     // Empty constructor needed for unordered map.
     AppData() {
     }
-    AppData(const int64_t v) : versionCode(v), deleted(false){};
+
+    AppData(const int64_t v, const string& versionString, const string& installer)
+        : versionCode(v), versionString(versionString), installer(installer), deleted(false){};
 };
 
 // When calling appendUidMap, we retrieve all the ChangeRecords since the last
@@ -61,15 +65,20 @@
     const int32_t uid;
     const int64_t version;
     const int64_t prevVersion;
+    const string versionString;
+    const string prevVersionString;
 
     ChangeRecord(const bool isDeletion, const int64_t timestampNs, const string& package,
-                 const int32_t uid, const int64_t version, const int64_t prevVersion)
+                 const int32_t uid, const int64_t version, const string versionString,
+                 const int64_t prevVersion, const string prevVersionString)
         : deletion(isDeletion),
           timestampNs(timestampNs),
           package(package),
           uid(uid),
           version(version),
-          prevVersion(prevVersion) {
+          prevVersion(prevVersion),
+          versionString(versionString),
+          prevVersionString(prevVersionString) {
     }
 };
 
@@ -87,10 +96,12 @@
      * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
      */
     void updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
-                   const vector<int64_t>& versionCode, const vector<String16>& packageName);
+                   const vector<int64_t>& versionCode, const vector<String16>& versionString,
+                   const vector<String16>& packageName, const vector<String16>& installer);
 
     void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid,
-                   const int64_t& versionCode);
+                   const int64_t& versionCode, const String16& versionString,
+                   const String16& installer);
     void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid);
 
     // Returns true if the given uid contains the specified app (eg. com.google.android.gms).
@@ -127,8 +138,9 @@
     // Gets all snapshots and changes that have occurred since the last output.
     // If every config key has received a change or snapshot record, then this
     // record is deleted.
-    void appendUidMap(const int64_t& timestamp, const ConfigKey& key,
-                      std::set<string> *str_set, util::ProtoOutputStream* proto);
+    void appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set,
+                      bool includeVersionStrings, bool includeInstaller,
+                      util::ProtoOutputStream* proto);
 
     // Forces the output to be cleared. We still generate a snapshot based on the current state.
     // This results in extra data uploaded but helps us reconstruct the uid mapping on the server
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 5d0f3d1..32ee5af 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -233,6 +233,14 @@
             optional bool deleted = 4;
 
             optional uint64 name_hash = 5;
+
+            optional string version_string = 6;
+
+            optional uint64 version_string_hash = 7;
+
+            optional string installer = 8;
+
+            optional uint64 installer_hash = 9;
         }
         optional int64 elapsed_timestamp_nanos = 1;
 
@@ -250,6 +258,10 @@
         optional int64 new_version = 5;
         optional int64 prev_version = 6;
         optional uint64 app_hash = 7;
+        optional string new_version_string = 8;
+        optional string prev_version_string = 9;
+        optional uint64 new_version_string_hash = 10;
+        optional uint64 prev_version_string_hash = 11;
     }
     repeated Change changes = 2;
 }
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index aa789c7..f955df2 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -409,6 +409,10 @@
 
   repeated MetricActivation metric_activation = 17;
 
+  optional bool version_strings_in_metric_report = 18;
+
+  optional bool installer_in_metric_report = 19;
+
   // Field number 1000 is reserved for later use.
   reserved 1000;
 }
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 4c6671d..2b9528f 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -148,8 +148,12 @@
 
     uidMap.updateMap(
             1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+             android::String16("v1"), android::String16("v2")},
             {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
-             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */);
+             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+            {android::String16(""), android::String16(""), android::String16(""),
+             android::String16(""), android::String16("")});
 
     EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
@@ -297,8 +301,12 @@
     UidMap uidMap;
     uidMap.updateMap(
             1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+             android::String16("v1"), android::String16("v2")},
             {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
-             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */);
+             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+            {android::String16(""), android::String16(""), android::String16(""),
+             android::String16(""), android::String16("")});
 
     AttributionNodeInternal attribution_node1;
     attribution_node1.set_uid(1111);
@@ -372,8 +380,12 @@
     UidMap uidMap;
     uidMap.updateMap(
             1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+             android::String16("v1"), android::String16("v2")},
             {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
-             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */);
+             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+            {android::String16(""), android::String16(""), android::String16(""),
+             android::String16(""), android::String16("")});
 
     AttributionNodeInternal attribution_node1;
     attribution_node1.set_uid(1067);
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 8864252..355df29 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -153,7 +153,8 @@
     // Setup simple config key corresponding to empty config.
     sp<UidMap> m = new UidMap();
     sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    m->updateMap(1, {1, 2}, {1, 2}, {String16("p1"), String16("p2")});
+    m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")},
+                 {String16("p1"), String16("p2")}, {String16(""), String16("")});
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> subscriberAlarmMonitor;
     int broadcastCount = 0;
@@ -182,7 +183,8 @@
     // Setup simple config key corresponding to empty config.
     sp<UidMap> m = new UidMap();
     sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    m->updateMap(1, {1, 2}, {1, 2}, {String16("p1"), String16("p2")});
+    m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")},
+                 {String16("p1"), String16("p2")}, {String16(""), String16("")});
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> subscriberAlarmMonitor;
     int broadcastCount = 0;
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index 99082cc..f0d9cf1 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -71,14 +71,20 @@
     vector<int32_t> uids;
     vector<int64_t> versions;
     vector<String16> apps;
+    vector<String16> versionStrings;
+    vector<String16> installers;
 
     uids.push_back(1000);
     uids.push_back(1000);
+    versionStrings.push_back(String16("v1"));
+    versionStrings.push_back(String16("v1"));
+    installers.push_back(String16(""));
+    installers.push_back(String16(""));
     apps.push_back(String16(kApp1.c_str()));
     apps.push_back(String16(kApp2.c_str()));
     versions.push_back(4);
     versions.push_back(5);
-    m.updateMap(1, uids, versions, apps);
+    m.updateMap(1, uids, versions, versionStrings, apps, installers);
     EXPECT_TRUE(m.hasApp(1000, kApp1));
     EXPECT_TRUE(m.hasApp(1000, kApp2));
     EXPECT_FALSE(m.hasApp(1000, "not.app"));
@@ -97,14 +103,20 @@
     vector<int32_t> uids;
     vector<int64_t> versions;
     vector<String16> apps;
+    vector<String16> versionStrings;
+    vector<String16> installers;
 
     uids.push_back(1000);
     uids.push_back(1000);
+    versionStrings.push_back(String16("v1"));
+    versionStrings.push_back(String16("v1"));
+    installers.push_back(String16(""));
+    installers.push_back(String16(""));
     apps.push_back(String16(kApp1.c_str()));
     apps.push_back(String16(kApp2.c_str()));
     versions.push_back(4);
     versions.push_back(5);
-    m.updateMap(1, uids, versions, apps);
+    m.updateMap(1, uids, versions, versionStrings, apps, installers);
 
     std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
     EXPECT_EQ(name_set.size(), 2u);
@@ -112,7 +124,7 @@
     EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
 
     // Update the app1 version.
-    m.updateApp(2, String16(kApp1.c_str()), 1000, 40);
+    m.updateApp(2, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16(""));
     EXPECT_EQ(40, m.getAppVersion(1000, kApp1));
 
     name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
@@ -138,14 +150,15 @@
 
 TEST(UidMapTest, TestUpdateApp) {
     UidMap m;
-    m.updateMap(1, {1000, 1000}, {4, 5}, {String16(kApp1.c_str()), String16(kApp2.c_str())});
+    m.updateMap(1, {1000, 1000}, {4, 5}, {String16("v4"), String16("v5")},
+                {String16(kApp1.c_str()), String16(kApp2.c_str())}, {String16(""), String16("")});
     std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
     EXPECT_EQ(name_set.size(), 2u);
     EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
     EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
 
     // Adds a new name for uid 1000.
-    m.updateApp(2, String16("NeW_aPP1_NAmE"), 1000, 40);
+    m.updateApp(2, String16("NeW_aPP1_NAmE"), 1000, 40, String16("v40"), String16(""));
     name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
     EXPECT_EQ(name_set.size(), 3u);
     EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
@@ -154,7 +167,7 @@
     EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end());
 
     // This name is also reused by another uid 2000.
-    m.updateApp(3, String16("NeW_aPP1_NAmE"), 2000, 1);
+    m.updateApp(3, String16("NeW_aPP1_NAmE"), 2000, 1, String16("v1"), String16(""));
     name_set = m.getAppNamesFromUid(2000, true /* returnNormalized */);
     EXPECT_EQ(name_set.size(), 1u);
     EXPECT_TRUE(name_set.find("NeW_aPP1_NAmE") == name_set.end());
@@ -185,21 +198,26 @@
     vector<int32_t> uids;
     vector<int64_t> versions;
     vector<String16> apps;
+    vector<String16> versionStrings;
+    vector<String16> installers;
     uids.push_back(1000);
     apps.push_back(String16(kApp2.c_str()));
+    versionStrings.push_back(String16("v1"));
+    installers.push_back(String16(""));
     versions.push_back(5);
-    m.updateMap(1, uids, versions, apps);
+    m.updateMap(1, uids, versions, versionStrings, apps, installers);
 
     // Set the last timestamp for this config key to be newer.
     m.mLastUpdatePerConfigKey[config1] = 2;
 
     ProtoOutputStream proto;
-    m.appendUidMap(3, config1, nullptr, &proto);
+    m.appendUidMap(3, config1, nullptr, true, true, &proto);
 
     // Check there's still a uidmap attached this one.
     UidMapping results;
     protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
+    EXPECT_EQ("v1", results.snapshots(0).package_info(0).version_string());
 }
 
 TEST(UidMapTest, TestRemovedAppRetained) {
@@ -209,15 +227,19 @@
     m.OnConfigUpdated(config1);
     vector<int32_t> uids;
     vector<int64_t> versions;
+    vector<String16> versionStrings;
+    vector<String16> installers;
     vector<String16> apps;
     uids.push_back(1000);
     apps.push_back(String16(kApp2.c_str()));
     versions.push_back(5);
-    m.updateMap(1, uids, versions, apps);
+    versionStrings.push_back(String16("v5"));
+    installers.push_back(String16(""));
+    m.updateMap(1, uids, versions, versionStrings, apps, installers);
     m.removeApp(2, String16(kApp2.c_str()), 1000);
 
     ProtoOutputStream proto;
-    m.appendUidMap(3, config1, nullptr, &proto);
+    m.appendUidMap(3, config1, nullptr, true, true, &proto);
 
     // Snapshot should still contain this item as deleted.
     UidMapping results;
@@ -233,30 +255,34 @@
     m.OnConfigUpdated(config1);
     vector<int32_t> uids;
     vector<int64_t> versions;
+    vector<String16> versionStrings;
+    vector<String16> installers;
     vector<String16> apps;
     const int maxDeletedApps = StatsdStats::kMaxDeletedAppsInUidMap;
     for (int j = 0; j < maxDeletedApps + 10; j++) {
         uids.push_back(j);
         apps.push_back(String16(kApp1.c_str()));
         versions.push_back(j);
+        versionStrings.push_back(String16("v"));
+        installers.push_back(String16(""));
     }
-    m.updateMap(1, uids, versions, apps);
+    m.updateMap(1, uids, versions, versionStrings, apps, installers);
 
     // First, verify that we have the expected number of items.
     UidMapping results;
     ProtoOutputStream proto;
-    m.appendUidMap(3, config1, nullptr, &proto);
+    m.appendUidMap(3, config1, nullptr, true, true, &proto);
     protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(maxDeletedApps + 10, results.snapshots(0).package_info_size());
 
     // Now remove all the apps.
-    m.updateMap(1, uids, versions, apps);
+    m.updateMap(1, uids, versions, versionStrings, apps, installers);
     for (int j = 0; j < maxDeletedApps + 10; j++) {
         m.removeApp(4, String16(kApp1.c_str()), j);
     }
 
     proto.clear();
-    m.appendUidMap(5, config1, nullptr, &proto);
+    m.appendUidMap(5, config1, nullptr, true, true, &proto);
     // Snapshot drops the first nine items.
     protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(maxDeletedApps, results.snapshots(0).package_info_size());
@@ -272,6 +298,8 @@
 
     vector<int32_t> uids;
     vector<int64_t> versions;
+    vector<String16> versionStrings;
+    vector<String16> installers;
     vector<String16> apps;
     uids.push_back(1000);
     uids.push_back(1000);
@@ -279,45 +307,49 @@
     apps.push_back(String16(kApp2.c_str()));
     versions.push_back(4);
     versions.push_back(5);
-    m.updateMap(1, uids, versions, apps);
+    versionStrings.push_back(String16("v4"));
+    versionStrings.push_back(String16("v5"));
+    installers.push_back(String16(""));
+    installers.push_back(String16(""));
+    m.updateMap(1, uids, versions, versionStrings, apps, installers);
 
     ProtoOutputStream proto;
-    m.appendUidMap(2, config1, nullptr, &proto);
+    m.appendUidMap(2, config1, nullptr, true, true, &proto);
     UidMapping results;
     protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
 
     // We have to keep at least one snapshot in memory at all times.
     proto.clear();
-    m.appendUidMap(2, config1, nullptr, &proto);
+    m.appendUidMap(2, config1, nullptr, true, true, &proto);
     protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
 
     // Now add another configuration.
     m.OnConfigUpdated(config2);
-    m.updateApp(5, String16(kApp1.c_str()), 1000, 40);
+    m.updateApp(5, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16(""));
     EXPECT_EQ(1U, m.mChanges.size());
     proto.clear();
-    m.appendUidMap(6, config1, nullptr, &proto);
+    m.appendUidMap(6, config1, nullptr, true, true, &proto);
     protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
     EXPECT_EQ(1, results.changes_size());
     EXPECT_EQ(1U, m.mChanges.size());
 
     // Add another delta update.
-    m.updateApp(7, String16(kApp2.c_str()), 1001, 41);
+    m.updateApp(7, String16(kApp2.c_str()), 1001, 41, String16("v41"), String16(""));
     EXPECT_EQ(2U, m.mChanges.size());
 
     // We still can't remove anything.
     proto.clear();
-    m.appendUidMap(8, config1, nullptr, &proto);
+    m.appendUidMap(8, config1, nullptr, true, true, &proto);
     protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
     EXPECT_EQ(1, results.changes_size());
     EXPECT_EQ(2U, m.mChanges.size());
 
     proto.clear();
-    m.appendUidMap(9, config2, nullptr, &proto);
+    m.appendUidMap(9, config2, nullptr, true, true, &proto);
     protoOutputStreamToUidMapping(&proto, &results);
     EXPECT_EQ(1, results.snapshots_size());
     EXPECT_EQ(2, results.changes_size());
@@ -335,19 +367,23 @@
     vector<int32_t> uids;
     vector<int64_t> versions;
     vector<String16> apps;
+    vector<String16> versionStrings;
+    vector<String16> installers;
     uids.push_back(1000);
     apps.push_back(String16(kApp1.c_str()));
     versions.push_back(1);
-    m.updateMap(1, uids, versions, apps);
+    versionStrings.push_back(String16("v1"));
+    installers.push_back(String16(""));
+    m.updateMap(1, uids, versions, versionStrings, apps, installers);
 
-    m.updateApp(3, String16(kApp1.c_str()), 1000, 40);
+    m.updateApp(3, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16(""));
 
     ProtoOutputStream proto;
     vector<uint8_t> bytes;
-    m.appendUidMap(2, config1, nullptr, &proto);
+    m.appendUidMap(2, config1, nullptr, true, true, &proto);
     size_t prevBytes = m.mBytesUsed;
 
-    m.appendUidMap(4, config1, nullptr, &proto);
+    m.appendUidMap(4, config1, nullptr, true, true, &proto);
     EXPECT_TRUE(m.mBytesUsed < prevBytes);
 }
 
@@ -361,21 +397,27 @@
     size_t startBytes = m.mBytesUsed;
     vector<int32_t> uids;
     vector<int64_t> versions;
+    vector<String16> versionStrings;
+    vector<String16> installers;
     vector<String16> apps;
     for (int i = 0; i < 100; i++) {
         uids.push_back(1);
         buf = "EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY." + to_string(i);
         apps.push_back(String16(buf.c_str()));
         versions.push_back(1);
+        versionStrings.push_back(String16("v1"));
+        installers.push_back(String16(""));
     }
-    m.updateMap(1, uids, versions, apps);
+    m.updateMap(1, uids, versions, versionStrings, apps, installers);
 
-    m.updateApp(3, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 2);
+    m.updateApp(3, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 2,
+                String16("v2"), String16(""));
     EXPECT_EQ(1U, m.mChanges.size());
 
     // Now force deletion by limiting the memory to hold one delta change.
-    m.maxBytesOverride = 80; // Since the app string alone requires >45 characters.
-    m.updateApp(5, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 4);
+    m.maxBytesOverride = 120; // Since the app string alone requires >45 characters.
+    m.updateApp(5, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 4,
+                String16("v4"), String16(""));
     EXPECT_EQ(1U, m.mChanges.size());
 }
 
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index 5c47af7..a9841c9 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -69,8 +69,10 @@
     // Here it assumes that GMS core has two uids.
     processor->getUidMap()->updateMap(
             1, {222, 444, 111, 333}, {1, 1, 2, 2},
+            {String16("v1"), String16("v1"), String16("v2"), String16("v2")},
             {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"),
-             String16("APP3")});
+             String16("APP3")},
+            {String16(""), String16(""), String16(""), String16("")});
 
     // GMS core node is in the middle.
     std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
@@ -215,8 +217,10 @@
     // Here it assumes that GMS core has two uids.
     processor->getUidMap()->updateMap(
             1, {222, 444, 111, 333}, {1, 1, 2, 2},
+            {String16("v1"), String16("v1"), String16("v2"), String16("v2")},
             {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"),
-             String16("APP3")});
+             String16("APP3")},
+            {String16(""), String16(""), String16(""), String16("")});
 
     // GMS core node is in the middle.
     std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 3cb553f..2b0285b 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -132,7 +132,8 @@
     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
     // This is a new installation, so there shouldn't be a split (should be same as the without
     // split case).
-    service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2);
+    service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+                               String16(""));
     // Goes into the second bucket.
     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
 
@@ -145,11 +146,13 @@
     SendConfig(service, MakeConfig());
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
-    service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())});
+    service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+                               {String16("")});
 
     // Force the uidmap to update at timestamp 2.
     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
-    service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2);
+    service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+                               String16(""));
     // Goes into the second bucket.
     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
 
@@ -168,7 +171,8 @@
     SendConfig(service, MakeConfig());
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
-    service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())});
+    service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+                               {String16("")});
 
     // Force the uidmap to update at timestamp 2.
     service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
@@ -189,13 +193,14 @@
 TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
     StatsService service(nullptr);
     // Partial buckets don't occur when app is first installed.
-    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
+    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
     SendConfig(service, MakeValueMetricConfig(0));
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
     service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-    service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2);
+    service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
+                               String16("v2"), String16(""));
 
     ConfigMetricsReport report =
             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
@@ -206,14 +211,15 @@
 TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
     StatsService service(nullptr);
     // Partial buckets don't occur when app is first installed.
-    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
+    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
     SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */));
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
     const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
     service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-    service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2);
+    service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
+                               String16(""));
 
     ConfigMetricsReport report =
             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
@@ -229,13 +235,14 @@
 TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
     StatsService service(nullptr);
     // Partial buckets don't occur when app is first installed.
-    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
+    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
     SendConfig(service, MakeGaugeMetricConfig(0));
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
     service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-    service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2);
+    service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
+                               String16("v2"), String16(""));
 
     ConfigMetricsReport report =
             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
@@ -246,14 +253,15 @@
 TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
     StatsService service(nullptr);
     // Partial buckets don't occur when app is first installed.
-    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1);
+    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
     SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */));
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
     const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
     service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-    service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2);
+    service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
+                               String16(""));
 
     ConfigMetricsReport report =
             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 851e35b..39b327c 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1320,18 +1320,6 @@
 Landroid/security/Credentials;->convertToPem([Ljava/security/cert/Certificate;)[B
 Landroid/security/IKeyChainService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/security/IKeyChainService;
 Landroid/security/IKeyChainService;->requestPrivateKey(Ljava/lang/String;)Ljava/lang/String;
-Landroid/security/IKeystoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/security/IKeystoreService;
-Landroid/security/IKeystoreService;->clear_uid(J)I
-Landroid/security/IKeystoreService;->del(Ljava/lang/String;I)I
-Landroid/security/IKeystoreService;->exist(Ljava/lang/String;I)I
-Landroid/security/IKeystoreService;->generateKey(Ljava/lang/String;Landroid/security/keymaster/KeymasterArguments;[BIILandroid/security/keymaster/KeyCharacteristics;)I
-Landroid/security/IKeystoreService;->get(Ljava/lang/String;I)[B
-Landroid/security/IKeystoreService;->getState(I)I
-Landroid/security/IKeystoreService;->insert(Ljava/lang/String;[BII)I
-Landroid/security/IKeystoreService;->is_hardware_backed(Ljava/lang/String;)I
-Landroid/security/IKeystoreService;->list(Ljava/lang/String;I)[Ljava/lang/String;
-Landroid/security/IKeystoreService;->reset()I
-Landroid/security/IKeystoreService;->ungrant(Ljava/lang/String;I)I
 Landroid/security/keymaster/KeymasterBlobArgument;-><init>(ILandroid/os/Parcel;)V
 Landroid/security/keymaster/KeymasterBlobArgument;-><init>(I[B)V
 Landroid/security/keymaster/KeymasterBlobArgument;->blob:[B
@@ -1343,6 +1331,17 @@
 Landroid/security/keymaster/KeymasterLongArgument;-><init>(IJ)V
 Landroid/security/keymaster/KeymasterLongArgument;-><init>(ILandroid/os/Parcel;)V
 Landroid/security/keymaster/KeymasterLongArgument;->value:J
+Landroid/security/keystore/IKeystoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/security/keystore/IKeystoreService;
+Landroid/security/keystore/IKeystoreService;->clear_uid(J)I
+Landroid/security/keystore/IKeystoreService;->del(Ljava/lang/String;I)I
+Landroid/security/keystore/IKeystoreService;->exist(Ljava/lang/String;I)I
+Landroid/security/keystore/IKeystoreService;->get(Ljava/lang/String;I)[B
+Landroid/security/keystore/IKeystoreService;->getState(I)I
+Landroid/security/keystore/IKeystoreService;->insert(Ljava/lang/String;[BII)I
+Landroid/security/keystore/IKeystoreService;->is_hardware_backed(Ljava/lang/String;)I
+Landroid/security/keystore/IKeystoreService;->list(Ljava/lang/String;I)[Ljava/lang/String;
+Landroid/security/keystore/IKeystoreService;->reset()I
+Landroid/security/keystore/IKeystoreService;->ungrant(Ljava/lang/String;I)I
 Landroid/service/carrier/ICarrierMessagingCallback$Stub;-><init>()V
 Landroid/service/carrier/ICarrierMessagingService;->filterSms(Landroid/service/carrier/MessagePdu;Ljava/lang/String;IILandroid/service/carrier/ICarrierMessagingCallback;)V
 Landroid/service/dreams/IDreamManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/dreams/IDreamManager;
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/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/Intent.java b/core/java/android/content/Intent.java
index 02f38a7..e9b2404 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -7508,7 +7508,7 @@
      *
      * @see #putExtra(String, String)
      */
-    public String getStringExtra(String name) {
+    public @Nullable String getStringExtra(String name) {
         return mExtras == null ? null : mExtras.getString(name);
     }
 
@@ -7522,7 +7522,7 @@
      *
      * @see #putExtra(String, CharSequence)
      */
-    public CharSequence getCharSequenceExtra(String name) {
+    public @Nullable CharSequence getCharSequenceExtra(String name) {
         return mExtras == null ? null : mExtras.getCharSequence(name);
     }
 
@@ -7536,7 +7536,7 @@
      *
      * @see #putExtra(String, Parcelable)
      */
-    public <T extends Parcelable> T getParcelableExtra(String name) {
+    public @Nullable <T extends Parcelable> T getParcelableExtra(String name) {
         return mExtras == null ? null : mExtras.<T>getParcelable(name);
     }
 
@@ -7550,7 +7550,7 @@
      *
      * @see #putExtra(String, Parcelable[])
      */
-    public Parcelable[] getParcelableArrayExtra(String name) {
+    public @Nullable Parcelable[] getParcelableArrayExtra(String name) {
         return mExtras == null ? null : mExtras.getParcelableArray(name);
     }
 
@@ -7565,7 +7565,7 @@
      *
      * @see #putParcelableArrayListExtra(String, ArrayList)
      */
-    public <T extends Parcelable> ArrayList<T> getParcelableArrayListExtra(String name) {
+    public @Nullable <T extends Parcelable> ArrayList<T> getParcelableArrayListExtra(String name) {
         return mExtras == null ? null : mExtras.<T>getParcelableArrayList(name);
     }
 
@@ -7579,7 +7579,7 @@
      *
      * @see #putExtra(String, Serializable)
      */
-    public Serializable getSerializableExtra(String name) {
+    public @Nullable Serializable getSerializableExtra(String name) {
         return mExtras == null ? null : mExtras.getSerializable(name);
     }
 
@@ -7594,7 +7594,7 @@
      *
      * @see #putIntegerArrayListExtra(String, ArrayList)
      */
-    public ArrayList<Integer> getIntegerArrayListExtra(String name) {
+    public @Nullable ArrayList<Integer> getIntegerArrayListExtra(String name) {
         return mExtras == null ? null : mExtras.getIntegerArrayList(name);
     }
 
@@ -7609,7 +7609,7 @@
      *
      * @see #putStringArrayListExtra(String, ArrayList)
      */
-    public ArrayList<String> getStringArrayListExtra(String name) {
+    public @Nullable ArrayList<String> getStringArrayListExtra(String name) {
         return mExtras == null ? null : mExtras.getStringArrayList(name);
     }
 
@@ -7624,7 +7624,7 @@
      *
      * @see #putCharSequenceArrayListExtra(String, ArrayList)
      */
-    public ArrayList<CharSequence> getCharSequenceArrayListExtra(String name) {
+    public @Nullable ArrayList<CharSequence> getCharSequenceArrayListExtra(String name) {
         return mExtras == null ? null : mExtras.getCharSequenceArrayList(name);
     }
 
@@ -7638,7 +7638,7 @@
      *
      * @see #putExtra(String, boolean[])
      */
-    public boolean[] getBooleanArrayExtra(String name) {
+    public @Nullable boolean[] getBooleanArrayExtra(String name) {
         return mExtras == null ? null : mExtras.getBooleanArray(name);
     }
 
@@ -7652,7 +7652,7 @@
      *
      * @see #putExtra(String, byte[])
      */
-    public byte[] getByteArrayExtra(String name) {
+    public @Nullable byte[] getByteArrayExtra(String name) {
         return mExtras == null ? null : mExtras.getByteArray(name);
     }
 
@@ -7666,7 +7666,7 @@
      *
      * @see #putExtra(String, short[])
      */
-    public short[] getShortArrayExtra(String name) {
+    public @Nullable short[] getShortArrayExtra(String name) {
         return mExtras == null ? null : mExtras.getShortArray(name);
     }
 
@@ -7680,7 +7680,7 @@
      *
      * @see #putExtra(String, char[])
      */
-    public char[] getCharArrayExtra(String name) {
+    public @Nullable char[] getCharArrayExtra(String name) {
         return mExtras == null ? null : mExtras.getCharArray(name);
     }
 
@@ -7694,7 +7694,7 @@
      *
      * @see #putExtra(String, int[])
      */
-    public int[] getIntArrayExtra(String name) {
+    public @Nullable int[] getIntArrayExtra(String name) {
         return mExtras == null ? null : mExtras.getIntArray(name);
     }
 
@@ -7708,7 +7708,7 @@
      *
      * @see #putExtra(String, long[])
      */
-    public long[] getLongArrayExtra(String name) {
+    public @Nullable long[] getLongArrayExtra(String name) {
         return mExtras == null ? null : mExtras.getLongArray(name);
     }
 
@@ -7722,7 +7722,7 @@
      *
      * @see #putExtra(String, float[])
      */
-    public float[] getFloatArrayExtra(String name) {
+    public @Nullable float[] getFloatArrayExtra(String name) {
         return mExtras == null ? null : mExtras.getFloatArray(name);
     }
 
@@ -7736,7 +7736,7 @@
      *
      * @see #putExtra(String, double[])
      */
-    public double[] getDoubleArrayExtra(String name) {
+    public @Nullable double[] getDoubleArrayExtra(String name) {
         return mExtras == null ? null : mExtras.getDoubleArray(name);
     }
 
@@ -7750,7 +7750,7 @@
      *
      * @see #putExtra(String, String[])
      */
-    public String[] getStringArrayExtra(String name) {
+    public @Nullable String[] getStringArrayExtra(String name) {
         return mExtras == null ? null : mExtras.getStringArray(name);
     }
 
@@ -7764,7 +7764,7 @@
      *
      * @see #putExtra(String, CharSequence[])
      */
-    public CharSequence[] getCharSequenceArrayExtra(String name) {
+    public @Nullable CharSequence[] getCharSequenceArrayExtra(String name) {
         return mExtras == null ? null : mExtras.getCharSequenceArray(name);
     }
 
@@ -7778,7 +7778,7 @@
      *
      * @see #putExtra(String, Bundle)
      */
-    public Bundle getBundleExtra(String name) {
+    public @Nullable Bundle getBundleExtra(String name) {
         return mExtras == null ? null : mExtras.getBundle(name);
     }
 
@@ -8584,7 +8584,7 @@
      * @see #removeExtra
      * @see #getStringExtra(String)
      */
-    public @NonNull Intent putExtra(String name, String value) {
+    public @NonNull Intent putExtra(String name, @Nullable String value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8607,7 +8607,7 @@
      * @see #removeExtra
      * @see #getCharSequenceExtra(String)
      */
-    public @NonNull Intent putExtra(String name, CharSequence value) {
+    public @NonNull Intent putExtra(String name, @Nullable CharSequence value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8630,7 +8630,7 @@
      * @see #removeExtra
      * @see #getParcelableExtra(String)
      */
-    public @NonNull Intent putExtra(String name, Parcelable value) {
+    public @NonNull Intent putExtra(String name, @Nullable Parcelable value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8653,7 +8653,7 @@
      * @see #removeExtra
      * @see #getParcelableArrayExtra(String)
      */
-    public @NonNull Intent putExtra(String name, Parcelable[] value) {
+    public @NonNull Intent putExtra(String name, @Nullable Parcelable[] value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8677,7 +8677,7 @@
      * @see #getParcelableArrayListExtra(String)
      */
     public @NonNull Intent putParcelableArrayListExtra(String name,
-            ArrayList<? extends Parcelable> value) {
+            @Nullable ArrayList<? extends Parcelable> value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8700,7 +8700,8 @@
      * @see #removeExtra
      * @see #getIntegerArrayListExtra(String)
      */
-    public @NonNull Intent putIntegerArrayListExtra(String name, ArrayList<Integer> value) {
+    public @NonNull Intent putIntegerArrayListExtra(String name,
+            @Nullable ArrayList<Integer> value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8723,7 +8724,7 @@
      * @see #removeExtra
      * @see #getStringArrayListExtra(String)
      */
-    public @NonNull Intent putStringArrayListExtra(String name, ArrayList<String> value) {
+    public @NonNull Intent putStringArrayListExtra(String name, @Nullable ArrayList<String> value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8747,7 +8748,7 @@
      * @see #getCharSequenceArrayListExtra(String)
      */
     public @NonNull Intent putCharSequenceArrayListExtra(String name,
-            ArrayList<CharSequence> value) {
+            @Nullable ArrayList<CharSequence> value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8770,7 +8771,7 @@
      * @see #removeExtra
      * @see #getSerializableExtra(String)
      */
-    public @NonNull Intent putExtra(String name, Serializable value) {
+    public @NonNull Intent putExtra(String name, @Nullable Serializable value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8793,7 +8794,7 @@
      * @see #removeExtra
      * @see #getBooleanArrayExtra(String)
      */
-    public @NonNull Intent putExtra(String name, boolean[] value) {
+    public @NonNull Intent putExtra(String name, @Nullable boolean[] value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8816,7 +8817,7 @@
      * @see #removeExtra
      * @see #getByteArrayExtra(String)
      */
-    public @NonNull Intent putExtra(String name, byte[] value) {
+    public @NonNull Intent putExtra(String name, @Nullable byte[] value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8839,7 +8840,7 @@
      * @see #removeExtra
      * @see #getShortArrayExtra(String)
      */
-    public @NonNull Intent putExtra(String name, short[] value) {
+    public @NonNull Intent putExtra(String name, @Nullable short[] value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8862,7 +8863,7 @@
      * @see #removeExtra
      * @see #getCharArrayExtra(String)
      */
-    public @NonNull Intent putExtra(String name, char[] value) {
+    public @NonNull Intent putExtra(String name, @Nullable char[] value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8885,7 +8886,7 @@
      * @see #removeExtra
      * @see #getIntArrayExtra(String)
      */
-    public @NonNull Intent putExtra(String name, int[] value) {
+    public @NonNull Intent putExtra(String name, @Nullable int[] value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8908,7 +8909,7 @@
      * @see #removeExtra
      * @see #getLongArrayExtra(String)
      */
-    public @NonNull Intent putExtra(String name, long[] value) {
+    public @NonNull Intent putExtra(String name, @Nullable long[] value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8931,7 +8932,7 @@
      * @see #removeExtra
      * @see #getFloatArrayExtra(String)
      */
-    public @NonNull Intent putExtra(String name, float[] value) {
+    public @NonNull Intent putExtra(String name, @Nullable float[] value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8954,7 +8955,7 @@
      * @see #removeExtra
      * @see #getDoubleArrayExtra(String)
      */
-    public @NonNull Intent putExtra(String name, double[] value) {
+    public @NonNull Intent putExtra(String name, @Nullable double[] value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -8977,7 +8978,7 @@
      * @see #removeExtra
      * @see #getStringArrayExtra(String)
      */
-    public @NonNull Intent putExtra(String name, String[] value) {
+    public @NonNull Intent putExtra(String name, @Nullable String[] value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -9000,7 +9001,7 @@
      * @see #removeExtra
      * @see #getCharSequenceArrayExtra(String)
      */
-    public @NonNull Intent putExtra(String name, CharSequence[] value) {
+    public @NonNull Intent putExtra(String name, @Nullable CharSequence[] value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
@@ -9023,7 +9024,7 @@
      * @see #removeExtra
      * @see #getBundleExtra(String)
      */
-    public @NonNull Intent putExtra(String name, Bundle value) {
+    public @NonNull Intent putExtra(String name, @Nullable Bundle value) {
         if (mExtras == null) {
             mExtras = new Bundle();
         }
diff --git a/core/java/android/content/MimeTypeFilter.java b/core/java/android/content/MimeTypeFilter.java
new file mode 100644
index 0000000..1c26fd9
--- /dev/null
+++ b/core/java/android/content/MimeTypeFilter.java
@@ -0,0 +1,154 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+
+/**
+ * Provides utility methods for matching MIME type filters used in ContentProvider.
+ *
+ * <p>Wildcards are allowed only instead of the entire type or subtype with a tree prefix.
+ * Eg. image\/*, *\/* is a valid filter and will match image/jpeg, but image/j* is invalid and
+ * it will not match image/jpeg. Suffixes and parameters are not supported, and they are treated
+ * as part of the subtype during matching. Neither type nor subtype can be empty.
+ *
+ * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike the formal
+ * RFC definitions. As a result, you should always write these elements with lower case letters,
+ * or use {@link android.content.Intent#normalizeMimeType} to ensure that they are converted to
+ * lower case.</em>
+ *
+ * <p>MIME types can be null or ill-formatted. In such case they won't match anything.
+ *
+ * <p>MIME type filters must be correctly formatted, or an exception will be thrown.
+ * Copied from support library.
+ * {@hide}
+ */
+public final class MimeTypeFilter {
+
+    private MimeTypeFilter() {
+    }
+
+    private static boolean mimeTypeAgainstFilter(
+            @NonNull String[] mimeTypeParts, @NonNull String[] filterParts) {
+        if (filterParts.length != 2) {
+            throw new IllegalArgumentException(
+                    "Ill-formatted MIME type filter. Must be type/subtype.");
+        }
+        if (filterParts[0].isEmpty() || filterParts[1].isEmpty()) {
+            throw new IllegalArgumentException(
+                    "Ill-formatted MIME type filter. Type or subtype empty.");
+        }
+        if (mimeTypeParts.length != 2) {
+            return false;
+        }
+        if (!"*".equals(filterParts[0])
+                && !filterParts[0].equals(mimeTypeParts[0])) {
+            return false;
+        }
+        if (!"*".equals(filterParts[1])
+                && !filterParts[1].equals(mimeTypeParts[1])) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Matches one nullable MIME type against one MIME type filter.
+     * @return True if the {@code mimeType} matches the {@code filter}.
+     */
+    public static boolean matches(@Nullable String mimeType, @NonNull String filter) {
+        if (mimeType == null) {
+            return false;
+        }
+
+        final String[] mimeTypeParts = mimeType.split("/");
+        final String[] filterParts = filter.split("/");
+
+        return mimeTypeAgainstFilter(mimeTypeParts, filterParts);
+    }
+
+    /**
+     * Matches one nullable MIME type against an array of MIME type filters.
+     * @return The first matching filter, or null if nothing matches.
+     */
+    @Nullable
+    public static String matches(
+            @Nullable String mimeType, @NonNull String[] filters) {
+        if (mimeType == null) {
+            return null;
+        }
+
+        final String[] mimeTypeParts = mimeType.split("/");
+        for (String filter : filters) {
+            final String[] filterParts = filter.split("/");
+            if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+                return filter;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Matches multiple MIME types against an array of MIME type filters.
+     * @return The first matching MIME type, or null if nothing matches.
+     */
+    @Nullable
+    public static String matches(
+            @Nullable String[] mimeTypes, @NonNull String filter) {
+        if (mimeTypes == null) {
+            return null;
+        }
+
+        final String[] filterParts = filter.split("/");
+        for (String mimeType : mimeTypes) {
+            final String[] mimeTypeParts = mimeType.split("/");
+            if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+                return mimeType;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Matches multiple MIME types against an array of MIME type filters.
+     * @return The list of matching MIME types, or empty array if nothing matches.
+     */
+    @NonNull
+    public static String[] matchesMany(
+            @Nullable String[] mimeTypes, @NonNull String filter) {
+        if (mimeTypes == null) {
+            return new String[] {};
+        }
+
+        final ArrayList<String> list = new ArrayList<>();
+        final String[] filterParts = filter.split("/");
+        for (String mimeType : mimeTypes) {
+            final String[] mimeTypeParts = mimeType.split("/");
+            if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+                list.add(mimeType);
+            }
+        }
+
+        return list.toArray(new String[list.size()]);
+    }
+}
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/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 9cf7de5..c437dde 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2421,7 +2421,7 @@
 
     public static final IntToString[] HISTORY_EVENT_INT_FORMATTERS = new IntToString[] {
             sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sUidToString,
-            sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sUidToString,
+            sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sIntToString,
             sUidToString, sUidToString, sUidToString, sUidToString, sUidToString, sUidToString,
             sUidToString, sUidToString, sUidToString, sIntToString
     };
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 1f47f93..28ea553 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1390,3 +1390,4 @@
         }
     }
 }
+
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 124f207..74d434c 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -62,12 +62,15 @@
      * Inform statsd what the version and package are for each uid. Note that each array should
      * have the same number of elements, and version[i] and package[i] correspond to uid[i].
      */
-    oneway void informAllUidData(in int[] uid, in long[] version, in String[] app);
+    oneway void informAllUidData(in int[] uid, in long[] version, in String[] version_string,
+        in String[] app, in String[] installer);
 
     /**
-     * Inform statsd what the uid and version are for one app that was updated.
+     * Inform statsd what the uid, version, version_string, and installer are for one app that was
+     * updated.
      */
-    oneway void informOnePackage(in String app, in int uid, in long version);
+    oneway void informOnePackage(in String app, in int uid, in long version,
+        in String version_string, in String installer);
 
     /**
      * Inform stats that an app was removed.
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 126588a..44b9e311 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -1056,6 +1056,9 @@
     /**
      * Internal class representing a remote status read by
      * {@link ParcelFileDescriptor#readCommStatus(FileDescriptor, byte[])}.
+     *
+     * Warning: this must be kept in sync with ParcelFileDescriptorStatus at
+     * frameworks/native/libs/binder/Parcel.cpp
      */
     private static class Status {
         /** Special value indicating remote side died. */
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/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 67e52aa..16d454d 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -16,12 +16,11 @@
 
 package android.provider;
 
-import static android.system.OsConstants.SEEK_SET;
-
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
 import static com.android.internal.util.Preconditions.checkCollectionNotEmpty;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ContentProviderClient;
@@ -29,13 +28,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.MimeTypeFilter;
 import android.content.pm.ResolveInfo;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.ImageDecoder;
-import android.graphics.Matrix;
 import android.graphics.Point;
 import android.media.ExifInterface;
 import android.net.Uri;
@@ -50,20 +48,13 @@
 import android.os.ParcelableException;
 import android.os.RemoteException;
 import android.os.storage.StorageVolume;
-import android.system.ErrnoException;
-import android.system.Os;
 import android.util.DataUnit;
 import android.util.Log;
-import android.util.Size;
 
-import libcore.io.IoUtils;
-
-import java.io.BufferedInputStream;
 import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
@@ -113,6 +104,54 @@
     public static final String EXTRA_TARGET_URI = "android.content.extra.TARGET_URI";
 
     /**
+     * Key for {@link DocumentsProvider} to query display name is matched.
+     * The match of display name is partial matching and case-insensitive.
+     * Ex: The value is "o", the display name of the results will contain
+     * both "foo" and "Open".
+     *
+     * @see DocumentsProvider#querySearchDocuments(String, String[],
+     *      Bundle)
+     * {@hide}
+     */
+    public static final String QUERY_ARG_DISPLAY_NAME = "android:query-arg-display-name";
+
+    /**
+     * Key for {@link DocumentsProvider} to query mime types is matched.
+     * The value is a string array, it can support different mime types.
+     * Each items will be treated as "OR" condition. Ex: {"image/*" ,
+     * "video/*"}. The mime types of the results will contain both image
+     * type and video type.
+     *
+     * @see DocumentsProvider#querySearchDocuments(String, String[],
+     *      Bundle)
+     * {@hide}
+     */
+    public static final String QUERY_ARG_MIME_TYPES = "android:query-arg-mime-types";
+
+    /**
+     * Key for {@link DocumentsProvider} to query the file size in bytes is
+     * larger than the value.
+     *
+     * @see DocumentsProvider#querySearchDocuments(String, String[],
+     *      Bundle)
+     * {@hide}
+     */
+    public static final String QUERY_ARG_FILE_SIZE_OVER = "android:query-arg-file-size-over";
+
+    /**
+     * Key for {@link DocumentsProvider} to query the last modified time
+     * is newer than the value. The unit is in milliseconds since
+     * January 1, 1970 00:00:00.0 UTC.
+     *
+     * @see DocumentsProvider#querySearchDocuments(String, String[],
+     *      Bundle)
+     * @see Document#COLUMN_LAST_MODIFIED
+     * {@hide}
+     */
+    public static final String QUERY_ARG_LAST_MODIFIED_AFTER =
+            "android:query-arg-last-modified-after";
+
+    /**
      * Sets the desired initial location visible to user when file chooser is shown.
      *
      * <p>Applicable to {@link Intent} with actions:
@@ -929,6 +968,89 @@
     }
 
     /**
+     * Check if the values match the query arguments.
+     *
+     * @param queryArgs the query arguments
+     * @param displayName the display time to check against
+     * @param mimeType the mime type to check against
+     * @param lastModified the last modified time to check against
+     * @param size the size to check against
+     * @hide
+     */
+    public static boolean matchSearchQueryArguments(Bundle queryArgs, String displayName,
+            String mimeType, long lastModified, long size) {
+        if (queryArgs == null) {
+            return true;
+        }
+
+        final String argDisplayName = queryArgs.getString(QUERY_ARG_DISPLAY_NAME, "");
+        if (!argDisplayName.isEmpty()) {
+            // TODO (118795812) : Enhance the search string handled in DocumentsProvider
+            if (!displayName.toLowerCase().contains(argDisplayName.toLowerCase())) {
+                return false;
+            }
+        }
+
+        final long argFileSize = queryArgs.getLong(QUERY_ARG_FILE_SIZE_OVER, -1 /* defaultValue */);
+        if (argFileSize != -1 && size < argFileSize) {
+            return false;
+        }
+
+        final long argLastModified = queryArgs.getLong(QUERY_ARG_LAST_MODIFIED_AFTER,
+                -1 /* defaultValue */);
+        if (argLastModified != -1 && lastModified < argLastModified) {
+            return false;
+        }
+
+        final String[] argMimeTypes = queryArgs.getStringArray(QUERY_ARG_MIME_TYPES);
+        if (argMimeTypes != null && argMimeTypes.length > 0) {
+            mimeType = Intent.normalizeMimeType(mimeType);
+            for (String type : argMimeTypes) {
+                if (MimeTypeFilter.matches(mimeType, Intent.normalizeMimeType(type))) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Get the handled query arguments from the query bundle. The handled arguments are
+     * {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME},
+     * {@link DocumentsContract#QUERY_ARG_MIME_TYPES},
+     * {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER} and
+     * {@link DocumentsContract#QUERY_ARG_LAST_MODIFIED_AFTER}.
+     *
+     * @param queryArgs the query arguments to be parsed.
+     * @return the handled query arguments
+     * @hide
+     */
+    public static String[] getHandledQueryArguments(Bundle queryArgs) {
+        if (queryArgs == null) {
+            return new String[0];
+        }
+
+        final ArrayList<String> args = new ArrayList<>();
+        if (queryArgs.keySet().contains(QUERY_ARG_DISPLAY_NAME)) {
+            args.add(QUERY_ARG_DISPLAY_NAME);
+        }
+
+        if (queryArgs.keySet().contains(QUERY_ARG_FILE_SIZE_OVER)) {
+            args.add(QUERY_ARG_FILE_SIZE_OVER);
+        }
+
+        if (queryArgs.keySet().contains(QUERY_ARG_LAST_MODIFIED_AFTER)) {
+            args.add(QUERY_ARG_LAST_MODIFIED_AFTER);
+        }
+
+        if (queryArgs.keySet().contains(QUERY_ARG_MIME_TYPES)) {
+            args.add(QUERY_ARG_MIME_TYPES);
+        }
+        return args.toArray(new String[0]);
+    }
+
+    /**
      * Test if the given URI represents a {@link Document} backed by a
      * {@link DocumentsProvider}.
      *
@@ -1052,6 +1174,15 @@
         return searchDocumentsUri.getQueryParameter(PARAM_QUERY);
     }
 
+    /**
+     * Extract the search query from a Bundle
+     * {@link #QUERY_ARG_DISPLAY_NAME}.
+     * {@hide}
+     */
+    public static String getSearchDocumentsQuery(@NonNull Bundle bundle) {
+        return bundle.getString(QUERY_ARG_DISPLAY_NAME, "" /* defaultValue */);
+    }
+
     /** {@hide} */
     @UnsupportedAppUsage
     public static Uri setManageMode(Uri uri) {
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 68f8acd..58f8213 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -32,7 +32,6 @@
 import static android.provider.DocumentsContract.buildTreeDocumentUri;
 import static android.provider.DocumentsContract.getDocumentId;
 import static android.provider.DocumentsContract.getRootId;
-import static android.provider.DocumentsContract.getSearchDocumentsQuery;
 import static android.provider.DocumentsContract.getTreeDocumentId;
 import static android.provider.DocumentsContract.isTreeUri;
 
@@ -47,6 +46,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.MimeTypeFilter;
 import android.content.UriMatcher;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
@@ -651,6 +651,55 @@
     }
 
     /**
+     * Return documents that match the given query under the requested
+     * root. The returned documents should be sorted by relevance in descending
+     * order. How documents are matched against the query string is an
+     * implementation detail left to each provider, but it's suggested that at
+     * least {@link Document#COLUMN_DISPLAY_NAME} be matched in a
+     * case-insensitive fashion.
+     * <p>
+     * If your provider is cloud-based, and you have some data cached or pinned
+     * locally, you may return the local data immediately, setting
+     * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that
+     * you are still fetching additional data. Then, when the network data is
+     * available, you can send a change notification to trigger a requery and
+     * return the complete contents.
+     * <p>
+     * To support change notifications, you must
+     * {@link Cursor#setNotificationUri(ContentResolver, Uri)} with a relevant
+     * Uri, such as {@link DocumentsContract#buildSearchDocumentsUri(String,
+     * String, String)}. Then you can call {@link ContentResolver#notifyChange(Uri,
+     * android.database.ContentObserver, boolean)} with that Uri to send change
+     * notifications.
+     *
+     * @param rootId the root to search under.
+     * @param projection list of {@link Document} columns to put into the
+     *            cursor. If {@code null} all supported columns should be
+     *            included.
+     * @param queryArgs the query arguments.
+     *            {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME},
+     *            {@link DocumentsContract#QUERY_ARG_MIME_TYPES},
+     *            {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER},
+     *            {@link DocumentsContract#QUERY_ARG_LAST_MODIFIED_AFTER}.
+     * @return cursor containing search result. Include
+     *         {@link ContentResolver#EXTRA_HONORED_ARGS} in {@link Cursor}
+     *         extras {@link Bundle} when any QUERY_ARG_* value was honored
+     *         during the preparation of the results.
+     *
+     * @see ContentResolver#EXTRA_HONORED_ARGS
+     * @see DocumentsContract#EXTRA_LOADING
+     * @see DocumentsContract#EXTRA_INFO
+     * @see DocumentsContract#EXTRA_ERROR
+     * {@hide}
+     */
+    @SuppressWarnings("unused")
+    public Cursor querySearchDocuments(String rootId, String[] projection, Bundle queryArgs)
+            throws FileNotFoundException {
+        return querySearchDocuments(rootId, DocumentsContract.getSearchDocumentsQuery(queryArgs),
+                projection);
+    }
+
+    /**
      * Ejects the root. Throws {@link IllegalStateException} if ejection failed.
      *
      * @param rootId the root to be ejected.
@@ -795,7 +844,7 @@
      *      {@link #queryDocument(String, String[])},
      *      {@link #queryRecentDocuments(String, String[])},
      *      {@link #queryRoots(String[])}, and
-     *      {@link #querySearchDocuments(String, String, String[])}.
+     *      {@link #querySearchDocuments(String, String[], Bundle)}.
      */
     @Override
     public Cursor query(Uri uri, String[] projection, String selection,
@@ -812,7 +861,7 @@
      * @see #queryRecentDocuments(String, String[], Bundle, CancellationSignal)
      * @see #queryDocument(String, String[])
      * @see #queryChildDocuments(String, String[], String)
-     * @see #querySearchDocuments(String, String, String[])
+     * @see #querySearchDocuments(String, String[], Bundle)
      */
     @Override
     public final Cursor query(
@@ -825,8 +874,7 @@
                     return queryRecentDocuments(
                             getRootId(uri), projection, queryArgs, cancellationSignal);
                 case MATCH_SEARCH:
-                    return querySearchDocuments(
-                            getRootId(uri), getSearchDocumentsQuery(uri), projection);
+                    return querySearchDocuments(getRootId(uri), projection, queryArgs);
                 case MATCH_DOCUMENT:
                 case MATCH_DOCUMENT_TREE:
                     enforceTree(uri);
@@ -1301,7 +1349,7 @@
                 final long flags =
                     cursor.getLong(cursor.getColumnIndexOrThrow(Document.COLUMN_FLAGS));
                 if ((flags & Document.FLAG_VIRTUAL_DOCUMENT) == 0 && mimeType != null &&
-                        mimeTypeMatches(mimeTypeFilter, mimeType)) {
+                        MimeTypeFilter.matches(mimeType, mimeTypeFilter)) {
                     return new String[] { mimeType };
                 }
             }
@@ -1354,21 +1402,4 @@
         // For any other yet unhandled case, let the provider subclass handle it.
         return openTypedDocument(documentId, mimeTypeFilter, opts, signal);
     }
-
-    /**
-     * @hide
-     */
-    public static boolean mimeTypeMatches(String filter, String test) {
-        if (test == null) {
-            return false;
-        } else if (filter == null || "*/*".equals(filter)) {
-            return true;
-        } else if (filter.equals(test)) {
-            return true;
-        } else if (filter.endsWith("/*")) {
-            return filter.regionMatches(0, test, 0, filter.indexOf('/'));
-        } else {
-            return false;
-        }
-    }
 }
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/security/keymaster/ExportResult.java b/core/java/android/security/keymaster/ExportResult.java
index c104671..1ab79fb 100644
--- a/core/java/android/security/keymaster/ExportResult.java
+++ b/core/java/android/security/keymaster/ExportResult.java
@@ -28,6 +28,11 @@
     public final int resultCode;
     public final byte[] exportData;
 
+    public ExportResult(int resultCode) {
+        this.resultCode = resultCode;
+        this.exportData = new byte[0];
+    }
+
     @UnsupportedAppUsage
     public static final Parcelable.Creator<ExportResult> CREATOR = new
             Parcelable.Creator<ExportResult>() {
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.java b/core/java/android/security/keymaster/KeyCharacteristics.java
index 555863e..a4fe75d 100644
--- a/core/java/android/security/keymaster/KeyCharacteristics.java
+++ b/core/java/android/security/keymaster/KeyCharacteristics.java
@@ -52,6 +52,14 @@
         readFromParcel(in);
     }
 
+    /**
+     * Makes a shallow copy of other by copying the other's references to the KeymasterArguments
+     */
+    public void shallowCopyFrom(KeyCharacteristics other) {
+        this.swEnforced = other.swEnforced;
+        this.hwEnforced = other.hwEnforced;
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/security/keymaster/KeymasterCertificateChain.java b/core/java/android/security/keymaster/KeymasterCertificateChain.java
index 243b9fe..00a1a1c 100644
--- a/core/java/android/security/keymaster/KeymasterCertificateChain.java
+++ b/core/java/android/security/keymaster/KeymasterCertificateChain.java
@@ -54,6 +54,14 @@
         readFromParcel(in);
     }
 
+    /**
+     * Makes a shallow copy of other by copying the reference to the certificate chain list.
+     * @param other
+     */
+    public void shallowCopyFrom(KeymasterCertificateChain other) {
+        this.mCertificates = other.mCertificates;
+    }
+
     public List<byte[]> getCertificates() {
         return mCertificates;
     }
diff --git a/core/java/android/security/keymaster/OperationResult.java b/core/java/android/security/keymaster/OperationResult.java
index 2943211..bc4f360 100644
--- a/core/java/android/security/keymaster/OperationResult.java
+++ b/core/java/android/security/keymaster/OperationResult.java
@@ -59,6 +59,10 @@
         this.outParams = outParams;
     }
 
+    public OperationResult(int resultCode) {
+        this(resultCode, null, 0, 0, null, null);
+    }
+
     protected OperationResult(Parcel in) {
         resultCode = in.readInt();
         token = in.readStrongBinder();
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/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/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
index c02fb32..b74600b 100644
--- a/core/java/android/view/intelligence/IntelligenceManager.java
+++ b/core/java/android/view/intelligence/IntelligenceManager.java
@@ -86,6 +86,13 @@
      */
     public static final int STATE_ACTIVE = 2;
 
+    /**
+     * Session is disabled.
+     *
+     * @hide
+     */
+    public static final int STATE_DISABLED = 3;
+
     private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
 
     /**
@@ -166,13 +173,7 @@
                             public void send(int resultCode, Bundle resultData)
                                     throws RemoteException {
                                 synchronized (mLock) {
-                                    if (resultCode > 0) {
-                                        mState = STATE_ACTIVE;
-                                    } else {
-                                        // TODO(b/111276913): handle other cases like disabled by
-                                        // service
-                                        resetStateLocked();
-                                    }
+                                    mState = resultCode;
                                     if (VERBOSE) {
                                         Log.v(TAG, "onActivityStarted() result: code=" + resultCode
                                                 + ", id=" + mId
@@ -203,9 +204,13 @@
                     // Typically happens on system apps that are started before the system service
                     // is ready (like com.android.settings/.FallbackHome)
                     //TODO(b/111276913): try to ignore session while system is not ready / boot
-                    // not complete instead.
-                    Log.w(TAG, "Closing session for " + getActivityDebugNameLocked()
-                            + " after " + numberEvents + " delayed events");
+                    // not complete instead. Similarly, the manager service should return right away
+                    // when the user does not have a service set
+                    if (VERBOSE) {
+                        Log.v(TAG, "Closing session for " + getActivityDebugNameLocked()
+                                + " after " + numberEvents + " delayed events and state "
+                                + getStateAsString(mState));
+                    }
                     // TODO(b/111276913): blacklist activity / use special flag to indicate that
                     // when it's launched again
                     resetStateLocked();
@@ -380,7 +385,7 @@
         //TODO(b/111276913): properly implement by checking if it was explicitly disabled by
         // service, or if service is not set
         // (and probably renamign to isEnabledLocked()
-        return mService != null;
+        return mService != null && mState != STATE_DISABLED;
     }
 
     /**
@@ -509,6 +514,8 @@
                 return "WAITING_FOR_SERVER";
             case STATE_ACTIVE:
                 return "ACTIVE";
+            case STATE_DISABLED:
+                return "DISABLED";
             default:
                 return "INVALID:" + state;
         }
diff --git a/core/java/android/view/textclassifier/ModelFileManager.java b/core/java/android/view/textclassifier/ModelFileManager.java
index 896b516..8558a46 100644
--- a/core/java/android/view/textclassifier/ModelFileManager.java
+++ b/core/java/android/view/textclassifier/ModelFileManager.java
@@ -251,6 +251,9 @@
             if (!mLanguageIndependent && model.mLanguageIndependent) {
                 return true;
             }
+            if (mLanguageIndependent && !model.mLanguageIndependent) {
+                return false;
+            }
 
             // A higher-version model is preferred.
             if (mVersion > model.getVersion()) {
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/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 81dab2f..8bc90a8 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -389,14 +389,18 @@
      * @param query the search condition used to match file names
      * @param projection projection of the returned cursor
      * @param exclusion absolute file paths to exclude from result
-     * @return cursor containing search result
+     * @param queryArgs the query arguments for search
+     * @return cursor containing search result. Include
+     *         {@link ContentResolver#EXTRA_HONORED_ARGS} in {@link Cursor}
+     *         extras {@link Bundle} when any QUERY_ARG_* value was honored
+     *         during the preparation of the results.
      * @throws FileNotFoundException when root folder doesn't exist or search fails
+     *
+     * @see ContentResolver#EXTRA_HONORED_ARGS
      */
     protected final Cursor querySearchDocuments(
-            File folder, String query, String[] projection, Set<String> exclusion)
+            File folder, String[] projection, Set<String> exclusion, Bundle queryArgs)
             throws FileNotFoundException {
-
-        query = query.toLowerCase();
         final MatrixCursor result = new MatrixCursor(resolveProjection(projection));
         final LinkedList<File> pending = new LinkedList<>();
         pending.add(folder);
@@ -407,11 +411,18 @@
                     pending.add(child);
                 }
             }
-            if (file.getName().toLowerCase().contains(query)
-                    && !exclusion.contains(file.getAbsolutePath())) {
+            if (!exclusion.contains(file.getAbsolutePath()) && matchSearchQueryArguments(file,
+                    queryArgs)) {
                 includeFile(result, null, file);
             }
         }
+
+        final String[] handledQueryArgs = DocumentsContract.getHandledQueryArguments(queryArgs);
+        if (handledQueryArgs.length > 0) {
+            final Bundle extras = new Bundle();
+            extras.putStringArray(ContentResolver.EXTRA_HONORED_ARGS, handledQueryArgs);
+            result.setExtras(extras);
+        }
         return result;
     }
 
@@ -457,6 +468,34 @@
         }
     }
 
+    /**
+     * Test if the file matches the query arguments.
+     *
+     * @param file the file to test
+     * @param queryArgs the query arguments
+     */
+    private boolean matchSearchQueryArguments(File file, Bundle queryArgs) {
+        if (file == null) {
+            return false;
+        }
+
+        final String fileMimeType;
+        final String fileName = file.getName();
+
+        if (file.isDirectory()) {
+            fileMimeType = DocumentsContract.Document.MIME_TYPE_DIR;
+        } else {
+            int dotPos = fileName.lastIndexOf('.');
+            if (dotPos < 0) {
+                return false;
+            }
+            final String extension = fileName.substring(dotPos + 1);
+            fileMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+        }
+        return DocumentsContract.matchSearchQueryArguments(queryArgs, fileName, fileMimeType,
+                file.lastModified(), file.length());
+    }
+
     private void scanFile(File visibleFile) {
         final Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
         intent.setData(Uri.fromFile(visibleFile));
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/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/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/android/view/textclassifier/ModelFileManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
index f2efabf..88d162b 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
@@ -203,6 +203,28 @@
     }
 
     @Test
+    public void findBestModel_languageIsMoreImportantThanVersion_bestModelComesFirst() {
+        ModelFileManager.ModelFile matchLocaleModel =
+                new ModelFileManager.ModelFile(
+                        new File("/path/b"), 1,
+                        Collections.singletonList(Locale.forLanguageTag("ja")), false);
+
+        ModelFileManager.ModelFile languageIndependentModel =
+                new ModelFileManager.ModelFile(
+                        new File("/path/a"), 2,
+                        Collections.emptyList(), true);
+        when(mModelFileSupplier.get())
+                .thenReturn(
+                        Arrays.asList(matchLocaleModel, languageIndependentModel));
+
+        ModelFileManager.ModelFile bestModelFile =
+                mModelFileManager.findBestModelFile(
+                        LocaleList.forLanguageTags("ja"));
+
+        assertThat(bestModelFile).isEqualTo(matchLocaleModel);
+    }
+
+    @Test
     public void modelFileEquals() {
         ModelFileManager.ModelFile modelA =
                 new ModelFileManager.ModelFile(
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/Android.mk b/data/etc/Android.mk
index 936ad22..d24c140 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -47,3 +47,11 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 include $(BUILD_PREBUILT)
+
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.android.timezone.updater.xml
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_RELATIVE_PATH := permissions
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
diff --git a/data/etc/com.android.timezone.updater.xml b/data/etc/com.android.timezone.updater.xml
new file mode 100644
index 0000000..60a66e2
--- /dev/null
+++ b/data/etc/com.android.timezone.updater.xml
@@ -0,0 +1,22 @@
+<?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
+  -->
+<permissions>
+    <privapp-permissions package="com.android.timezone.updater">
+        <permission name="android.permission.QUERY_TIME_ZONE_RULES" />
+        <permission name="android.permission.UPDATE_TIME_ZONE_RULES" />
+    </privapp-permissions>
+</permissions>
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 c10e482..835b735 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -38,11 +38,13 @@
 import android.security.keymaster.KeymasterCertificateChain;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keymaster.OperationResult;
+import android.security.keystore.IKeystoreService;
 import android.security.keystore.KeyExpiredException;
 import android.security.keystore.KeyNotYetValidException;
 import android.security.keystore.KeyPermanentlyInvalidatedException;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
+import android.security.keystore.KeystoreResponse;
 import android.security.keystore.StrongBoxUnavailableException;
 import android.security.keystore.UserNotAuthenticatedException;
 import android.util.Log;
@@ -52,8 +54,11 @@
 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;
+import java.util.concurrent.ExecutionException;
 import sun.security.util.ObjectIdentifier;
 import sun.security.x509.AlgorithmId;
 
@@ -303,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);
     }
@@ -451,27 +481,107 @@
 
     public boolean addRngEntropy(byte[] data, int flags) {
         try {
-            return mBinder.addRngEntropy(data, flags) == NO_ERROR;
+            KeystoreResultPromise promise = new KeystoreResultPromise();
+            int errorCode = mBinder.addRngEntropy(promise, data, flags);
+            if (errorCode == NO_ERROR) {
+                return promise.getFuture().get().getErrorCode() == NO_ERROR;
+            } else {
+                return false;
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return false;
+        } catch (ExecutionException | InterruptedException e) {
+            Log.e(TAG, "AddRngEntropy completed with exception", e);
+            return false;
         }
     }
 
+    private class KeyCharacteristicsCallbackResult {
+        private KeystoreResponse keystoreResponse;
+        private KeyCharacteristics keyCharacteristics;
+
+        public KeyCharacteristicsCallbackResult(KeystoreResponse keystoreResponse,
+                                                KeyCharacteristics keyCharacteristics) {
+            this.keystoreResponse = keystoreResponse;
+            this.keyCharacteristics = keyCharacteristics;
+        }
+
+        public KeystoreResponse getKeystoreResponse() {
+            return keystoreResponse;
+        }
+
+        public void setKeystoreResponse(KeystoreResponse keystoreResponse) {
+            this.keystoreResponse = keystoreResponse;
+        }
+
+        public KeyCharacteristics getKeyCharacteristics() {
+            return keyCharacteristics;
+        }
+
+        public void setKeyCharacteristics(KeyCharacteristics keyCharacteristics) {
+            this.keyCharacteristics = keyCharacteristics;
+        }
+    }
+
+    private class KeyCharacteristicsPromise
+    extends android.security.keystore.IKeystoreKeyCharacteristicsCallback.Stub {
+        final private CompletableFuture<KeyCharacteristicsCallbackResult> future =
+                new CompletableFuture<KeyCharacteristicsCallbackResult>();
+        @Override
+        public void onFinished(KeystoreResponse keystoreResponse,
+                               KeyCharacteristics keyCharacteristics)
+                                       throws android.os.RemoteException {
+            future.complete(
+                    new KeyCharacteristicsCallbackResult(keystoreResponse, keyCharacteristics));
+        }
+        public final CompletableFuture<KeyCharacteristicsCallbackResult> getFuture() {
+            return future;
+        }
+    };
+
+    private int generateKeyInternal(String alias, KeymasterArguments args, byte[] entropy, int uid,
+            int flags, KeyCharacteristics outCharacteristics)
+                    throws RemoteException, ExecutionException, InterruptedException {
+        KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
+        int error = mBinder.generateKey(promise, alias, args, entropy, uid, flags);
+        if (error != NO_ERROR) {
+            Log.e(TAG, "generateKeyInternal failed on request " + error);
+            return error;
+        }
+
+        KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+        error = result.getKeystoreResponse().getErrorCode();
+        if (error != NO_ERROR) {
+            Log.e(TAG, "generateKeyInternal failed on response " + error);
+            return error;
+        }
+        KeyCharacteristics characteristics = result.getKeyCharacteristics();
+        if (characteristics == null) {
+            Log.e(TAG, "generateKeyInternal got empty key cheractariestics " + error);
+            return SYSTEM_ERROR;
+        }
+        outCharacteristics.shallowCopyFrom(characteristics);
+        return NO_ERROR;
+    }
+
     public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int uid,
             int flags, KeyCharacteristics outCharacteristics) {
         try {
             entropy = entropy != null ? entropy : new byte[0];
             args = args != null ? args : new KeymasterArguments();
-            int error = mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics);
+            int error = generateKeyInternal(alias, args, entropy, uid, flags, outCharacteristics);
             if (error == KEY_ALREADY_EXISTS) {
                 mBinder.del(alias, uid);
-                error = mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics);
+                error = generateKeyInternal(alias, args, entropy, uid, flags, outCharacteristics);
             }
             return error;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
+        } catch (ExecutionException | InterruptedException e) {
+            Log.e(TAG, "generateKey completed with exception", e);
+            return SYSTEM_ERROR;
         }
     }
 
@@ -485,10 +595,24 @@
         try {
             clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
             appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
-            return mBinder.getKeyCharacteristics(alias, clientId, appId, uid, outCharacteristics);
+            KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
+            int error = mBinder.getKeyCharacteristics(promise, alias, clientId, appId, uid);
+            if (error != NO_ERROR) return error;
+
+            KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+            error = result.getKeystoreResponse().getErrorCode();
+            if (error != NO_ERROR) return error;
+
+            KeyCharacteristics characteristics = result.getKeyCharacteristics();
+            if (characteristics == null) return SYSTEM_ERROR;
+            outCharacteristics.shallowCopyFrom(characteristics);
+            return NO_ERROR;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
+        } catch (ExecutionException | InterruptedException e) {
+            Log.e(TAG, "GetKeyCharacteristics completed with exception", e);
+            return SYSTEM_ERROR;
         }
     }
 
@@ -497,20 +621,40 @@
         return getKeyCharacteristics(alias, clientId, appId, UID_SELF, outCharacteristics);
     }
 
+    private int importKeyInternal(String alias, KeymasterArguments args, int format, byte[] keyData,
+            int uid, int flags, KeyCharacteristics outCharacteristics)
+                    throws RemoteException, ExecutionException, InterruptedException {
+        KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
+        int error = mBinder.importKey(promise, alias, args, format, keyData, uid, flags);
+        if (error != NO_ERROR) return error;
+
+        KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+        error = result.getKeystoreResponse().getErrorCode();
+        if (error != NO_ERROR) return error;
+
+        KeyCharacteristics characteristics = result.getKeyCharacteristics();
+        if (characteristics == null) return SYSTEM_ERROR;
+        outCharacteristics.shallowCopyFrom(characteristics);
+        return NO_ERROR;
+    }
+
     public int importKey(String alias, KeymasterArguments args, int format, byte[] keyData,
             int uid, int flags, KeyCharacteristics outCharacteristics) {
         try {
-            int error = mBinder.importKey(alias, args, format, keyData, uid, flags,
+            int error = importKeyInternal(alias, args, format, keyData, uid, flags,
                     outCharacteristics);
             if (error == KEY_ALREADY_EXISTS) {
                 mBinder.del(alias, uid);
-                error = mBinder.importKey(alias, args, format, keyData, uid, flags,
+                error = importKeyInternal(alias, args, format, keyData, uid, flags,
                         outCharacteristics);
             }
             return error;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
+        } catch (ExecutionException | InterruptedException e) {
+            Log.e(TAG, "ImportKey completed with exception", e);
+            return SYSTEM_ERROR;
         }
     }
 
@@ -578,34 +722,79 @@
         return true;
     }
 
+    private int importWrappedKeyInternal(String wrappedKeyAlias, byte[] wrappedKey,
+            String wrappingKeyAlias,
+            byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid,
+            KeyCharacteristics outCharacteristics)
+                    throws RemoteException, ExecutionException, InterruptedException {
+        KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
+        int error = mBinder.importWrappedKey(promise, wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
+                maskingKey, args, rootSid, fingerprintSid);
+        if (error != NO_ERROR) return error;
+
+        KeyCharacteristicsCallbackResult result = promise.getFuture().get();
+        error = result.getKeystoreResponse().getErrorCode();
+        if (error != NO_ERROR) return error;
+
+        KeyCharacteristics characteristics = result.getKeyCharacteristics();
+        if (characteristics == null) return SYSTEM_ERROR;
+        outCharacteristics.shallowCopyFrom(characteristics);
+        return NO_ERROR;
+    }
+
     public int importWrappedKey(String wrappedKeyAlias, byte[] wrappedKey,
             String wrappingKeyAlias,
             byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid, int uid,
             KeyCharacteristics outCharacteristics) {
+        // TODO b/119217337 uid parameter gets silently ignored.
         try {
-            int error = mBinder.importWrappedKey(wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
+            int error = importWrappedKeyInternal(wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
                     maskingKey, args, rootSid, fingerprintSid, outCharacteristics);
             if (error == KEY_ALREADY_EXISTS) {
-                mBinder.del(wrappedKeyAlias, -1);
-                error = mBinder.importWrappedKey(wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
+                mBinder.del(wrappedKeyAlias, UID_SELF);
+                error = importWrappedKeyInternal(wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
                         maskingKey, args, rootSid, fingerprintSid, outCharacteristics);
             }
             return error;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
+        } catch (ExecutionException | InterruptedException e) {
+            Log.e(TAG, "ImportWrappedKey completed with exception", e);
+            return SYSTEM_ERROR;
         }
     }
 
+    private class ExportKeyPromise
+    extends android.security.keystore.IKeystoreExportKeyCallback.Stub {
+        final private CompletableFuture<ExportResult> future = new CompletableFuture<ExportResult>();
+        @Override
+        public void onFinished(ExportResult exportKeyResult) throws android.os.RemoteException {
+            future.complete(exportKeyResult);
+        }
+        public final CompletableFuture<ExportResult> getFuture() {
+            return future;
+        }
+    };
+
     public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
             KeymasterBlob appId, int uid) {
         try {
             clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
             appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
-            return mBinder.exportKey(alias, format, clientId, appId, uid);
+            ExportKeyPromise promise = new ExportKeyPromise();
+            int error = mBinder.exportKey(promise, alias, format, clientId, appId, uid);
+            if (error == NO_ERROR) {
+                return promise.getFuture().get();
+            } else {
+                return new ExportResult(error);
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
+        } catch (ExecutionException | InterruptedException e) {
+            Log.e(TAG, "ExportKey completed with exception", e);
+            return null;
         }
     }
     public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
@@ -613,15 +802,37 @@
         return exportKey(alias, format, clientId, appId, UID_SELF);
     }
 
+    private class OperationPromise
+    extends android.security.keystore.IKeystoreOperationResultCallback.Stub {
+        final private CompletableFuture<OperationResult> future = new CompletableFuture<OperationResult>();
+        @Override
+        public void onFinished(OperationResult operationResult) throws android.os.RemoteException {
+            future.complete(operationResult);
+        }
+        public final CompletableFuture<OperationResult> getFuture() {
+            return future;
+        }
+    };
+
     public OperationResult begin(String alias, int purpose, boolean pruneable,
             KeymasterArguments args, byte[] entropy, int uid) {
         try {
             args = args != null ? args : new KeymasterArguments();
             entropy = entropy != null ? entropy : new byte[0];
-            return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid);
+            OperationPromise promise = new OperationPromise();
+            int errorCode =  mBinder.begin(promise, getToken(), alias, purpose, pruneable, args,
+                                           entropy, uid);
+            if (errorCode == NO_ERROR) {
+                return promise.getFuture().get();
+            } else {
+                return new OperationResult(errorCode);
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
+        } catch (ExecutionException | InterruptedException e) {
+            Log.e(TAG, "Begin completed with exception", e);
+            return null;
         }
     }
 
@@ -636,10 +847,19 @@
         try {
             arguments = arguments != null ? arguments : new KeymasterArguments();
             input = input != null ? input : new byte[0];
-            return mBinder.update(token, arguments, input);
+            OperationPromise promise = new OperationPromise();
+            int errorCode =  mBinder.update(promise, token, arguments, input);
+            if (errorCode == NO_ERROR) {
+                return promise.getFuture().get();
+            } else {
+                return new OperationResult(errorCode);
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
+        } catch (ExecutionException | InterruptedException e) {
+            Log.e(TAG, "Update completed with exception", e);
+            return null;
         }
     }
 
@@ -649,10 +869,19 @@
             arguments = arguments != null ? arguments : new KeymasterArguments();
             entropy = entropy != null ? entropy : new byte[0];
             signature = signature != null ? signature : new byte[0];
-            return mBinder.finish(token, arguments, signature, entropy);
+            OperationPromise promise = new OperationPromise();
+            int errorCode = mBinder.finish(promise, token, arguments, signature, entropy);
+            if (errorCode == NO_ERROR) {
+                return promise.getFuture().get();
+            } else {
+                return new OperationResult(errorCode);
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
+        } catch (ExecutionException | InterruptedException e) {
+            Log.e(TAG, "Finish completed with exception", e);
+            return null;
         }
     }
 
@@ -660,12 +889,33 @@
         return finish(token, arguments, signature, null);
     }
 
+    private class KeystoreResultPromise
+    extends android.security.keystore.IKeystoreResponseCallback.Stub {
+        final private CompletableFuture<KeystoreResponse> future = new CompletableFuture<KeystoreResponse>();
+        @Override
+        public void onFinished(KeystoreResponse keystoreResponse) throws android.os.RemoteException {
+            future.complete(keystoreResponse);
+        }
+        public final CompletableFuture<KeystoreResponse> getFuture() {
+            return future;
+        }
+    };
+
     public int abort(IBinder token) {
         try {
-            return mBinder.abort(token);
+            KeystoreResultPromise promise = new KeystoreResultPromise();
+            int errorCode = mBinder.abort(promise, token);
+            if (errorCode == NO_ERROR) {
+                return promise.getFuture().get().getErrorCode();
+            } else {
+                return errorCode;
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
+        } catch (ExecutionException | InterruptedException e) {
+            Log.e(TAG, "Abort completed with exception", e);
+            return SYSTEM_ERROR;
         }
     }
 
@@ -747,6 +997,47 @@
         return onUserPasswordChanged(UserHandle.getUserId(Process.myUid()), newPassword);
     }
 
+    private class KeyAttestationCallbackResult {
+        private KeystoreResponse keystoreResponse;
+        private KeymasterCertificateChain certificateChain;
+
+        public KeyAttestationCallbackResult(KeystoreResponse keystoreResponse,
+                KeymasterCertificateChain certificateChain) {
+            this.keystoreResponse = keystoreResponse;
+            this.certificateChain = certificateChain;
+        }
+
+        public KeystoreResponse getKeystoreResponse() {
+            return keystoreResponse;
+        }
+
+        public void setKeystoreResponse(KeystoreResponse keystoreResponse) {
+            this.keystoreResponse = keystoreResponse;
+        }
+
+        public KeymasterCertificateChain getCertificateChain() {
+            return certificateChain;
+        }
+
+        public void setCertificateChain(KeymasterCertificateChain certificateChain) {
+            this.certificateChain = certificateChain;
+        }
+    }
+
+    private class CertificateChainPromise
+    extends android.security.keystore.IKeystoreCertificateChainCallback.Stub {
+        final private CompletableFuture<KeyAttestationCallbackResult> future = new CompletableFuture<KeyAttestationCallbackResult>();
+        @Override
+        public void onFinished(KeystoreResponse keystoreResponse,
+                KeymasterCertificateChain certificateChain) throws android.os.RemoteException {
+            future.complete(new KeyAttestationCallbackResult(keystoreResponse, certificateChain));
+        }
+        public final CompletableFuture<KeyAttestationCallbackResult> getFuture() {
+            return future;
+        }
+    };
+
+
     public int attestKey(
             String alias, KeymasterArguments params, KeymasterCertificateChain outChain) {
         try {
@@ -756,10 +1047,21 @@
             if (outChain == null) {
                 outChain = new KeymasterCertificateChain();
             }
-            return mBinder.attestKey(alias, params, outChain);
+            CertificateChainPromise promise = new CertificateChainPromise();
+            int error = mBinder.attestKey(promise, alias, params);
+            if (error != NO_ERROR) return error;
+            KeyAttestationCallbackResult result = promise.getFuture().get();
+            error = result.getKeystoreResponse().getErrorCode();
+            if (error == NO_ERROR) {
+                outChain.shallowCopyFrom(result.getCertificateChain());
+            }
+            return error;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
+        } catch (ExecutionException | InterruptedException e) {
+            Log.e(TAG, "AttestKey completed with exception", e);
+            return SYSTEM_ERROR;
         }
     }
 
@@ -771,10 +1073,21 @@
             if (outChain == null) {
                 outChain = new KeymasterCertificateChain();
             }
-            return mBinder.attestDeviceIds(params, outChain);
+            CertificateChainPromise promise = new CertificateChainPromise();
+            int error = mBinder.attestDeviceIds(promise, params);
+            if (error != NO_ERROR) return error;
+            KeyAttestationCallbackResult result = promise.getFuture().get();
+            error = result.getKeystoreResponse().getErrorCode();
+            if (error == NO_ERROR) {
+                outChain.shallowCopyFrom(result.getCertificateChain());
+            }
+            return error;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
+        } catch (ExecutionException | InterruptedException e) {
+            Log.e(TAG, "AttestDevicdeIds completed with exception", e);
+            return SYSTEM_ERROR;
         }
     }
 
diff --git a/keystore/java/android/security/keystore/KeystoreResponse.java b/keystore/java/android/security/keystore/KeystoreResponse.java
new file mode 100644
index 0000000..3a229cb
--- /dev/null
+++ b/keystore/java/android/security/keystore/KeystoreResponse.java
@@ -0,0 +1,78 @@
+/*
+ * 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.security.keystore;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelFormatException;
+
+/**
+ * The Java side of the KeystoreResponse.
+ * <p>
+ * Serialization code for this and subclasses must be kept in sync with system/security/keystore.
+ * @hide
+ */
+public class KeystoreResponse implements Parcelable {
+    public final int error_code_;
+    public final String error_msg_;
+
+    public static final Parcelable.Creator<KeystoreResponse> CREATOR = new
+            Parcelable.Creator<KeystoreResponse>() {
+                @Override
+                public KeystoreResponse createFromParcel(Parcel in) {
+                    final int error_code = in.readInt();
+                    final String error_msg = in.readString();
+                    return new KeystoreResponse(error_code, error_msg);
+                }
+
+                @Override
+                public KeystoreResponse[] newArray(int size) {
+                    return new KeystoreResponse[size];
+                }
+            };
+
+    protected KeystoreResponse(int error_code, String error_msg) {
+        this.error_code_ = error_code;
+        this.error_msg_ = error_msg;
+    }
+
+    /**
+     * @return the error_code_
+     */
+    public final int getErrorCode() {
+        return error_code_;
+    }
+
+    /**
+     * @return the error_msg_
+     */
+    public final String getErrorMessage() {
+        return error_msg_;
+    }
+
+    
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(error_code_);
+        out.writeString(error_msg_);
+    }
+}
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/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 4e90162..e8b2f65 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -856,11 +856,9 @@
      * Checks whether the MediaPlayer2 is looping or non-looping.
      *
      * @return true if the MediaPlayer2 is currently looping, false otherwise
-     * @hide
      */
-    public boolean isLooping() {
-        return false;
-    }
+    // This is a synchronous call.
+    public abstract boolean isLooping();
 
     /**
      * Sets the audio session ID.
@@ -875,7 +873,8 @@
      * When created, a MediaPlayer2 instance automatically generates its own audio session ID.
      * However, it is possible to force this player to be part of an already existing audio session
      * by calling this method.
-     * This method must be called before one of the overloaded <code> setDataSource </code> methods.
+     * This method must be called when player is in {@link #PLAYER_STATE_IDLE} or
+     * {@link #PLAYER_STATE_PREPARED} state in order to have sessionId take effect.
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
@@ -885,8 +884,10 @@
      * Returns the audio session ID.
      *
      * @return the audio session ID. {@see #setAudioSessionId(int)}
-     * Note that the audio session ID is 0 only if a problem occured when the MediaPlayer2 was contructed.
+     * Note that the audio session ID is 0 only if a problem occured when the MediaPlayer2 was
+     * contructed.
      */
+    // This is a synchronous call.
     public abstract int getAudioSessionId();
 
     /**
@@ -907,7 +908,6 @@
     // This is an asynchronous call.
     public abstract Object attachAuxEffect(int effectId);
 
-
     /**
      * Sets the send level of the player to the attached auxiliary effect.
      * See {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0.
@@ -972,36 +972,10 @@
      * @return List of track info. The total number of tracks is the array length.
      * Must be called again if an external timed text source has been added after
      * addTimedTextSource method is called.
+     * @throws IllegalStateException if it is called in an invalid state.
      */
     public abstract List<TrackInfo> getTrackInfo();
 
-    /* Do not change these values without updating their counterparts
-     * in include/media/stagefright/MediaDefs.h and media/libstagefright/MediaDefs.cpp!
-     */
-    /**
-     * MIME type for SubRip (SRT) container. Used in addTimedTextSource APIs.
-     * @hide
-     */
-    public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
-
-    /**
-     * MIME type for WebVTT subtitle data.
-     * @hide
-     */
-    public static final String MEDIA_MIMETYPE_TEXT_VTT = "text/vtt";
-
-    /**
-     * MIME type for CEA-608 closed caption data.
-     * @hide
-     */
-    public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608";
-
-    /**
-     * MIME type for CEA-708 closed caption data.
-     * @hide
-     */
-    public static final String MEDIA_MIMETYPE_TEXT_CEA_708 = "text/cea-708";
-
     /**
      * Returns the index of the audio, video, or subtitle track currently selected for playback,
      * The return value is an index into the array returned by {@link #getTrackInfo()}, and can
@@ -1202,7 +1176,7 @@
     public abstract void unregisterEventCallback(EventCallback eventCallback);
 
     /* Do not change these values without updating their counterparts
-     * in include/media/mediaplayer2.h!
+     * in include/media/MediaPlayer2Types.h!
      */
     /** Unspecified media player error.
      * @see EventCallback#onError
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 9b97b10..babf24e 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -56,7 +56,6 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -64,7 +63,6 @@
 import java.util.Map;
 import java.util.Queue;
 import java.util.UUID;
-import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
@@ -1015,24 +1013,6 @@
 
     private native final void _seekTo(long msec, int mode);
 
-    /**
-     * Get current playback position as a {@link MediaTimestamp}.
-     * <p>
-     * The MediaTimestamp represents how the media time correlates to the system time in
-     * a linear fashion using an anchor and a clock rate. During regular playback, the media
-     * time moves fairly constantly (though the anchor frame may be rebased to a current
-     * system time, the linear correlation stays steady). Therefore, this method does not
-     * need to be called often.
-     * <p>
-     * To help users get current playback position, this method always anchors the timestamp
-     * to the current {@link System#nanoTime system time}, so
-     * {@link MediaTimestamp#getAnchorMediaTimeUs} can be used as current playback position.
-     *
-     * @return a MediaTimestamp object if a timestamp is available, or {@code null} if no timestamp
-     *         is available, e.g. because the media player has not been initialized.
-     *
-     * @see MediaTimestamp
-     */
     @Override
     @Nullable
     public MediaTimestamp getTimestamp()
@@ -1048,11 +1028,6 @@
         }
     }
 
-    /**
-     * Resets the MediaPlayer2 to its uninitialized state. After calling
-     * this method, you will have to initialize it again by setting the
-     * data source and calling prepare().
-     */
     @Override
     public void reset() {
         synchronized (mEventCbLock) {
@@ -1085,41 +1060,14 @@
 
     // Keep KEY_PARAMETER_* in sync with include/media/mediaplayer2.h
     private final static int KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400;
-    /**
-     * Sets the audio attributes.
-     * @param value value of the parameter to be set.
-     * @return true if the parameter is set successfully, false otherwise
-     */
+
+    // return true if the parameter is set successfully, false otherwise
     private native boolean native_setAudioAttributes(AudioAttributes audioAttributes);
     private native AudioAttributes native_getAudioAttributes();
 
-
-    /**
-     * Checks whether the MediaPlayer2 is looping or non-looping.
-     *
-     * @return true if the MediaPlayer2 is currently looping, false otherwise
-     * @hide
-     */
     @Override
     public native boolean isLooping();
 
-    /**
-     * Sets the audio session ID.
-     *
-     * @param sessionId the audio session ID.
-     * The audio session ID is a system wide unique identifier for the audio stream played by
-     * this MediaPlayer2 instance.
-     * The primary use of the audio session ID  is to associate audio effects to a particular
-     * instance of MediaPlayer2: if an audio session ID is provided when creating an audio effect,
-     * this effect will be applied only to the audio content of media players within the same
-     * audio session and not to the output mix.
-     * When created, a MediaPlayer2 instance automatically generates its own audio session ID.
-     * However, it is possible to force this player to be part of an already existing audio session
-     * by calling this method.
-     * This method must be called before one of the overloaded <code> setDataSource </code> methods.
-     * @throws IllegalStateException if it is called in an invalid state
-     * @throws IllegalArgumentException if the sessionId is invalid.
-     */
     @Override
     public Object setAudioSessionId(int sessionId) {
         return addTask(new Task(CALL_COMPLETED_SET_AUDIO_SESSION_ID, false) {
@@ -1132,29 +1080,9 @@
 
     private native void _setAudioSessionId(int sessionId);
 
-    /**
-     * Returns the audio session ID.
-     *
-     * @return the audio session ID. {@see #setAudioSessionId(int)}
-     * Note that the audio session ID is 0 only if a problem occured when the MediaPlayer2 was contructed.
-     */
     @Override
     public native int getAudioSessionId();
 
-    /**
-     * Attaches an auxiliary effect to the player. A typical auxiliary effect is a reverberation
-     * effect which can be applied on any sound source that directs a certain amount of its
-     * energy to this effect. This amount is defined by setAuxEffectSendLevel().
-     * See {@link #setAuxEffectSendLevel(float)}.
-     * <p>After creating an auxiliary effect (e.g.
-     * {@link android.media.audiofx.EnvironmentalReverb}), retrieve its ID with
-     * {@link android.media.audiofx.AudioEffect#getId()} and use it when calling this method
-     * to attach the player to the effect.
-     * <p>To detach the effect from the player, call this method with a null effect id.
-     * <p>This method must be called after one of the overloaded <code> setDataSource </code>
-     * methods.
-     * @param effectId system wide unique id of the effect to attach
-     */
     @Override
     public Object attachAuxEffect(int effectId) {
         return addTask(new Task(CALL_COMPLETED_ATTACH_AUX_EFFECT, false) {
@@ -1167,18 +1095,6 @@
 
     private native void _attachAuxEffect(int effectId);
 
-    /**
-     * Sets the send level of the player to the attached auxiliary effect.
-     * See {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0.
-     * <p>By default the send level is 0, so even if an effect is attached to the player
-     * this method must be called for the effect to be applied.
-     * <p>Note that the passed level value is a raw scalar. UI controls should be scaled
-     * logarithmically: the gain applied by audio framework ranges from -72dB to 0dB,
-     * so an appropriate conversion from linear UI input x to level is:
-     * x == 0 -> level = 0
-     * 0 < x <= R -> level = 10^(72*(x-R)/20/R)
-     * @param level send level scalar
-     */
     @Override
     public Object setAuxEffectSendLevel(float level) {
         return addTask(new Task(CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL, false) {
@@ -1208,31 +1124,17 @@
      * @see android.media.MediaPlayer2#getTrackInfo
      */
     public static final class TrackInfoImpl extends TrackInfo {
-        /**
-         * Gets the track type.
-         * @return TrackType which indicates if the track is video, audio, timed text.
-         */
         @Override
         public int getTrackType() {
             return mTrackType;
         }
 
-        /**
-         * Gets the language code of the track.
-         * @return a language code in either way of ISO-639-1 or ISO-639-2.
-         * When the language is unknown or could not be determined,
-         * ISO-639-2 language code, "und", is returned.
-         */
         @Override
         public String getLanguage() {
             String language = mFormat.getString(MediaFormat.KEY_LANGUAGE);
             return language == null ? "und" : language;
         }
 
-        /**
-         * Gets the {@link MediaFormat} of the track.  If the format is
-         * unknown or could not be determined, null is returned.
-         */
         @Override
         public MediaFormat getFormat() {
             if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT
@@ -1294,14 +1196,6 @@
         }
     };
 
-    /**
-     * Returns a List of track information.
-     *
-     * @return List of track info. The total number of tracks is the array length.
-     * Must be called again if an external timed text source has been added after
-     * addTimedTextSource method is called.
-     * @throws IllegalStateException if it is called in an invalid state.
-     */
     @Override
     public List<TrackInfo> getTrackInfo() {
         TrackInfoImpl trackInfo[] = getInbandTrackInfoImpl();
@@ -1328,33 +1222,6 @@
         return trackInfo;
     }
 
-    /*
-     * A helper function to check if the mime type is supported by media framework.
-     */
-    private static boolean availableMimeTypeForExternalSource(String mimeType) {
-        if (MEDIA_MIMETYPE_TEXT_SUBRIP.equals(mimeType)) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns the index of the audio, video, or subtitle track currently selected for playback,
-     * The return value is an index into the array returned by {@link #getTrackInfo()}, and can
-     * be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}.
-     *
-     * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
-     * {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
-     * {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
-     * @return index of the audio, video, or subtitle track currently selected for playback;
-     * a negative integer is returned when there is no selected track for {@code trackType} or
-     * when {@code trackType} is not one of audio, video, or subtitle.
-     * @throws IllegalStateException if called after {@link #close()}
-     *
-     * @see #getTrackInfo()
-     * @see #selectTrack(int)
-     * @see #deselectTrack(int)
-     */
     @Override
     public int getSelectedTrack(int trackType) {
         PlayerMessage request = PlayerMessage.newBuilder()
@@ -1368,34 +1235,6 @@
         return response.getValues(0).getInt32Value();
     }
 
-    /**
-     * Selects a track.
-     * <p>
-     * If a MediaPlayer2 is in invalid state, it throws an IllegalStateException exception.
-     * If a MediaPlayer2 is in <em>Started</em> state, the selected track is presented immediately.
-     * If a MediaPlayer2 is not in Started state, it just marks the track to be played.
-     * </p>
-     * <p>
-     * In any valid state, if it is called multiple times on the same type of track (ie. Video,
-     * Audio, Timed Text), the most recent one will be chosen.
-     * </p>
-     * <p>
-     * The first audio and video tracks are selected by default if available, even though
-     * this method is not called. However, no timed text track will be selected until
-     * this function is called.
-     * </p>
-     * <p>
-     * Currently, only timed text tracks or audio tracks can be selected via this method.
-     * In addition, the support for selecting an audio track at runtime is pretty limited
-     * in that an audio track can only be selected in the <em>Prepared</em> state.
-     * </p>
-     * @param index the index of the track to be selected. The valid range of the index
-     * is 0..total number of track - 1. The total number of tracks as well as the type of
-     * each individual track can be found by calling {@link #getTrackInfo()} method.
-     * @throws IllegalStateException if called in an invalid state.
-     *
-     * @see android.media.MediaPlayer2#getTrackInfo
-     */
     @Override
     public Object selectTrack(int index) {
         return addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) {
@@ -1406,20 +1245,6 @@
         });
     }
 
-    /**
-     * Deselect a track.
-     * <p>
-     * Currently, the track must be a timed text track and no audio or video tracks can be
-     * deselected. If the timed text track identified by index has not been
-     * selected before, it throws an exception.
-     * </p>
-     * @param index the index of the track to be deselected. The valid range of the index
-     * is 0..total number of tracks - 1. The total number of tracks as well as the type of
-     * each individual track can be found by calling {@link #getTrackInfo()} method.
-     * @throws IllegalStateException if called in an invalid state.
-     *
-     * @see android.media.MediaPlayer2#getTrackInfo
-     */
     @Override
     public Object deselectTrack(int index) {
         return addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) {
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 0769e5c..c49e720 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -1050,9 +1050,9 @@
     android_media_MediaPlayer2_release(env, thiz);
 }
 
-static void android_media_MediaPlayer2_set_audio_session_id(JNIEnv *env,  jobject thiz,
+static void android_media_MediaPlayer2_setAudioSessionId(JNIEnv *env,  jobject thiz,
         jint sessionId) {
-    ALOGV("set_session_id(): %d", sessionId);
+    ALOGV("setAudioSessionId(): %d", sessionId);
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL ) {
         jniThrowException(env, "java/lang/IllegalStateException", NULL);
@@ -1062,8 +1062,8 @@
             NULL);
 }
 
-static jint android_media_MediaPlayer2_get_audio_session_id(JNIEnv *env,  jobject thiz) {
-    ALOGV("get_session_id()");
+static jint android_media_MediaPlayer2_getAudioSessionId(JNIEnv *env,  jobject thiz) {
+    ALOGV("getAudioSessionId()");
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL ) {
         jniThrowException(env, "java/lang/IllegalStateException", NULL);
@@ -1419,8 +1419,8 @@
     {"native_init",         "()V",                              (void *)android_media_MediaPlayer2_native_init},
     {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer2_native_setup},
     {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer2_native_finalize},
-    {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer2_get_audio_session_id},
-    {"_setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer2_set_audio_session_id},
+    {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer2_getAudioSessionId},
+    {"_setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer2_setAudioSessionId},
     {"_setAuxEffectSendLevel", "(F)V",                          (void *)android_media_MediaPlayer2_setAuxEffectSendLevel},
     {"_attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer2_attachAuxEffect},
     // Modular DRM
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/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 4abcf73..c9ee5c8 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -541,14 +541,14 @@
     }
 
     @Override
-    public Cursor querySearchDocuments(String rootId, String query, String[] projection)
+    public Cursor querySearchDocuments(String rootId, String[] projection, Bundle queryArgs)
             throws FileNotFoundException {
         final File parent;
         synchronized (mRootsLock) {
             parent = mRoots.get(rootId).path;
         }
 
-        return querySearchDocuments(parent, query, projection, Collections.emptySet());
+        return querySearchDocuments(parent, projection, Collections.emptySet(), queryArgs);
     }
 
     @Override
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/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/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/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0e41a7fd..d4e6987 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>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6244e1c..95fd86f 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,19 @@
 
     <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="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/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index f409902..3d9aa0f 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -23,9 +23,9 @@
 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)
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index b838c9b..37bf06e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -21,6 +21,8 @@
 import static android.service.notification.NotificationListenerService.Ranking
         .USER_SENTIMENT_NEGATIVE;
 
+import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;
+
 import android.app.INotificationManager;
 import android.app.NotificationChannel;
 import android.content.Context;
@@ -189,7 +191,13 @@
             } else if (gutsView instanceof AppOpsInfo) {
                 initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
             } else if (gutsView instanceof NotificationInfo) {
-                initializeNotificationInfo(row, (NotificationInfo) gutsView);
+                int action;
+                if (item instanceof NotificationMenuRow.NotificationInfoMenuItem) {
+                    action = ((NotificationMenuRow.NotificationInfoMenuItem) item).mAction;
+                } else {
+                    action = ACTION_NONE;
+                }
+                initializeNotificationInfo(row, (NotificationInfo) gutsView, action);
             }
             return true;
         } catch (Exception e) {
@@ -246,14 +254,15 @@
 
     /**
      * Sets up the {@link NotificationInfo} inside the notification row's guts.
-     *
      * @param row view to set up the guts for
      * @param notificationInfoView view to set up/bind within {@code row}
+     * @param action The action to take immediately upon binding, if any.
      */
     @VisibleForTesting
     void initializeNotificationInfo(
             final ExpandableNotificationRow row,
-            NotificationInfo notificationInfoView) throws Exception {
+            NotificationInfo notificationInfoView,
+            @NotificationInfo.NotificationInfoAction int action) throws Exception {
         NotificationGuts guts = row.getGuts();
         StatusBarNotification sbn = row.getStatusBarNotification();
         String packageName = sbn.getPackageName();
@@ -297,7 +306,8 @@
                 isForBlockingHelper,
                 row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE,
                 row.getEntry().noisy,
-                row.getEntry().importance);
+                row.getEntry().importance,
+                action);
 
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 522da4d..3a7091b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -71,16 +71,19 @@
 public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
     private static final String TAG = "InfoGuts";
 
-    @IntDef(prefix = { "SWAP_CONTENT_" }, value = {
-            SWAP_CONTENT_UNDO,
-            SWAP_CONTENT_TOGGLE_SILENT,
-            SWAP_CONTENT_BLOCK,
+    @IntDef(prefix = { "ACTION_" }, value = {
+            ACTION_NONE,
+            ACTION_UNDO,
+            ACTION_TOGGLE_SILENT,
+            ACTION_BLOCK,
     })
-    @interface SwapContentAction {}
+    public @interface NotificationInfoAction {
+    }
 
-    private static final int SWAP_CONTENT_UNDO = 0;
-    private static final int SWAP_CONTENT_TOGGLE_SILENT = 1;
-    private static final int SWAP_CONTENT_BLOCK = 2;
+    public static final int ACTION_NONE = 0;
+    public static final int ACTION_UNDO = 1;
+    public static final int ACTION_TOGGLE_SILENT = 2;
+    public static final int ACTION_BLOCK = 3;
 
     private INotificationManager mINotificationManager;
     private PackageManager mPm;
@@ -123,8 +126,7 @@
 
     private OnClickListener mOnToggleSilent = v -> {
         Runnable saveImportance = () -> {
-            mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT;
-            swapContent(SWAP_CONTENT_TOGGLE_SILENT);
+            swapContent(ACTION_TOGGLE_SILENT, true /* animate */);
         };
         if (mCheckSaveListener != null) {
             mCheckSaveListener.checkSave(saveImportance, mSbn);
@@ -135,8 +137,7 @@
 
     private OnClickListener mOnStopOrMinimizeNotifications = v -> {
         Runnable saveImportance = () -> {
-            mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS;
-            swapContent(SWAP_CONTENT_BLOCK);
+            swapContent(ACTION_BLOCK, true /* animate */);
         };
         if (mCheckSaveListener != null) {
             mCheckSaveListener.checkSave(saveImportance, mSbn);
@@ -149,7 +150,7 @@
         // Reset exit counter that we'll log and record an undo event separately (not an exit event)
         mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
         logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
-        swapContent(SWAP_CONTENT_UNDO);
+        swapContent(ACTION_UNDO, true /* animate */);
     };
 
     public NotificationInfo(Context context, AttributeSet attrs) {
@@ -185,13 +186,14 @@
             boolean isDeviceProvisioned,
             boolean isNonblockable,
             boolean isNoisy,
-            int importance)
+            int importance,
+            @NotificationInfoAction int action)
             throws RemoteException {
         bindNotification(pm, iNotificationManager, pkg, notificationChannel,
                 numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
                 onAppSettingsClick, isDeviceProvisioned, isNonblockable,
                 false /* isBlockingHelper */, false /* isUserSentimentNegative */, isNoisy,
-                importance);
+                importance, action);
     }
 
     public void bindNotification(
@@ -209,7 +211,8 @@
             boolean isForBlockingHelper,
             boolean isUserSentimentNegative,
             boolean isNoisy,
-            int importance)
+            int importance,
+            @NotificationInfoAction int action)
             throws RemoteException {
         mINotificationManager = iNotificationManager;
         mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -250,6 +253,10 @@
         bindHeader();
         bindPrompt();
         bindButtons();
+
+        if (action != ACTION_NONE) {
+            swapContent(action, false /* don't animate */);
+        }
     }
 
     private void bindHeader() throws RemoteException {
@@ -351,7 +358,8 @@
     }
 
     private void saveImportance() {
-        if (!mIsNonblockable) {
+        if (!mIsNonblockable
+                || mExitReason != NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS) {
             updateImportance();
         }
     }
@@ -421,7 +429,7 @@
         }
     }
 
-    private void swapContent(@SwapContentAction int action) {
+    private void swapContent(@NotificationInfoAction int action, boolean animate) {
         if (mExpandAnimation != null) {
             mExpandAnimation.cancel();
         }
@@ -432,10 +440,11 @@
         View header = findViewById(R.id.header);
 
         switch (action) {
-            case SWAP_CONTENT_UNDO:
+            case ACTION_UNDO:
                 mChosenImportance = mStartingChannelImportance;
                 break;
-            case SWAP_CONTENT_TOGGLE_SILENT:
+            case ACTION_TOGGLE_SILENT:
+                mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT;
                 if (mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT) {
                     mChosenImportance = IMPORTANCE_LOW;
                     confirmationText.setText(R.string.notification_channel_silenced);
@@ -444,7 +453,8 @@
                     confirmationText.setText(R.string.notification_channel_unsilenced);
                 }
                 break;
-            case SWAP_CONTENT_BLOCK:
+            case ACTION_BLOCK:
+                mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS;
                 if (mIsForeground) {
                     mChosenImportance = IMPORTANCE_MIN;
                     confirmationText.setText(R.string.notification_channel_minimized);
@@ -457,38 +467,41 @@
                 throw new IllegalArgumentException();
         }
 
-        boolean isUndo = action == SWAP_CONTENT_UNDO;
-        ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA,
-                prompt.getAlpha(), isUndo ? 1f : 0f);
-        promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
-        ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
-                confirmation.getAlpha(), isUndo ? 0f : 1f);
-        confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
+        boolean isUndo = action == ACTION_UNDO;
 
         prompt.setVisibility(isUndo ? VISIBLE : GONE);
         confirmation.setVisibility(isUndo ? GONE : VISIBLE);
         header.setVisibility(isUndo ? VISIBLE : GONE);
 
-        mExpandAnimation = new AnimatorSet();
-        mExpandAnimation.playTogether(promptAnim, confirmAnim);
-        mExpandAnimation.setDuration(150);
-        mExpandAnimation.addListener(new AnimatorListenerAdapter() {
-            boolean cancelled = false;
+        if (animate) {
+            ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA,
+                    prompt.getAlpha(), isUndo ? 1f : 0f);
+            promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
+            ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
+                    confirmation.getAlpha(), isUndo ? 0f : 1f);
+            confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
 
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                cancelled = true;
-            }
+            mExpandAnimation = new AnimatorSet();
+            mExpandAnimation.playTogether(promptAnim, confirmAnim);
+            mExpandAnimation.setDuration(150);
+            mExpandAnimation.addListener(new AnimatorListenerAdapter() {
+                boolean mCancelled = false;
 
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (!cancelled) {
-                    prompt.setVisibility(isUndo ? VISIBLE : GONE);
-                    confirmation.setVisibility(isUndo ? GONE : VISIBLE);
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    mCancelled = true;
                 }
-            }
-        });
-        mExpandAnimation.start();
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (!mCancelled) {
+                        prompt.setVisibility(isUndo ? VISIBLE : GONE);
+                        confirmation.setVisibility(isUndo ? GONE : VISIBLE);
+                    }
+                }
+            });
+            mExpandAnimation.start();
+        }
 
         // Since we're swapping/update the content, reset the timeout so the UI can't close
         // immediately after the update.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 674c8ee..2bc4f02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
+import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -41,6 +42,7 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.AlphaOptimizedImageView;
 import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
+import com.android.systemui.statusbar.notification.row.NotificationInfo.NotificationInfoAction;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
@@ -610,8 +612,8 @@
         String infoDescription = res.getString(R.string.notification_menu_gear_description);
         NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
                 R.layout.notification_info, null, false);
-        MenuItem info = new NotificationMenuItem(context, infoDescription, infoContent,
-                R.drawable.ic_settings);
+        MenuItem info = new NotificationInfoMenuItem(context, infoDescription, infoContent,
+                R.drawable.ic_settings, ACTION_NONE);
         return info;
     }
 
@@ -737,4 +739,18 @@
             return mContentDescription;
         }
     }
+
+    /** A {@link NotificationMenuItem} with an associated {@link NotificationInfoAction}. */
+    public static class NotificationInfoMenuItem extends NotificationMenuItem {
+
+        @NotificationInfoAction
+        int mAction;
+
+        public NotificationInfoMenuItem(Context context, String s,
+                NotificationInfo content, int iconResId,
+                @NotificationInfoAction int action) {
+            super(context, s, content, iconResId);
+            this.mAction = action;
+        }
+    }
 }
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/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
index d85e18c..67da8a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
@@ -14,14 +14,11 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.accessibilityservice.AccessibilityServiceInfo;
 import android.content.Context;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
 
-import java.util.List;
-
 /**
  * For mocking because AccessibilityManager is final for some reason...
  */
@@ -62,8 +59,8 @@
         mAccessibilityManager.sendAccessibilityEvent(event);
     }
 
-    public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
-            int feedbackTypeFlags) {
-        return mAccessibilityManager.getEnabledAccessibilityServiceList(feedbackTypeFlags);
+    /** Returns a recommended ui timeout value in milliseconds. */
+    public int getRecommendedTimeoutMillis(int originalTimeout, int uiContentFlags) {
+        return mAccessibilityManager.getRecommendedTimeoutMillis(originalTimeout, uiContentFlags);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 798f8bc..b588305 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.volume;
 
-import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
-import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.media.AudioManager.RINGER_MODE_NORMAL;
 import static android.media.AudioManager.RINGER_MODE_SILENT;
@@ -32,7 +30,6 @@
 
 import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
 
-import android.accessibilityservice.AccessibilityServiceInfo;
 import android.animation.ObjectAnimator;
 import android.annotation.SuppressLint;
 import android.app.ActivityManager;
@@ -68,13 +65,12 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
-import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
+import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
@@ -113,6 +109,10 @@
     private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
     private static final int UPDATE_ANIMATION_DURATION = 80;
 
+    static final int DIALOG_TIMEOUT_MILLIS = 3000;
+    static final int DIALOG_SAFETYWARNING_TIMEOUT_MILLIS = 5000;
+    static final int DIALOG_HOVERING_TIMEOUT_MILLIS = 16000;
+
     private final Context mContext;
     private final H mHandler = new H();
     private final VolumeDialogController mController;
@@ -170,7 +170,6 @@
 
     @Override
     public void destroy() {
-        mAccessibility.destroy();
         mController.removeCallback(mControllerCallbackH);
         mHandler.removeCallbacksAndMessages(null);
     }
@@ -356,8 +355,6 @@
         writer.print("  mDynamic: "); writer.println(mDynamic);
         writer.print("  mAutomute: "); writer.println(mAutomute);
         writer.print("  mSilentMode: "); writer.println(mSilentMode);
-        writer.print("  mAccessibility.mFeedbackEnabled: ");
-        writer.println(mAccessibility.mFeedbackEnabled);
     }
 
     private static int getImpliedLevel(SeekBar seekBar, int progress) {
@@ -571,10 +568,18 @@
     }
 
     private int computeTimeoutH() {
-        if (mAccessibility.mFeedbackEnabled) return 20000;
-        if (mHovering) return 16000;
-        if (mSafetyWarning != null) return 5000;
-        return 3000;
+        if (mHovering) {
+            return mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_HOVERING_TIMEOUT_MILLIS,
+                    AccessibilityManager.FLAG_CONTENT_CONTROLS);
+        }
+        if (mSafetyWarning != null) {
+            return mAccessibilityMgr.getRecommendedTimeoutMillis(
+                    DIALOG_SAFETYWARNING_TIMEOUT_MILLIS,
+                    AccessibilityManager.FLAG_CONTENT_TEXT
+                            | AccessibilityManager.FLAG_CONTENT_CONTROLS);
+        }
+        return mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS,
+                AccessibilityManager.FLAG_CONTENT_CONTROLS);
     }
 
     protected void dismissH(int reason) {
@@ -1261,28 +1266,8 @@
     }
 
     private final class Accessibility extends AccessibilityDelegate {
-        private boolean mFeedbackEnabled;
-
         public void init() {
-            mDialogView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
-                @Override
-                public void onViewDetachedFromWindow(View v) {
-                    if (D.BUG) Log.d(TAG, "onViewDetachedFromWindow");
-                }
-
-                @Override
-                public void onViewAttachedToWindow(View v) {
-                    if (D.BUG) Log.d(TAG, "onViewAttachedToWindow");
-                    updateFeedbackEnabled();
-                }
-            });
             mDialogView.setAccessibilityDelegate(this);
-            mAccessibilityMgr.addCallback(mListener);
-            updateFeedbackEnabled();
-        }
-
-        public void destroy() {
-            mAccessibilityMgr.removeCallback(mListener);
         }
 
         @Override
@@ -1298,25 +1283,6 @@
             rescheduleTimeoutH();
             return super.onRequestSendAccessibilityEvent(host, child, event);
         }
-
-        private void updateFeedbackEnabled() {
-            mFeedbackEnabled = computeFeedbackEnabled();
-        }
-
-        private boolean computeFeedbackEnabled() {
-            // are there any enabled non-generic a11y services?
-            final List<AccessibilityServiceInfo> services =
-                    mAccessibilityMgr.getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK);
-            for (AccessibilityServiceInfo asi : services) {
-                if (asi.feedbackType != 0 && asi.feedbackType != FEEDBACK_GENERIC) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        private final AccessibilityServicesStateChangeListener mListener =
-                enabled -> updateFeedbackEnabled();
     }
 
     private static class VolumeRow {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 626726d..3d2ea70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -284,7 +284,8 @@
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
+                NotificationInfo.ACTION_NONE);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -301,7 +302,8 @@
                 eq(true) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
                 eq(false) /*isNoisy */,
-                eq(0));
+                eq(0),
+                eq(NotificationInfo.ACTION_NONE));
     }
 
     @Test
@@ -313,7 +315,8 @@
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
+                NotificationInfo.ACTION_NONE);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -330,7 +333,8 @@
                 eq(false) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
                 eq(false) /*isNoisy */,
-                eq(0));
+                eq(0),
+                eq(NotificationInfo.ACTION_NONE));
     }
 
     @Test
@@ -343,7 +347,8 @@
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
+                NotificationInfo.ACTION_NONE);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -360,7 +365,8 @@
                 eq(true) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
                 eq(true) /*isNoisy */,
-                eq(0));
+                eq(0),
+                eq(NotificationInfo.ACTION_NONE));
     }
 
     @Test
@@ -373,7 +379,8 @@
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
+                NotificationInfo.ACTION_NONE);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -390,7 +397,8 @@
                 eq(true) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
                 eq(false) /*isNoisy */,
-                eq(IMPORTANCE_DEFAULT));
+                eq(IMPORTANCE_DEFAULT),
+                eq(NotificationInfo.ACTION_NONE));
     }
 
     @Test
@@ -403,7 +411,8 @@
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
+                NotificationInfo.ACTION_NONE);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -420,7 +429,39 @@
                 eq(false) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
                 eq(false) /*isNoisy */,
-                eq(0));
+                eq(0),
+                eq(NotificationInfo.ACTION_NONE));
+    }
+
+    @Test
+    public void testInitializeNotificationInfoView_withInitialAction() throws Exception {
+        NotificationInfo notificationInfoView = mock(NotificationInfo.class);
+        ExpandableNotificationRow row = spy(mHelper.createRow());
+        row.setBlockingHelperShowing(true);
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        when(row.getIsNonblockable()).thenReturn(false);
+        StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
+                NotificationInfo.ACTION_BLOCK);
+
+        verify(notificationInfoView).bindNotification(
+                any(PackageManager.class),
+                any(INotificationManager.class),
+                eq(statusBarNotification.getPackageName()),
+                any(NotificationChannel.class),
+                anyInt(),
+                eq(statusBarNotification),
+                any(NotificationInfo.CheckSaveListener.class),
+                any(NotificationInfo.OnSettingsClickListener.class),
+                any(NotificationInfo.OnAppSettingsClickListener.class),
+                eq(false),
+                eq(false),
+                eq(true) /* isForBlockingHelper */,
+                eq(true) /* isUserSentimentNegative */,
+                eq(false) /*isNoisy */,
+                eq(0),
+                eq(NotificationInfo.ACTION_BLOCK));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 3744196..1cc1c63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -187,7 +187,7 @@
         when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
         assertTrue(textView.getText().toString().contains("App Name"));
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -200,7 +200,7 @@
                 .thenReturn(iconDrawable);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
         assertEquals(iconDrawable, iconView.getDrawable());
     }
@@ -209,7 +209,7 @@
     public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(GONE, groupNameView.getVisibility());
         final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
@@ -226,7 +226,7 @@
                 .thenReturn(notificationChannelGroup);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(View.VISIBLE, groupNameView.getVisibility());
         assertEquals("Test Group Name", groupNameView.getText());
@@ -238,7 +238,7 @@
     public void testBindNotification_SetsTextChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(TEST_CHANNEL_NAME, textView.getText());
     }
@@ -247,7 +247,7 @@
     public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
-                false, false, IMPORTANCE_DEFAULT);
+                false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, textView.getVisibility());
     }
@@ -260,7 +260,7 @@
                 eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
-                false, false, IMPORTANCE_DEFAULT);
+                false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -269,7 +269,7 @@
     public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -278,7 +278,7 @@
     public void testBindNotification_BlockButton() throws Exception {
        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-               false, IMPORTANCE_DEFAULT);
+               false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final View block = mNotificationInfo.findViewById(R.id.block);
         final View toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
         final View minimize = mNotificationInfo.findViewById(R.id.minimize);
@@ -292,7 +292,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT);
+                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
         assertEquals(VISIBLE, toggleSilent.getVisibility());
         assertEquals(
@@ -304,7 +304,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_LOW);
+                true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
         final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
         assertEquals(VISIBLE, toggleSilent.getVisibility());
         assertEquals(
@@ -316,7 +316,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT);
+                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
         assertEquals(VISIBLE, toggleSilent.getVisibility());
         assertEquals(
@@ -329,7 +329,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_LOW);
+                true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
         final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
         assertEquals(VISIBLE, toggleSilent.getVisibility());
         assertEquals(
@@ -341,7 +341,7 @@
         mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final View block = mNotificationInfo.findViewById(R.id.block);
         final View minimize = mNotificationInfo.findViewById(R.id.minimize);
         assertEquals(GONE, block.getVisibility());
@@ -356,7 +356,7 @@
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
                     latch.countDown();
-                }, null, true, false, false, IMPORTANCE_DEFAULT);
+                }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         settingsButton.performClick();
@@ -368,7 +368,7 @@
     public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -380,7 +380,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
-                }, null, false, false, false, IMPORTANCE_DEFAULT);
+                }, null, false, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -389,11 +389,11 @@
     public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
                 (View v, NotificationChannel c, int appUid) -> {
-                }, null, true, false, false, IMPORTANCE_DEFAULT);
+                }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertEquals(View.VISIBLE, settingsButton.getVisibility());
     }
@@ -402,7 +402,7 @@
     public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
         verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
     }
@@ -411,7 +411,7 @@
     public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
-                true, true, false, IMPORTANCE_DEFAULT);
+                true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
         verify(mMetricsLogger, times(1)).count(anyString(), anyInt());
     }
@@ -424,7 +424,7 @@
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(null, c);
                     latch.countDown();
-                }, null, true, true, false, IMPORTANCE_DEFAULT);
+                }, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.info).performClick();
         // Verify that listener was triggered.
@@ -437,7 +437,7 @@
             throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
-                null, true, true, false, IMPORTANCE_DEFAULT);
+                null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView channelNameView =
                 mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, channelNameView.getVisibility());
@@ -448,7 +448,7 @@
     public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
-                null, true, true, false, IMPORTANCE_DEFAULT);
+                null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView blockView = mNotificationInfo.findViewById(R.id.block);
         assertEquals(GONE, blockView.getVisibility());
     }
@@ -457,7 +457,7 @@
     public void testbindNotification_BlockingHelper() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false,
-                true, true, false, IMPORTANCE_DEFAULT);
+                true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
@@ -467,7 +467,7 @@
     public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -478,7 +478,7 @@
     public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
@@ -489,7 +489,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         mTestableLooper.processAllMessages();
@@ -503,7 +503,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         mTestableLooper.processAllMessages();
@@ -517,7 +517,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT);
+                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         mTestableLooper.processAllMessages();
@@ -531,7 +531,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT);
+                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         mTestableLooper.processAllMessages();
@@ -545,7 +545,7 @@
         int originalImportance = mNotificationChannel.getImportance();
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.handleCloseControls(true, false);
         mTestableLooper.processAllMessages();
@@ -560,7 +560,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -578,7 +578,8 @@
                 TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
                 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */ ,
-                true, false /* isNonblockable */, false, /* isNoisy */IMPORTANCE_DEFAULT);
+                true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT,
+                NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -598,8 +599,9 @@
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
                 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
-                null /* onSettingsClick */, null /* onAppSettingsClick */ ,
-                true, false /* isNonblockable */, false, /* isNoisy */IMPORTANCE_DEFAULT);
+                null /* onSettingsClick */, null /* onAppSettingsClick */,
+                true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT,
+                NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -620,7 +622,8 @@
                 null /* onSettingsClick */, null /* onAppSettingsClick */ ,
                 true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT);
+                true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
+                NotificationInfo.ACTION_NONE);
 
         NotificationGuts guts = spy(new NotificationGuts(mContext, null));
         when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -648,7 +651,8 @@
                 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT);
+                true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
+                NotificationInfo.ACTION_NONE);
 
         NotificationGuts guts = spy(new NotificationGuts(mContext, null));
         when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -676,8 +680,8 @@
                 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */ ,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true, true /* isUserSentimentNegative */, false, /* isNoisy */
-                IMPORTANCE_DEFAULT);
+                true, true /* isUserSentimentNegative */, false /* isNoisy */,
+                IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
 
@@ -696,7 +700,8 @@
                 null /* onSettingsClick */, null /* onAppSettingsClick */,
                 true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT);
+                true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
+                NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         mTestableLooper.processAllMessages();
@@ -719,7 +724,7 @@
                 true /* isForBlockingHelper */,
                 true,
                 false /* isUserSentimentNegative */,
-                false, /* isNoisy */IMPORTANCE_DEFAULT);
+                false /* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         NotificationGuts guts = mock(NotificationGuts.class);
         doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
         mNotificationInfo.setGutsParent(guts);
@@ -734,7 +739,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
 
@@ -748,7 +753,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -781,7 +786,7 @@
                 false /* isNonblockable */,
                 true /* isForBlockingHelper */,
                 true /* isUserSentimentNegative */,
-                false, /* isNoisy */IMPORTANCE_DEFAULT);
+                false/* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -803,7 +808,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
 
@@ -818,7 +823,7 @@
         mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -839,7 +844,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -857,7 +862,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -879,7 +884,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -901,7 +906,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT);
+                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         waitForUndoButton();
@@ -922,7 +927,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         waitForUndoButton();
@@ -944,7 +949,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT);
+                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         waitForUndoButton();
@@ -966,7 +971,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_LOW);
+                false, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         waitForUndoButton();
@@ -987,7 +992,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -1003,7 +1008,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -1020,7 +1025,7 @@
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
                 (Runnable saveImportance, StatusBarNotification sbn) -> {
-                }, null, null, true, true, false, IMPORTANCE_DEFAULT);
+                }, null, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         mTestableLooper.processAllMessages();
@@ -1038,7 +1043,8 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
                 (Runnable saveImportance, StatusBarNotification sbn) -> {
                     saveImportance.run();
-                }, null, null, true, false, false, IMPORTANCE_DEFAULT);
+                }, null, null, true, false, false, IMPORTANCE_DEFAULT,
+                NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         mTestableLooper.processAllMessages();
@@ -1074,7 +1080,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null,
                 (View v, Intent intent) -> {
                     latch.countDown();
-                }, true, false, false, IMPORTANCE_DEFAULT);
+                }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(View.VISIBLE, settingsLink.getVisibility());
         settingsLink.performClick();
@@ -1102,7 +1108,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
                 (View v, Intent intent) -> {
                     latch.countDown();
-                }, true, false, false, IMPORTANCE_DEFAULT);
+                }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(View.VISIBLE, settingsLink.getVisibility());
         settingsLink.performClick();
@@ -1121,7 +1127,7 @@
 
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
-                null, true, false, false, IMPORTANCE_DEFAULT);
+                null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(GONE, settingsLink.getVisibility());
     }
@@ -1142,7 +1148,7 @@
 
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(GONE, settingsLink.getVisibility());
     }
@@ -1165,7 +1171,7 @@
 
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true,
-                true, true, false, IMPORTANCE_DEFAULT);
+                true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(GONE, settingsLink.getVisibility());
     }
@@ -1182,7 +1188,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -1195,7 +1201,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -1208,7 +1214,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                true, IMPORTANCE_DEFAULT);
+                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         waitForUndoButton();
@@ -1222,7 +1228,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                true, IMPORTANCE_DEFAULT);
+                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
         waitForUndoButton();
@@ -1236,7 +1242,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -1248,7 +1254,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT);
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -1256,4 +1262,60 @@
         waitForStopButton();
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
     }
+
+    @Test
+    public void testBindNotificationWithInitialBlockAction() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_BLOCK);
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        ArgumentCaptor<NotificationChannel> updated =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), updated.capture());
+        assertTrue((updated.getValue().getUserLockedFields()
+                & USER_LOCKED_IMPORTANCE) != 0);
+        assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance());
+    }
+
+    @Test
+    public void testBindNotificationWithInitialSilenceAction() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_TOGGLE_SILENT);
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        ArgumentCaptor<NotificationChannel> updated =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), updated.capture());
+        assertTrue((updated.getValue().getUserLockedFields()
+                & USER_LOCKED_IMPORTANCE) != 0);
+        assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance());
+    }
+
+    @Test
+    public void testBindNotificationWithInitialUnSilenceAction() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                true, IMPORTANCE_LOW, NotificationInfo.ACTION_TOGGLE_SILENT);
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        ArgumentCaptor<NotificationChannel> updated =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), updated.capture());
+        assertTrue((updated.getValue().getUserLockedFields()
+                & USER_LOCKED_IMPORTANCE) != 0);
+        assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index c536dca..5f02fad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -27,18 +27,23 @@
 
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.KeyguardManager;
 import android.media.AudioManager;
+import android.os.SystemClock;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.text.TextUtils;
+import android.view.InputDevice;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.ImageView;
 
 import com.android.systemui.R;
@@ -48,10 +53,11 @@
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.function.Predicate;
@@ -59,7 +65,6 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-@Ignore
 public class VolumeDialogImplTest extends SysuiTestCase {
 
     VolumeDialogImpl mDialog;
@@ -113,6 +118,45 @@
                     + " failed test", condition.test(view));
         }
     }
+
+    @Test
+    public void testComputeTimeout() {
+        Mockito.reset(mAccessibilityMgr);
+        mDialog.rescheduleTimeoutH();
+        verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
+                VolumeDialogImpl.DIALOG_TIMEOUT_MILLIS,
+                AccessibilityManager.FLAG_CONTENT_CONTROLS);
+    }
+
+    @Test
+    public void testComputeTimeout_withHovering() {
+        Mockito.reset(mAccessibilityMgr);
+        View dialog = mDialog.getDialogView();
+        long uptimeMillis = SystemClock.uptimeMillis();
+        MotionEvent event = MotionEvent.obtain(uptimeMillis, uptimeMillis,
+                MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
+        event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+        dialog.dispatchGenericMotionEvent(event);
+        event.recycle();
+        verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
+                VolumeDialogImpl.DIALOG_HOVERING_TIMEOUT_MILLIS,
+                AccessibilityManager.FLAG_CONTENT_CONTROLS);
+    }
+
+    @Test
+    public void testComputeTimeout_withSafetyWarningOn() {
+        Mockito.reset(mAccessibilityMgr);
+        ArgumentCaptor<VolumeDialogController.Callbacks> controllerCallbackCapture =
+                ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class);
+        verify(mController).addCallback(controllerCallbackCapture.capture(), any());
+        VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue();
+        callbacks.onShowSafetyWarning(AudioManager.FLAG_SHOW_UI);
+        verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
+                VolumeDialogImpl.DIALOG_SAFETYWARNING_TIMEOUT_MILLIS,
+                AccessibilityManager.FLAG_CONTENT_TEXT
+                        | AccessibilityManager.FLAG_CONTENT_CONTROLS);
+    }
+
 /*
     @Test
     public void testContentDescriptions() {
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/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/AbstractPerUserSystemService.java b/services/core/java/com/android/server/AbstractPerUserSystemService.java
index 281ac1a..97977df 100644
--- a/services/core/java/com/android/server/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/AbstractPerUserSystemService.java
@@ -167,7 +167,7 @@
     @GuardedBy("mLock")
     protected final int getServiceUidLocked() {
         if (mServiceInfo == null) {
-            Slog.w(mTag, "getServiceUidLocked(): no mServiceInfo");
+            if (mMaster.verbose) Slog.v(mTag, "getServiceUidLocked(): no mServiceInfo");
             return Process.INVALID_UID;
         }
         return mServiceInfo.applicationInfo.uid;
@@ -267,12 +267,18 @@
     @GuardedBy("mLock")
     protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
         pw.print(prefix); pw.print("User: "); pw.println(mUserId);
-        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+        pw.print(prefix); pw.print("Disabled by UserManager: "); pw.println(mDisabled);
         pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
         if (mServiceInfo != null) {
             pw.print(prefix); pw.print("Service UID: ");
             pw.println(mServiceInfo.applicationInfo.uid);
         }
-        pw.print(prefix); pw.print("Service name: "); pw.println(getComponentNameFromSettings());
+        final String componentName = getComponentNameFromSettings();
+        if (componentName != null) {
+            pw.print(prefix); pw.print("Service name: ");
+            pw.println(componentName);
+        } else {
+            pw.println("No service package set");
+        }
     }
 }
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/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..cfbe83d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -885,6 +885,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 +1163,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 +1192,7 @@
                     mConditionProviders.onUserUnlocked(userId);
                     mListeners.onUserUnlocked(userId);
                     mZenModeHelper.onUserUnlocked(userId);
+                    mPreferencesHelper.onUserUnlocked(userId);
                 }
             }
         }
@@ -2525,6 +2530,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();
         }
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 36bf83df..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/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index a55b49f..f78d263 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -20,7 +20,7 @@
 import android.content.Context;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.security.IKeystoreService;
+import android.security.keystore.IKeystoreService;
 import android.util.Slog;
 
 import com.android.internal.policy.IKeyguardService;
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 01d02d6..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");
@@ -366,6 +373,8 @@
         List<Integer> uids = new ArrayList<>();
         List<Long> versions = new ArrayList<>();
         List<String> apps = new ArrayList<>();
+        List<String> versionStrings = new ArrayList<>();
+        List<String> installers = new ArrayList<>();
 
         // Add in all the apps for every user/profile.
         for (UserInfo profile : users) {
@@ -373,14 +382,24 @@
                     pm.getInstalledPackagesAsUser(PackageManager.MATCH_KNOWN_PACKAGES, profile.id);
             for (int j = 0; j < pi.size(); j++) {
                 if (pi.get(j).applicationInfo != null) {
+                    String installer;
+                    try {
+                        installer = pm.getInstallerPackageName(pi.get(j).packageName);
+                    } catch (IllegalArgumentException e) {
+                        installer = "";
+                    }
+                    installers.add(installer == null ? "" : installer);
                     uids.add(pi.get(j).applicationInfo.uid);
                     versions.add(pi.get(j).getLongVersionCode());
+                    versionStrings.add(pi.get(j).versionName);
                     apps.add(pi.get(j).packageName);
                 }
             }
         }
-        sStatsd.informAllUidData(toIntArray(uids), toLongArray(versions), apps.toArray(new
-                String[apps.size()]));
+        sStatsd.informAllUidData(toIntArray(uids), toLongArray(versions),
+                versionStrings.toArray(new String[versionStrings.size()]),
+                apps.toArray(new String[apps.size()]),
+                installers.toArray(new String[installers.size()]));
         if (DEBUG) {
             Slog.d(TAG, "Sent data for " + uids.size() + " apps");
         }
@@ -422,7 +441,14 @@
                         int uid = b.getInt(Intent.EXTRA_UID);
                         String app = intent.getData().getSchemeSpecificPart();
                         PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
-                        sStatsd.informOnePackage(app, uid, pi.getLongVersionCode());
+                        String installer;
+                        try {
+                            installer = pm.getInstallerPackageName(app);
+                        } catch (IllegalArgumentException e) {
+                            installer = "";
+                        }
+                        sStatsd.informOnePackage(app, uid, pi.getLongVersionCode(), pi.versionName,
+                                installer == null ? "" : installer);
                     }
                 } catch (Exception e) {
                     Slog.w(TAG, "Failed to inform statsd of an app update", e);
@@ -1430,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,
@@ -1655,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/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 61e1414..1c08d03 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -383,7 +383,7 @@
             return;
         }
 
-        if (launchedActivity != null && launchedActivity.nowVisible) {
+        if (launchedActivity != null && launchedActivity.mDrawn) {
             // Launched activity is already visible. We cannot measure windows drawn delay.
             reset(true /* abort */, info, "launched activity already visible");
             return;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 23f8125..c43e64e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -20,6 +20,7 @@
 import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
 import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
 import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
 import static android.app.WaitResult.INVALID_DELAY;
@@ -75,6 +76,25 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
 
+import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
+import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
+import static com.android.server.am.ActivityRecordProto.PROC_ID;
+import static com.android.server.am.ActivityRecordProto.STATE;
+import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
+import static com.android.server.am.ActivityRecordProto.VISIBLE;
+import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
+import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
+import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStack.LAUNCH_TICK;
+import static com.android.server.wm.ActivityStack.LAUNCH_TICK_MSG;
+import static com.android.server.wm.ActivityStack.PAUSE_TIMEOUT_MSG;
+import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
@@ -89,34 +109,14 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
-import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
-import static com.android.server.am.ActivityRecordProto.PROC_ID;
-import static com.android.server.am.ActivityRecordProto.STATE;
-import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
-import static com.android.server.am.ActivityRecordProto.VISIBLE;
-import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.wm.ActivityStack.LAUNCH_TICK;
-import static com.android.server.wm.ActivityStack.LAUNCH_TICK_MSG;
-import static com.android.server.wm.ActivityStack.PAUSE_TIMEOUT_MSG;
-import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
-import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
-import static com.android.server.wm.TaskPersister.DEBUG;
-import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
+import static com.android.server.wm.TaskPersister.DEBUG;
+import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -179,9 +179,9 @@
 import com.android.server.AttributeCache.Entry;
 import com.android.server.am.AppTimeTracker;
 import com.android.server.am.PendingIntentRecord;
+import com.android.server.uri.UriPermissionOwner;
 import com.android.server.wm.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot;
 import com.android.server.wm.ActivityStack.ActivityState;
-import com.android.server.uri.UriPermissionOwner;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -303,6 +303,7 @@
                                         // process that it is hidden.
     boolean sleeping;       // have we told the activity to sleep?
     boolean nowVisible;     // is this activity's window visible?
+    boolean mDrawn;          // is this activity's window drawn?
     boolean mClientVisibilityDeferred;// was the visibility change message to client deferred?
     boolean idle;           // has the activity gone idle?
     boolean hasBeenLaunched;// has this activity ever been launched?
@@ -869,6 +870,7 @@
         inHistory = false;
         visible = false;
         nowVisible = false;
+        mDrawn = false;
         idle = false;
         hasBeenLaunched = false;
         mStackSupervisor = supervisor;
@@ -1944,8 +1946,12 @@
     }
 
     @Override
-    public void onWindowsDrawn(long timestamp) {
+    public void onWindowsDrawn(boolean drawn, long timestamp) {
         synchronized (service.mGlobalLock) {
+            mDrawn = drawn;
+            if (!drawn) {
+                return;
+            }
             final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
                     .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp);
             final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 3cbb2577..bd1460a 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -82,6 +82,7 @@
     private final class H extends Handler {
         public static final int NOTIFY_WINDOWS_DRAWN = 1;
         public static final int NOTIFY_STARTING_WINDOW_DRAWN = 2;
+        public static final int NOTIFY_WINDOWS_NOTDRAWN = 3;
 
         public H(Looper looper) {
             super(looper);
@@ -96,16 +97,24 @@
                     }
                     if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
                             + AppWindowContainerController.this.mToken);
-                    mListener.onWindowsDrawn(msg.getWhen());
+                    mListener.onWindowsDrawn(true /* drawn */, msg.getWhen());
                     break;
                 case NOTIFY_STARTING_WINDOW_DRAWN:
                     if (mListener == null) {
                         return;
                     }
-                    if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
+                    if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting starting window drawn in "
                             + AppWindowContainerController.this.mToken);
                     mListener.onStartingWindowDrawn(msg.getWhen());
                     break;
+                case NOTIFY_WINDOWS_NOTDRAWN:
+                    if (mListener == null) {
+                        return;
+                    }
+                    if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting not drawn in "
+                            + AppWindowContainerController.this.mToken);
+                    mListener.onWindowsDrawn(false /* drawn */, msg.getWhen());
+                    break;
                 default:
                     break;
             }
@@ -762,6 +771,10 @@
         mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_DRAWN));
     }
 
+    void reportWindowsNotDrawn() {
+        mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_NOTDRAWN));
+    }
+
     void reportWindowsVisible() {
         mHandler.post(mOnWindowsVisible);
     }
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerListener.java b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
index 8a39a74..ad27669 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerListener.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
@@ -18,8 +18,8 @@
 
 /** Interface used by the creator of the controller to listen to changes with the container. */
 public interface AppWindowContainerListener extends WindowContainerListener {
-    /** Called when the windows associated app window container are drawn. */
-    void onWindowsDrawn(long timestamp);
+    /** Called when the windows associated app window container drawn state changes. */
+    void onWindowsDrawn(boolean drawn, long timestamp);
     /** Called when the windows associated app window container are visible. */
     void onWindowsVisible();
     /** Called when the windows associated app window container are no longer visible. */
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 1709588..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;
 
@@ -366,6 +366,10 @@
                 if (controller != null) {
                     controller.reportWindowsDrawn();
                 }
+            } else {
+                if (controller != null) {
+                    controller.reportWindowsNotDrawn();
+                }
             }
             reportedDrawn = nowDrawn;
         }
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/ContentCaptureSession.java b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
index 9cab1ed..b7d34d7 100644
--- a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
+++ b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
@@ -76,7 +76,7 @@
     }
 
     /**
-     * Cleans up the session and remove itself from the service.
+     * Cleans up the session and removes it from the service.
      *
      * @param notifyRemoteService whether it should trigger a {@link
      * IntelligenceService#onDestroyInteractionSession(InteractionSessionId)}
@@ -85,14 +85,30 @@
     @GuardedBy("mLock")
     public void removeSelfLocked(boolean notifyRemoteService) {
         try {
-            if (notifyRemoteService) {
-                mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
-            }
+            destroyLocked(notifyRemoteService);
         } finally {
             mService.removeSessionLocked(mId);
         }
     }
 
+    /**
+     * Cleans up the session, but not removes it from the service.
+     *
+     * @param notifyRemoteService whether it should trigger a {@link
+     * IntelligenceService#onDestroyInteractionSession(InteractionSessionId)}
+     * request.
+     */
+    @GuardedBy("mLock")
+    public void destroyLocked(boolean notifyRemoteService) {
+        if (mService.isVerbose()) {
+            Slog.v(TAG, "destroyLocked(notifyRemoteService=" + notifyRemoteService + ")");
+        }
+        // TODO(b/111276913): must call client to set session as FINISHED_BY_SERVER
+        if (notifyRemoteService) {
+            mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
+        }
+    }
+
     @Override // from RemoteScreenObservationServiceCallbacks
     public void onServiceDied(AbstractRemoteService service) {
         // TODO(b/111276913): implement (remove session from PerUserSession?)
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
index 5763300..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);
     }
@@ -78,6 +79,12 @@
         publishLocalService(IntelligenceManagerInternal.class, mLocalService);
     }
 
+    @Override // from AbstractMasterSystemService
+    protected void onServiceRemoved(@NonNull IntelligencePerUserService service,
+            @UserIdInt int userId) {
+        service.destroyLocked();
+    }
+
     private ActivityManagerInternal getAmInternal() {
         synchronized (mLock) {
             if (mAm == null) {
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
index 829d2f4..471b40f 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -88,12 +88,18 @@
             @NonNull ComponentName componentName, int taskId, int displayId,
             @NonNull InteractionSessionId sessionId, int flags,
             @NonNull IResultReceiver resultReceiver) {
+        if (!isEnabledLocked()) {
+            sendToClient(resultReceiver, IntelligenceManager.STATE_DISABLED);
+            return;
+        }
         final ComponentName serviceComponentName = getServiceComponentName();
         if (serviceComponentName == null) {
             // TODO(b/111276913): this happens when the system service is starting, we should
             // probably handle it in a more elegant way (like waiting for boot_complete or
             // something like that
-            Slog.w(TAG, "startSession(" + activityToken + "): hold your horses");
+            if (mMaster.debug) {
+                Slog.d(TAG, "startSession(" + activityToken + "): hold your horses");
+            }
             return;
         }
 
@@ -128,9 +134,15 @@
     // TODO(b/111276913): log metrics
     @GuardedBy("mLock")
     public void finishSessionLocked(@NonNull InteractionSessionId sessionId) {
+        if (!isEnabledLocked()) {
+            return;
+        }
+
         final ContentCaptureSession session = mSessions.get(sessionId);
         if (session == null) {
-            Slog.w(TAG, "finishSession(): no session with id" + sessionId);
+            if (mMaster.debug) {
+                Slog.d(TAG, "finishSession(): no session with id" + sessionId);
+            }
             return;
         }
         if (mMaster.verbose) {
@@ -139,12 +151,19 @@
         session.removeSelfLocked(true);
     }
 
+    // TODO(b/111276913): need to figure out why some events are sent before session is started;
+    // probably because IntelligenceManager is not buffering them until it gets the session back
     @GuardedBy("mLock")
     public void sendEventsLocked(@NonNull InteractionSessionId sessionId,
             @NonNull List<ContentCaptureEvent> events) {
+        if (!isEnabledLocked()) {
+            return;
+        }
         final ContentCaptureSession session = mSessions.get(sessionId);
         if (session == null) {
-            Slog.w(TAG, "sendEvents(): no session for " + sessionId);
+            if (mMaster.verbose) {
+                Slog.v(TAG, "sendEvents(): no session for " + sessionId);
+            }
             return;
         }
         if (mMaster.verbose) {
@@ -163,6 +182,22 @@
         return uid == getServiceUidLocked();
     }
 
+    /**
+     * Destroys the service and all state associated with it.
+     *
+     * <p>Called when the service was disabled (for example, if the settings change).
+     */
+    @GuardedBy("mLock")
+    public void destroyLocked() {
+        if (mMaster.debug) Slog.d(TAG, "destroyLocked()");
+        final int numSessions = mSessions.size();
+        for (int i = 0; i < numSessions; i++) {
+            final ContentCaptureSession session = mSessions.valueAt(i);
+            session.destroyLocked(true);
+        }
+        mSessions.clear();
+    }
+
     @Override
     protected void dumpLocked(String prefix, PrintWriter pw) {
         super.dumpLocked(prefix, pw);
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 9ab06a1..565152c 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -27,7 +27,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     services.backup \
-    services.core
+    services.core \
+    services.net
 
 include $(BUILD_PACKAGE)
 
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/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/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 33df6f9..906d64c 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -49,18 +49,27 @@
     case Instruction::Op::kReturn:
       out << "kReturn";
       return out;
+    case Instruction::Op::kReturnObject:
+      out << "kReturnObject";
+      return out;
     case Instruction::Op::kMove:
       out << "kMove";
       return out;
     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;
   }
 }
 
@@ -137,6 +146,9 @@
     entry = Alloc<ir::String>();
     // +1 for null terminator
     entry->data = slicer::MemView{buffer.get(), header_length + string.size() + 1};
+    ::dex::u4 const new_index = dex_file_->strings_indexes.AllocateIndex();
+    dex_file_->strings_map[new_index] = entry;
+    entry->orig_index = new_index;
     string_data_.push_back(std::move(buffer));
   }
   return entry;
@@ -161,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;
 }
 
@@ -217,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);
@@ -240,8 +255,9 @@
 
 void MethodBuilder::BuildReturn() { AddInstruction(Instruction::OpNoArgs(Op::kReturn)); }
 
-void MethodBuilder::BuildReturn(Value src) {
-  AddInstruction(Instruction::OpWithArgs(Op::kReturn, /*destination=*/{}, src));
+void MethodBuilder::BuildReturn(Value src, bool is_object) {
+  AddInstruction(Instruction::OpWithArgs(
+      is_object ? Op::kReturnObject : Op::kReturn, /*destination=*/{}, src));
 }
 
 void MethodBuilder::BuildConst4(Value target, int value) {
@@ -249,6 +265,11 @@
   AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::Immediate(value)));
 }
 
+void MethodBuilder::BuildConstString(Value target, const std::string& value) {
+  const ir::String* const dex_string = dex_->GetOrAddString(value);
+  AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::String(dex_string->orig_index)));
+}
+
 void MethodBuilder::EncodeInstructions() {
   buffer_.clear();
   for (const auto& instruction : instructions_) {
@@ -259,27 +280,32 @@
 void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
   switch (instruction.opcode()) {
     case Instruction::Op::kReturn:
-      return EncodeReturn(instruction);
+      return EncodeReturn(instruction, ::art::Instruction::RETURN);
+    case Instruction::Op::kReturnObject:
+      return EncodeReturn(instruction, ::art::Instruction::RETURN_OBJECT);
     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) {
-  DCHECK_EQ(Instruction::Op::kReturn, instruction.opcode());
+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(instruction.args().size() == 1);
+    DCHECK_EQ(1, instruction.args().size());
     size_t source = RegisterValue(instruction.args()[0]);
-    buffer_.push_back(art::Instruction::RETURN | source << 8);
+    Encode11x(opcode, source);
   }
 }
 
@@ -294,31 +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
+    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.
@@ -331,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 0744151..adf82bf 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -110,18 +110,22 @@
   static constexpr Value Local(size_t id) { return Value{id, Kind::kLocalRegister}; }
   static constexpr Value Parameter(size_t id) { return Value{id, Kind::kParameter}; }
   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; }
   bool is_variable() const { return is_register() || is_parameter(); }
   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, kLabel };
+  enum class Kind { kLocalRegister, kParameter, kImmediate, kString, kLabel, kType };
 
   const size_t value_;
   const Kind kind_;
@@ -137,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, kMove, kInvokeVirtual, kBindLabel, kBranchEqz };
+  enum class Op {
+    kReturn,
+    kReturnObject,
+    kMove,
+    kInvokeVirtual,
+    kInvokeDirect,
+    kBindLabel,
+    kBranchEqz,
+    kNew
+  };
 
   ////////////////////////
   // Named Constructors //
@@ -158,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 //
@@ -187,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:
@@ -210,19 +235,74 @@
 
   // return-void
   void BuildReturn();
-  void BuildReturn(Value src);
+  void BuildReturn(Value src, bool is_object = false);
   // 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
 
  private:
   void EncodeInstructions();
   void EncodeInstruction(const Instruction& instruction);
-  void EncodeReturn(const Instruction& instruction);
+
+  // Encodes a return instruction. For instructions with no return value, the opcode field is
+  // ignored. Otherwise, this specifies which return instruction will be used (return,
+  // return-object, etc.)
+  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;
@@ -262,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.
@@ -281,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:
@@ -355,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 169c633..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");
@@ -82,4 +90,38 @@
     Method method = clazz.getMethod("backwardsBranch");
     Assert.assertEquals(2, method.invoke(null));
   }
+
+  @Test
+  public void returnNull() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("returnNull");
+    Assert.assertEquals(null, method.invoke(null));
+  }
+
+  @Test
+  public void makeString() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("makeString");
+    Assert.assertEquals("Hello, World!", method.invoke(null));
+  }
+
+  @Test
+  public void returnStringIfZeroAB() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("returnStringIfZeroAB", int.class);
+    Assert.assertEquals("a", method.invoke(null, 0));
+    Assert.assertEquals("b", method.invoke(null, 1));
+  }
+
+  @Test
+  public void returnStringIfZeroBA() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("returnStringIfZeroBA", int.class);
+    Assert.assertEquals("b", method.invoke(null, 0));
+    Assert.assertEquals("a", method.invoke(null, 1));
+  }
 }
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index c521bf2..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()})};
@@ -138,6 +151,71 @@
   }(backwardsBranch);
   backwardsBranch.Encode();
 
+  // Test that we can make a null value. Basically:
+  //
+  // public static String returnNull() { return null; }
+  MethodBuilder returnNull{cbuilder.CreateMethod("returnNull", Prototype{string_type})};
+  [](MethodBuilder& method) {
+    Value zero = method.MakeRegister();
+    method.BuildConst4(zero, 0);
+    method.BuildReturn(zero, /*is_object=*/true);
+  }(returnNull);
+  returnNull.Encode();
+
+  // Test that we can make String literals. Basically:
+  //
+  // public static String makeString() { return "Hello, World!"; }
+  MethodBuilder makeString{cbuilder.CreateMethod("makeString", Prototype{string_type})};
+  [](MethodBuilder& method) {
+    Value string = method.MakeRegister();
+    method.BuildConstString(string, "Hello, World!");
+    method.BuildReturn(string, /*is_object=*/true);
+  }(makeString);
+  makeString.Encode();
+
+  // Make sure strings are sorted correctly.
+  //
+  // int returnStringIfZeroAB(int x) { if (x == 0) { return "a"; } else { return "b"; } }
+  MethodBuilder returnStringIfZeroAB{
+      cbuilder.CreateMethod("returnStringIfZeroAB", Prototype{string_type, TypeDescriptor::Int()})};
+  [&](MethodBuilder& method) {
+    Value resultIfZero{method.MakeRegister()};
+    Value else_target{method.MakeLabel()};
+    method.AddInstruction(Instruction::OpWithArgs(
+        Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
+    // else branch
+    method.BuildConstString(resultIfZero, "b");
+    method.AddInstruction(
+        Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
+    // then branch
+    method.AddInstruction(
+        Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
+    method.BuildConstString(resultIfZero, "a");
+    method.AddInstruction(
+        Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
+    method.Encode();
+  }(returnStringIfZeroAB);
+  // int returnStringIfZeroAB(int x) { if (x == 0) { return "b"; } else { return "a"; } }
+  MethodBuilder returnStringIfZeroBA{
+      cbuilder.CreateMethod("returnStringIfZeroBA", Prototype{string_type, TypeDescriptor::Int()})};
+  [&](MethodBuilder& method) {
+    Value resultIfZero{method.MakeRegister()};
+    Value else_target{method.MakeLabel()};
+    method.AddInstruction(Instruction::OpWithArgs(
+        Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
+    // else branch
+    method.BuildConstString(resultIfZero, "a");
+    method.AddInstruction(
+        Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
+    // then branch
+    method.AddInstruction(
+        Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
+    method.BuildConstString(resultIfZero, "b");
+    method.AddInstruction(
+        Instruction::OpWithArgs(Instruction::Op::kReturnObject, /*dest=*/{}, resultIfZero));
+    method.Encode();
+  }(returnStringIfZeroBA);
+
   slicer::MemView image{dex_file.CreateImage()};
   std::ofstream out_file(outdir + "/simple.dex");
   out_file.write(image.ptr<const char>(), image.size());
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 f5dff20..3c5ad84 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -21,16 +21,18 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Looper;
-import android.os.Message;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.IPhoneStateListener;
 
 import java.lang.ref.WeakReference;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * A listener class for monitoring changes in specific telephony states
@@ -231,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;
 
@@ -320,7 +323,12 @@
     @UnsupportedAppUsage
     protected Integer mSubId;
 
-    private final Handler mHandler;
+    /**
+     * @hide
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    @UnsupportedAppUsage
+    public final IPhoneStateListener callback;
 
     /**
      * Create a PhoneStateListener for the Phone with the default subscription.
@@ -358,95 +366,27 @@
      */
     @UnsupportedAppUsage
     public PhoneStateListener(Integer subId, Looper looper) {
-        if (DBG) log("ctor: subId=" + subId + " looper=" + looper);
+        this(subId, new HandlerExecutor(new Handler(looper)));
+    }
+
+    /**
+     * Create a PhoneStateListener for the Phone using the specified Executor
+     *
+     * <p>Create a PhoneStateListener with a specified Executor for handling necessary callbacks.
+     * The Executor must not be null.
+     *
+     * @param executor a non-null Executor that will execute callbacks for the PhoneStateListener.
+     */
+    public PhoneStateListener(@NonNull Executor executor) {
+        this(null, executor);
+    }
+
+    private PhoneStateListener(Integer subId, Executor e) {
+        if (e == null) {
+            throw new IllegalArgumentException("PhoneStateListener Executor must be non-null");
+        }
         mSubId = subId;
-        mHandler = new Handler(looper) {
-            public void handleMessage(Message msg) {
-                if (DBG) {
-                    log("mSubId=" + mSubId + " what=0x" + Integer.toHexString(msg.what)
-                            + " msg=" + msg);
-                }
-                switch (msg.what) {
-                    case LISTEN_SERVICE_STATE:
-                        PhoneStateListener.this.onServiceStateChanged((ServiceState)msg.obj);
-                        break;
-                    case LISTEN_SIGNAL_STRENGTH:
-                        PhoneStateListener.this.onSignalStrengthChanged(msg.arg1);
-                        break;
-                    case LISTEN_MESSAGE_WAITING_INDICATOR:
-                        PhoneStateListener.this.onMessageWaitingIndicatorChanged(msg.arg1 != 0);
-                        break;
-                    case LISTEN_CALL_FORWARDING_INDICATOR:
-                        PhoneStateListener.this.onCallForwardingIndicatorChanged(msg.arg1 != 0);
-                        break;
-                    case LISTEN_CELL_LOCATION:
-                        PhoneStateListener.this.onCellLocationChanged((CellLocation)msg.obj);
-                        break;
-                    case LISTEN_CALL_STATE:
-                        PhoneStateListener.this.onCallStateChanged(msg.arg1, (String)msg.obj);
-                        break;
-                    case LISTEN_DATA_CONNECTION_STATE:
-                        PhoneStateListener.this.onDataConnectionStateChanged(msg.arg1, msg.arg2);
-                        PhoneStateListener.this.onDataConnectionStateChanged(msg.arg1);
-                        break;
-                    case LISTEN_DATA_ACTIVITY:
-                        PhoneStateListener.this.onDataActivity(msg.arg1);
-                        break;
-                    case LISTEN_SIGNAL_STRENGTHS:
-                        PhoneStateListener.this.onSignalStrengthsChanged((SignalStrength)msg.obj);
-                        break;
-                    case LISTEN_OTASP_CHANGED:
-                        PhoneStateListener.this.onOtaspChanged(msg.arg1);
-                        break;
-                    case LISTEN_CELL_INFO:
-                        PhoneStateListener.this.onCellInfoChanged((List<CellInfo>)msg.obj);
-                        break;
-                    case LISTEN_PRECISE_CALL_STATE:
-                        PhoneStateListener.this.onPreciseCallStateChanged((PreciseCallState)msg.obj);
-                        break;
-                    case LISTEN_PRECISE_DATA_CONNECTION_STATE:
-                        PhoneStateListener.this.onPreciseDataConnectionStateChanged(
-                                (PreciseDataConnectionState)msg.obj);
-                        break;
-                    case LISTEN_DATA_CONNECTION_REAL_TIME_INFO:
-                        PhoneStateListener.this.onDataConnectionRealTimeInfoChanged(
-                                (DataConnectionRealTimeInfo)msg.obj);
-                        break;
-                    case LISTEN_SRVCC_STATE_CHANGED:
-                        PhoneStateListener.this.onSrvccStateChanged((int) msg.obj);
-                        break;
-                    case LISTEN_VOICE_ACTIVATION_STATE:
-                        PhoneStateListener.this.onVoiceActivationStateChanged((int)msg.obj);
-                        break;
-                    case LISTEN_DATA_ACTIVATION_STATE:
-                        PhoneStateListener.this.onDataActivationStateChanged((int)msg.obj);
-                        break;
-                    case LISTEN_USER_MOBILE_DATA_STATE:
-                        PhoneStateListener.this.onUserMobileDataStateChanged((boolean)msg.obj);
-                        break;
-                    case LISTEN_OEM_HOOK_RAW_EVENT:
-                        PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj);
-                        break;
-                    case LISTEN_CARRIER_NETWORK_CHANGE:
-                        PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
-                        break;
-                    case LISTEN_PHYSICAL_CHANNEL_CONFIGURATION:
-                        PhoneStateListener.this.onPhysicalChannelConfigurationChanged(
-                                (List<PhysicalChannelConfig>)msg.obj);
-                        break;
-                    case LISTEN_PHONE_CAPABILITY_CHANGE:
-                        PhoneStateListener.this.onPhoneCapabilityChanged(
-                                (PhoneCapability) msg.obj);
-                        break;
-                    case LISTEN_PREFERRED_DATA_SUBID_CHANGE:
-                        PhoneStateListener.this.onPreferredDataSubIdChanged((int) msg.obj);
-                        break;
-                    case LISTEN_RADIO_POWER_STATE_CHANGED:
-                        PhoneStateListener.this.onRadioPowerStateChanged((int) msg.obj);
-                        break;
-                }
-            }
-        };
+        callback = new IPhoneStateListenerStub(this, e);
     }
 
     /**
@@ -630,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) {
     }
 
     /**
@@ -639,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) {
     }
 
     /**
@@ -735,127 +674,217 @@
      */
     private static class IPhoneStateListenerStub extends IPhoneStateListener.Stub {
         private WeakReference<PhoneStateListener> mPhoneStateListenerWeakRef;
+        private Executor mExecutor;
 
-        public IPhoneStateListenerStub(PhoneStateListener phoneStateListener) {
+        IPhoneStateListenerStub(PhoneStateListener phoneStateListener, Executor executor) {
             mPhoneStateListenerWeakRef = new WeakReference<PhoneStateListener>(phoneStateListener);
-        }
-
-        private void send(int what, int arg1, int arg2, Object obj) {
-            PhoneStateListener listener = mPhoneStateListenerWeakRef.get();
-            if (listener != null) {
-                Message.obtain(listener.mHandler, what, arg1, arg2, obj).sendToTarget();
-            }
+            mExecutor = executor;
         }
 
         public void onServiceStateChanged(ServiceState serviceState) {
-            send(LISTEN_SERVICE_STATE, 0, 0, serviceState);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onServiceStateChanged(serviceState)));
         }
 
         public void onSignalStrengthChanged(int asu) {
-            send(LISTEN_SIGNAL_STRENGTH, asu, 0, null);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onSignalStrengthChanged(asu)));
         }
 
         public void onMessageWaitingIndicatorChanged(boolean mwi) {
-            send(LISTEN_MESSAGE_WAITING_INDICATOR, mwi ? 1 : 0, 0, null);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onMessageWaitingIndicatorChanged(mwi)));
         }
 
         public void onCallForwardingIndicatorChanged(boolean cfi) {
-            send(LISTEN_CALL_FORWARDING_INDICATOR, cfi ? 1 : 0, 0, null);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCallForwardingIndicatorChanged(cfi)));
         }
 
         public void onCellLocationChanged(Bundle bundle) {
             CellLocation location = CellLocation.newFromBundle(bundle);
-            send(LISTEN_CELL_LOCATION, 0, 0, location);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCellLocationChanged(location)));
         }
 
         public void onCallStateChanged(int state, String incomingNumber) {
-            send(LISTEN_CALL_STATE, state, 0, incomingNumber);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCallStateChanged(state, incomingNumber)));
         }
 
         public void onDataConnectionStateChanged(int state, int networkType) {
-            send(LISTEN_DATA_CONNECTION_STATE, state, networkType, null);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onDataConnectionStateChanged(state, networkType)));
         }
 
         public void onDataActivity(int direction) {
-            send(LISTEN_DATA_ACTIVITY, direction, 0, null);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onDataActivity(direction)));
         }
 
         public void onSignalStrengthsChanged(SignalStrength signalStrength) {
-            send(LISTEN_SIGNAL_STRENGTHS, 0, 0, signalStrength);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onSignalStrengthsChanged(signalStrength)));
         }
 
         public void onOtaspChanged(int otaspMode) {
-            send(LISTEN_OTASP_CHANGED, otaspMode, 0, null);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onOtaspChanged(otaspMode)));
         }
 
         public void onCellInfoChanged(List<CellInfo> cellInfo) {
-            send(LISTEN_CELL_INFO, 0, 0, cellInfo);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCellInfoChanged(cellInfo)));
         }
 
         public void onPreciseCallStateChanged(PreciseCallState callState) {
-            send(LISTEN_PRECISE_CALL_STATE, 0, 0, callState);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onPreciseCallStateChanged(callState)));
         }
 
         public void onPreciseDataConnectionStateChanged(
                 PreciseDataConnectionState dataConnectionState) {
-            send(LISTEN_PRECISE_DATA_CONNECTION_STATE, 0, 0, dataConnectionState);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onPreciseDataConnectionStateChanged(dataConnectionState)));
         }
 
-        public void onDataConnectionRealTimeInfoChanged(
-                DataConnectionRealTimeInfo dcRtInfo) {
-            send(LISTEN_DATA_CONNECTION_REAL_TIME_INFO, 0, 0, dcRtInfo);
+        public void onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo dcRtInfo) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onDataConnectionRealTimeInfoChanged(dcRtInfo)));
         }
 
         public void onSrvccStateChanged(int state) {
-            send(LISTEN_SRVCC_STATE_CHANGED, 0, 0, state);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onSrvccStateChanged(state)));
         }
 
         public void onVoiceActivationStateChanged(int activationState) {
-            send(LISTEN_VOICE_ACTIVATION_STATE, 0, 0, activationState);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onVoiceActivationStateChanged(activationState)));
         }
 
         public void onDataActivationStateChanged(int activationState) {
-            send(LISTEN_DATA_ACTIVATION_STATE, 0, 0, activationState);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onDataActivationStateChanged(activationState)));
         }
 
         public void onUserMobileDataStateChanged(boolean enabled) {
-            send(LISTEN_USER_MOBILE_DATA_STATE, 0, 0, enabled);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onUserMobileDataStateChanged(enabled)));
         }
 
         public void onOemHookRawEvent(byte[] rawData) {
-            send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onOemHookRawEvent(rawData)));
         }
 
         public void onCarrierNetworkChange(boolean active) {
-            send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onCarrierNetworkChange(active)));
         }
 
         public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
-            send(LISTEN_PHYSICAL_CHANNEL_CONFIGURATION, 0, 0, configs);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onPhysicalChannelConfigurationChanged(configs)));
         }
 
         public void onPhoneCapabilityChanged(PhoneCapability capability) {
-            send(LISTEN_PHONE_CAPABILITY_CHANGE, 0, 0, capability);
-        }
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
 
-        public void onPreferredDataSubIdChanged(int subId) {
-            send(LISTEN_PREFERRED_DATA_SUBID_CHANGE, 0, 0, subId);
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onPhoneCapabilityChanged(capability)));
         }
 
         public void onRadioPowerStateChanged(@TelephonyManager.RadioPowerState int state) {
-            send(LISTEN_RADIO_POWER_STATE_CHANGED, 0, 0, state);
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onRadioPowerStateChanged(state)));
         }
 
+        public void onPreferredDataSubIdChanged(int subId) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onPreferredDataSubIdChanged(subId)));
+        }
     }
 
-    /**
-     * @hide
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    @UnsupportedAppUsage
-    public final IPhoneStateListener callback = new IPhoneStateListenerStub(this);
 
     private void log(String s) {
         Rlog.d(LOG_TAG, s);
     }
-}
\ No newline at end of file
+}
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/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 954b51f..7919074 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -4256,27 +4256,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);
     }