Merge "BluetoothA2dpSink System APIs now require BLUETOOTH_ADMIN permission"
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 232a7cf..8abe64c 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -186,7 +186,6 @@
 droidstubs {
     name: "module-lib-api",
     defaults: ["metalava-api-stubs-default"],
-    libs: ["framework-all"],
     arg_files: ["core/res/AndroidManifest.xml"],
     args: metalava_framework_docs_args + module_libs,
     check_api: {
@@ -218,7 +217,6 @@
 droidstubs {
     name: "module-lib-api-stubs-docs",
     defaults: ["metalava-api-stubs-default"],
-    libs: ["framework-all"],
     arg_files: ["core/res/AndroidManifest.xml"],
     args: metalava_framework_docs_args + priv_apps + module_libs,
 }
@@ -230,6 +228,9 @@
 
 java_defaults {
     name: "framework-stubs-default",
+    libs: [ "stub-annotations" ],
+    static_libs: [ "private-stub-annotations-jar" ],
+    sdk_version: "core_current",
     errorprone: {
         javacflags: [
             "-XepDisableAllChecks",
@@ -245,61 +246,25 @@
 
 java_library_static {
     name: "android_stubs_current",
-    srcs: [
-        ":api-stubs-docs",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    static_libs: [
-        "private-stub-annotations-jar",
-    ],
+    srcs: [ ":api-stubs-docs" ],
     defaults: ["framework-stubs-default"],
-    sdk_version: "core_current",
 }
 
 java_library_static {
     name: "android_system_stubs_current",
-    srcs: [
-        ":system-api-stubs-docs",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    static_libs: [
-        "private-stub-annotations-jar",
-    ],
+    srcs: [ ":system-api-stubs-docs" ],
     defaults: ["framework-stubs-default"],
-    sdk_version: "core_current",
 }
 
 java_library_static {
     name: "android_test_stubs_current",
-    srcs: [
-        ":test-api-stubs-docs",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    static_libs: [
-        "private-stub-annotations-jar",
-    ],
+    srcs: [ ":test-api-stubs-docs" ],
     defaults: ["framework-stubs-default"],
-    sdk_version: "core_current",
 }
 
 java_library_static {
     name: "android_module_lib_stubs_current",
-    srcs: [
-        ":module-lib-api-stubs-docs",
-    ],
-    libs: [
-        "stub-annotations",
-        "framework-all",
-    ],
-    static_libs: [
-        "private-stub-annotations-jar",
-    ],
+    srcs: [ ":module-lib-api-stubs-docs" ],
     defaults: ["framework-stubs-default"],
 }
 
diff --git a/apex/sdkextensions/derive_sdk/derive_sdk.cpp b/apex/sdkextensions/derive_sdk/derive_sdk.cpp
index 6fb7ef4..900193a 100644
--- a/apex/sdkextensions/derive_sdk/derive_sdk.cpp
+++ b/apex/sdkextensions/derive_sdk/derive_sdk.cpp
@@ -69,7 +69,7 @@
     auto itr = std::min_element(versions.begin(), versions.end());
     std::string prop_value = itr == versions.end() ? "0" : std::to_string(*itr);
 
-    if (!android::base::SetProperty("ro.build.version.extensions.r", prop_value)) {
+    if (!android::base::SetProperty("build.version.extensions.r", prop_value)) {
         LOG(ERROR) << "failed to set sdk_info prop";
         return EXIT_FAILURE;
     }
diff --git a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
index a8a7eff..103b53e 100644
--- a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
+++ b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
@@ -38,7 +38,7 @@
 
     private static final int R_EXTENSION_INT;
     static {
-        R_EXTENSION_INT = SystemProperties.getInt("ro.build.version.extensions.r", 0);
+        R_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.r", 0);
     }
 
     /**
diff --git a/api/current.txt b/api/current.txt
index 7136e3f..c72de2e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -29796,7 +29796,6 @@
     method public void close();
     method public void continueCall(int) throws android.net.sip.SipException;
     method public void endCall() throws android.net.sip.SipException;
-    method @Nullable public android.net.rtp.AudioGroup getAudioGroup();
     method public android.net.sip.SipProfile getLocalProfile();
     method public android.net.sip.SipProfile getPeerProfile();
     method public int getState();
@@ -29807,7 +29806,6 @@
     method public void makeCall(android.net.sip.SipProfile, android.net.sip.SipSession, int) throws android.net.sip.SipException;
     method public void sendDtmf(int);
     method public void sendDtmf(int, android.os.Message);
-    method public void setAudioGroup(@NonNull android.net.rtp.AudioGroup);
     method public void setListener(android.net.sip.SipAudioCall.Listener);
     method public void setListener(android.net.sip.SipAudioCall.Listener, boolean);
     method public void setSpeakerMode(boolean);
@@ -29856,7 +29854,6 @@
     method public void close(String) throws android.net.sip.SipException;
     method public android.net.sip.SipSession createSipSession(android.net.sip.SipProfile, android.net.sip.SipSession.Listener) throws android.net.sip.SipException;
     method public static String getCallId(android.content.Intent);
-    method @NonNull public java.util.List<android.net.sip.SipProfile> getListOfProfiles() throws android.net.sip.SipException;
     method public static String getOfferSessionDescription(android.content.Intent);
     method public android.net.sip.SipSession getSessionFor(android.content.Intent) throws android.net.sip.SipException;
     method public static boolean isApiSupported(android.content.Context);
@@ -29874,11 +29871,6 @@
     method public void setRegistrationListener(String, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
     method public android.net.sip.SipAudioCall takeAudioCall(android.content.Intent, android.net.sip.SipAudioCall.Listener) throws android.net.sip.SipException;
     method public void unregister(android.net.sip.SipProfile, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
-    field public static final String ACTION_SIP_CALL_OPTION_CHANGED = "android.net.sip.action.SIP_CALL_OPTION_CHANGED";
-    field public static final String ACTION_SIP_INCOMING_CALL = "android.net.sip.action.SIP_INCOMING_CALL";
-    field public static final String ACTION_SIP_REMOVE_PROFILE = "android.net.sip.action.SIP_REMOVE_PROFILE";
-    field public static final String ACTION_SIP_SERVICE_UP = "android.net.sip.action.SIP_SERVICE_UP";
-    field public static final String ACTION_START_SIP = "android.net.sip.action.START_SIP";
     field public static final String EXTRA_CALL_ID = "android:sipCallID";
     field public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
     field public static final int INCOMING_CALL_RESULT_CODE = 101; // 0x65
@@ -29888,7 +29880,6 @@
     method public int describeContents();
     method public String getAuthUserName();
     method public boolean getAutoRegistration();
-    method public int getCallingUid();
     method public String getDisplayName();
     method public String getPassword();
     method public int getPort();
diff --git a/api/system-current.txt b/api/system-current.txt
index d7aa32d..8496edc 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -59,6 +59,7 @@
     field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
     field @Deprecated public static final String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
     field public static final String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
+    field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS";
     field public static final String CONTROL_DISPLAY_COLOR_TRANSFORMS = "android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS";
     field public static final String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION";
     field public static final String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE";
@@ -1471,9 +1472,9 @@
   }
 
   public class BluetoothPbap implements android.bluetooth.BluetoothProfile {
-    method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
-    field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+    field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
   }
 
   public interface BluetoothProfile {
@@ -2562,6 +2563,48 @@
 
 }
 
+package android.hardware.lights {
+
+  public final class Light implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getId();
+    method public int getOrdinal();
+    method public int getType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR;
+  }
+
+  public final class LightState implements android.os.Parcelable {
+    ctor public LightState(@ColorInt int);
+    method public int describeContents();
+    method @ColorInt public int getColor();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR;
+  }
+
+  public final class LightsManager {
+    method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public java.util.List<android.hardware.lights.Light> getLights();
+    method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightsManager.LightsSession openSession();
+    field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
+  }
+
+  public final class LightsManager.LightsSession implements java.lang.AutoCloseable {
+    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close();
+    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void setLights(@NonNull android.hardware.lights.LightsRequest);
+  }
+
+  public final class LightsRequest {
+  }
+
+  public static final class LightsRequest.Builder {
+    ctor public LightsRequest.Builder();
+    method @NonNull public android.hardware.lights.LightsRequest build();
+    method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light);
+    method @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState);
+  }
+
+}
+
 package android.hardware.location {
 
   public class ContextHubClient implements java.io.Closeable {
@@ -5442,6 +5485,28 @@
 
 }
 
+package android.net.sip {
+
+  public class SipAudioCall {
+    method @Nullable public android.net.rtp.AudioGroup getAudioGroup();
+    method public void setAudioGroup(@NonNull android.net.rtp.AudioGroup);
+  }
+
+  public class SipManager {
+    method @NonNull public java.util.List<android.net.sip.SipProfile> getProfiles() throws android.net.sip.SipException;
+    field public static final String ACTION_SIP_CALL_OPTION_CHANGED = "android.net.sip.action.SIP_CALL_OPTION_CHANGED";
+    field public static final String ACTION_SIP_INCOMING_CALL = "android.net.sip.action.SIP_INCOMING_CALL";
+    field public static final String ACTION_SIP_REMOVE_PROFILE = "android.net.sip.action.SIP_REMOVE_PROFILE";
+    field public static final String ACTION_SIP_SERVICE_UP = "android.net.sip.action.SIP_SERVICE_UP";
+    field public static final String ACTION_START_SIP = "android.net.sip.action.START_SIP";
+  }
+
+  public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
+    method public int getCallingUid();
+  }
+
+}
+
 package android.net.util {
 
   public final class SocketUtils {
@@ -9558,6 +9623,7 @@
   public class TelephonyManager {
     method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int changeIccLockPassword(@NonNull String, @NonNull String);
     method public int checkCarrierPrivilegesForPackage(String);
     method public int checkCarrierPrivilegesForPackageAnyPhone(String);
     method public void dial(String);
@@ -9619,10 +9685,12 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
     method public boolean isCurrentSimOperator(@NonNull String, int, @Nullable String);
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataAllowedInVoiceCall();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionEnabled();
     method public boolean isDataConnectivityPossible();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isManualNetworkSelectionAllowed();
@@ -9657,8 +9725,10 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setDataAllowedDuringVoiceCall(boolean);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setIccLockEnabled(boolean, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(@NonNull String, int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
@@ -9701,6 +9771,7 @@
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
     field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
+    field public static final int CHANGE_ICC_LOCK_SUCCESS = 2147483647; // 0x7fffffff
     field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
     field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
     field @Deprecated public static final String EXTRA_APN_PROTOCOL = "apnProto";
diff --git a/api/test-current.txt b/api/test-current.txt
index 1e5b7b8..afb49c2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10,6 +10,7 @@
     field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
     field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
     field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
+    field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS";
     field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
     field public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
@@ -985,6 +986,49 @@
 
 }
 
+package android.hardware.lights {
+
+  public final class Light implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getId();
+    method public int getOrdinal();
+    method public int getType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR;
+  }
+
+  public final class LightState implements android.os.Parcelable {
+    ctor public LightState(@ColorInt int);
+    method public int describeContents();
+    method @ColorInt public int getColor();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR;
+  }
+
+  public final class LightsManager {
+    method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightState getLightState(@NonNull android.hardware.lights.Light);
+    method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public java.util.List<android.hardware.lights.Light> getLights();
+    method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightsManager.LightsSession openSession();
+    field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
+  }
+
+  public final class LightsManager.LightsSession implements java.lang.AutoCloseable {
+    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close();
+    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void setLights(@NonNull android.hardware.lights.LightsRequest);
+  }
+
+  public final class LightsRequest {
+  }
+
+  public static final class LightsRequest.Builder {
+    ctor public LightsRequest.Builder();
+    method @NonNull public android.hardware.lights.LightsRequest build();
+    method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light);
+    method @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState);
+  }
+
+}
+
 package android.location {
 
   public final class GnssClock implements android.os.Parcelable {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 86b2c06..d79cd97 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -83,6 +83,7 @@
 import android.hardware.input.InputManager;
 import android.hardware.iris.IIrisService;
 import android.hardware.iris.IrisManager;
+import android.hardware.lights.LightsManager;
 import android.hardware.location.ContextHubManager;
 import android.hardware.radio.RadioManager;
 import android.hardware.usb.IUsbManager;
@@ -1348,6 +1349,13 @@
                         return new DynamicSystemManager(
                                 IDynamicSystemService.Stub.asInterface(b));
                     }});
+        registerService(Context.LIGHTS_SERVICE, LightsManager.class,
+            new CachedServiceFetcher<LightsManager>() {
+                @Override
+                public LightsManager createService(ContextImpl ctx)
+                    throws ServiceNotFoundException {
+                    return new LightsManager(ctx);
+                }});
         //CHECKSTYLE:ON IndentationCheck
     }
 
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index 5b36789..e289a27 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemApi;
 import android.compat.Compatibility;
 import android.content.Context;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -70,11 +71,14 @@
             @NonNull UserHandle user) {
         IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+        final long token = Binder.clearCallingIdentity();
         try {
             return platformCompat.isChangeEnabledByPackageName(changeId, packageName,
                     user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
     }
 
@@ -99,10 +103,13 @@
     public static boolean isChangeEnabled(long changeId, int uid) {
         IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+        final long token = Binder.clearCallingIdentity();
         try {
             return platformCompat.isChangeEnabledByUid(changeId, uid);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
     }
 }
diff --git a/core/java/android/app/compat/TEST_MAPPING b/core/java/android/app/compat/TEST_MAPPING
new file mode 100644
index 0000000..c047df5
--- /dev/null
+++ b/core/java/android/app/compat/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+    "imports": [
+        {
+            "path": "frameworks/base/services/core/java/com/android/services/compat"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index e07ca52..1f89ddf 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -38,9 +38,6 @@
 import java.util.List;
 
 /**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- *
  * Public API for controlling the Bluetooth Pbap Service. This includes
  * Bluetooth Phone book Access profile.
  * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
@@ -56,6 +53,11 @@
  * notification when it is bound, this is especially important if you wish to
  * immediately call methods on BluetoothPbap after construction.
  *
+ * To get an instance of the BluetoothPbap class, you can call
+ * {@link BluetoothAdapter#getProfileProxy(Context, ServiceListener, int)} with the final param
+ * being {@link BluetoothProfile#PBAP}. The ServiceListener should be able to get the instance of
+ * BluetoothPbap in {@link android.bluetooth.BluetoothProfile.ServiceListener#onServiceConnected}.
+ *
  * Android only supports one connected Bluetooth Pce at a time.
  *
  * @hide
@@ -87,6 +89,7 @@
      */
     @SuppressLint("ActionValue")
     @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_CONNECTION_STATE_CHANGED =
             "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
@@ -235,7 +238,8 @@
      */
     @SystemApi
     @Override
-    public int getConnectionState(@Nullable BluetoothDevice device) {
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public @BtProfileState int getConnectionState(@Nullable BluetoothDevice device) {
         log("getConnectionState: device=" + device);
         try {
             final IBluetoothPbap service = mService;
@@ -287,7 +291,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c3c7f97..4527d8e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3380,6 +3380,7 @@
             //@hide: TIME_DETECTOR_SERVICE,
             //@hide: TIME_ZONE_DETECTOR_SERVICE,
             PERMISSION_SERVICE,
+            LIGHTS_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceName {}
@@ -4880,6 +4881,15 @@
     public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.lights.LightsManager} for controlling device lights.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    public static final String LIGHTS_SERVICE = "lights";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/hardware/lights/ILightsManager.aidl b/core/java/android/hardware/lights/ILightsManager.aidl
new file mode 100644
index 0000000..6ea24b7
--- /dev/null
+++ b/core/java/android/hardware/lights/ILightsManager.aidl
@@ -0,0 +1,33 @@
+/**
+ * Copyright (C) 2020 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.hardware.lights;
+
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+
+/**
+ * API to lights manager service.
+ *
+ * {@hide}
+ */
+interface ILightsManager {
+  List<Light> getLights();
+  LightState getLightState(int lightId);
+  void openSession(in IBinder sessionToken);
+  void closeSession(in IBinder sessionToken);
+  void setLightStates(in IBinder sessionToken, in int[] lightIds, in LightState[] states);
+}
diff --git a/core/java/android/hardware/lights/Light.aidl b/core/java/android/hardware/lights/Light.aidl
new file mode 100644
index 0000000..946e06d4
--- /dev/null
+++ b/core/java/android/hardware/lights/Light.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (C) 2020 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.hardware.lights;
+
+/** @hide */
+parcelable Light;
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
new file mode 100644
index 0000000..c5cb803
--- /dev/null
+++ b/core/java/android/hardware/lights/Light.java
@@ -0,0 +1,105 @@
+/**
+ * Copyright (C) 2020 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.hardware.lights;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a logical light on the device.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class Light implements Parcelable {
+    private final int mId;
+    private final int mOrdinal;
+    private final int mType;
+
+    /**
+     * Creates a new light with the given data.
+     *
+     * @hide */
+    public Light(int id, int ordinal, int type) {
+        mId = id;
+        mOrdinal = ordinal;
+        mType = type;
+    }
+
+    private Light(@NonNull Parcel in) {
+        mId = in.readInt();
+        mOrdinal = in.readInt();
+        mType = in.readInt();
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mId);
+        dest.writeInt(mOrdinal);
+        dest.writeInt(mType);
+    }
+
+    /** Implement the Parcelable interface */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @android.annotation.NonNull Parcelable.Creator<Light> CREATOR =
+            new Parcelable.Creator<Light>() {
+                public Light createFromParcel(Parcel in) {
+                    return new Light(in);
+                }
+
+                public Light[] newArray(int size) {
+                    return new Light[size];
+                }
+            };
+
+    /**
+     * Returns the id of the light.
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the ordinal of the light.
+     *
+     * <p>This represents the physical order of the lights on the device. The exact values are
+     * device-dependent, but for example, if there are lights in a row, sorting the Light objects
+     * by ordinal should match the order in which they appear on the device. If the device has
+     * 4 lights, the ordinals could be [1, 2, 3, 4] or [0, 10, 20, 30] or any other values that
+     * have the same sort order.
+     */
+    public int getOrdinal() {
+        return mOrdinal;
+    }
+
+    /**
+     * Returns the logical type of the light.
+     */
+    public @LightsManager.LightType int getType() {
+        return mType;
+    }
+}
diff --git a/core/java/android/hardware/lights/LightState.aidl b/core/java/android/hardware/lights/LightState.aidl
new file mode 100644
index 0000000..d598336
--- /dev/null
+++ b/core/java/android/hardware/lights/LightState.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (C) 2020 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.hardware.lights;
+
+/** @hide */
+parcelable LightState;
diff --git a/core/java/android/hardware/lights/LightState.java b/core/java/android/hardware/lights/LightState.java
new file mode 100644
index 0000000..e55aa70
--- /dev/null
+++ b/core/java/android/hardware/lights/LightState.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright (C) 2020 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.hardware.lights;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents the state of a device light.
+ *
+ * <p>Controlling the color and brightness of a light is done on a best-effort basis. Each of the R,
+ * G and B channels represent the intensities of the respective part of an RGB LED, if that is
+ * supported. For devices that only support on or off lights, everything that's not off will turn
+ * the light on. If the light is monochrome and only the brightness can be controlled, the RGB color
+ * will be converted to only a brightness value and that will be used for the light's single
+ * channel.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class LightState implements Parcelable {
+    private final int mColor;
+
+    /**
+     * Creates a new LightState with the desired color and intensity.
+     *
+     * @param color the desired color and intensity in ARGB format.
+     */
+    public LightState(@ColorInt int color) {
+        mColor = color;
+    }
+
+    private LightState(@NonNull Parcel in) {
+        mColor = in.readInt();
+    }
+
+    /**
+     * Return the color and intensity associated with this LightState.
+     * @return the color and intensity in ARGB format. The A channel is ignored.
+     */
+    public @ColorInt int getColor() {
+        return mColor;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mColor);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Parcelable.Creator<LightState> CREATOR =
+            new Parcelable.Creator<LightState>() {
+                public LightState createFromParcel(Parcel in) {
+                    return new LightState(in);
+                }
+
+                public LightState[] newArray(int size) {
+                    return new LightState[size];
+                }
+            };
+}
diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java
new file mode 100644
index 0000000..1bc051b
--- /dev/null
+++ b/core/java/android/hardware/lights/LightsManager.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2020 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.hardware.lights;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.CloseGuard;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.Reference;
+import java.util.List;
+
+/**
+ * The LightsManager class allows control over device lights.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+@SystemService(Context.LIGHTS_SERVICE)
+public final class LightsManager {
+    private static final String TAG = "LightsManager";
+
+    // These enum values copy the values from {@link com.android.server.lights.LightsManager}
+    // and the light HAL. Since 0-7 are lights reserved for system use, only the microphone light
+    // is available through this API.
+    /** Type for lights that indicate microphone usage */
+    public static final int LIGHT_TYPE_MICROPHONE = 8;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"LIGHT_TYPE_"},
+        value = {
+            LIGHT_TYPE_MICROPHONE,
+        })
+    public @interface LightType {}
+
+    @NonNull private final Context mContext;
+    @NonNull private final ILightsManager mService;
+
+    /**
+     * Creates a LightsManager.
+     *
+     * @hide
+     */
+    public LightsManager(@NonNull Context context) throws ServiceNotFoundException {
+        this(context, ILightsManager.Stub.asInterface(
+            ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE)));
+    }
+
+    /**
+     * Creates a LightsManager with a provided service implementation.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public LightsManager(@NonNull Context context, @NonNull ILightsManager service) {
+        mContext = Preconditions.checkNotNull(context);
+        mService = Preconditions.checkNotNull(service);
+    }
+
+    /**
+     * Returns the lights available on the device.
+     *
+     * @return A list of available lights
+     */
+    @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+    public @NonNull List<Light> getLights() {
+        try {
+            return mService.getLights();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the state of a specified light.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+    @TestApi
+    public @NonNull LightState getLightState(@NonNull Light light) {
+        Preconditions.checkNotNull(light);
+        try {
+            return mService.getLightState(light.getId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Creates a new LightsSession that can be used to control the device lights.
+     */
+    @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+    public @NonNull LightsSession openSession() {
+        try {
+            final LightsSession session = new LightsSession();
+            mService.openSession(session.mToken);
+            return session;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Encapsulates a session that can be used to control device lights and represents the lifetime
+     * of the requests.
+     */
+    public final class LightsSession implements AutoCloseable {
+
+        private final IBinder mToken = new Binder();
+
+        private final CloseGuard mCloseGuard = new CloseGuard();
+        private boolean mClosed = false;
+
+        /**
+         * Instantiated by {@link LightsManager#openSession()}.
+         */
+        @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+        private LightsSession() {
+            mCloseGuard.open("close");
+        }
+
+        /**
+         * Sends a request to modify the states of multiple lights.
+         *
+         * <p>This method only controls lights that aren't overridden by higher-priority sessions.
+         * Additionally, lights not controlled by this session can be controlled by lower-priority
+         * sessions.
+         *
+         * @param request the settings for lights that should change
+         */
+        @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+        public void setLights(@NonNull LightsRequest request) {
+            Preconditions.checkNotNull(request);
+            if (!mClosed) {
+                try {
+                    mService.setLightStates(mToken, request.mLightIds, request.mLightStates);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+
+        /**
+         * Closes the session, reverting all changes made through it.
+         */
+        @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+        @Override
+        public void close() {
+            if (!mClosed) {
+                try {
+                    mService.closeSession(mToken);
+                    mClosed = true;
+                    mCloseGuard.close();
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+            Reference.reachabilityFence(this);
+        }
+
+        /** @hide */
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                mCloseGuard.warnIfOpen();
+                close();
+            } finally {
+                super.finalize();
+            }
+        }
+    }
+}
diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java
new file mode 100644
index 0000000..a36da4c
--- /dev/null
+++ b/core/java/android/hardware/lights/LightsRequest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 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.hardware.lights;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.util.SparseArray;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Encapsulates a request to modify the state of multiple lights.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class LightsRequest {
+
+    /** Visible to {@link LightsManager.Session}. */
+    final int[] mLightIds;
+
+    /** Visible to {@link LightsManager.Session}. */
+    final LightState[] mLightStates;
+
+    /**
+     * Can only be constructed via {@link LightsRequest.Builder#build()}.
+     */
+    private LightsRequest(SparseArray<LightState> changes) {
+        final int n = changes.size();
+        mLightIds = new int[n];
+        mLightStates = new LightState[n];
+        for (int i = 0; i < n; i++) {
+            mLightIds[i] = changes.keyAt(i);
+            mLightStates[i] = changes.valueAt(i);
+        }
+    }
+
+    /**
+     * Builder for creating device light change requests.
+     */
+    public static final class Builder {
+
+        private final SparseArray<LightState> mChanges = new SparseArray<>();
+
+        /**
+         * Overrides the color and intensity of a given light.
+         *
+         * @param light the light to modify
+         * @param state the desired color and intensity of the light
+         */
+        public @NonNull Builder setLight(@NonNull Light light, @NonNull LightState state) {
+            Preconditions.checkNotNull(light);
+            Preconditions.checkNotNull(state);
+            mChanges.put(light.getId(), state);
+            return this;
+        }
+
+        /**
+         * Removes the override for the color and intensity of a given light.
+         *
+         * @param light the light to modify
+         */
+        public @NonNull Builder clearLight(@NonNull Light light) {
+            Preconditions.checkNotNull(light);
+            mChanges.put(light.getId(), null);
+            return this;
+        }
+
+        /**
+         * Create a LightsRequest object used to override lights on the device.
+         *
+         * <p>The generated {@link LightsRequest} should be used in
+         * {@link LightsManager.Session#setLights(LightsLightsRequest).
+         */
+        public @NonNull LightsRequest build() {
+            return new LightsRequest(mChanges);
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java b/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java
index 6b90588..3164567 100644
--- a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java
+++ b/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java
@@ -60,7 +60,9 @@
     private boolean mParsed = true;
 
     public ActionsModelParamsSupplier(Context context, @Nullable Runnable onChangedListener) {
-        mAppContext = Preconditions.checkNotNull(context).getApplicationContext();
+        final Context appContext = Preconditions.checkNotNull(context).getApplicationContext();
+        // Some contexts don't have an app context.
+        mAppContext = appContext != null ? appContext : context;
         mOnChangedListener = onChangedListener == null ? () -> {} : onChangedListener;
         mSettingsObserver = new SettingsObserver(mAppContext, () -> {
             synchronized (mLock) {
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index aeb99b8..7c527ba 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -21,10 +21,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
+import android.annotation.UserIdInt;
 import android.app.Person;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.SpannedString;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -316,6 +318,8 @@
         private final List<String> mHints;
         @Nullable
         private String mCallingPackageName;
+        @UserIdInt
+        private int mUserId = UserHandle.USER_NULL;
         @NonNull
         private Bundle mExtras;
 
@@ -340,6 +344,7 @@
             List<String> hints = new ArrayList<>();
             in.readStringList(hints);
             String callingPackageName = in.readString();
+            int userId = in.readInt();
             Bundle extras = in.readBundle();
             Request request = new Request(
                     conversation,
@@ -348,6 +353,7 @@
                     hints,
                     extras);
             request.setCallingPackageName(callingPackageName);
+            request.setUserId(userId);
             return request;
         }
 
@@ -358,6 +364,7 @@
             parcel.writeInt(mMaxSuggestions);
             parcel.writeStringList(mHints);
             parcel.writeString(mCallingPackageName);
+            parcel.writeInt(mUserId);
             parcel.writeBundle(mExtras);
         }
 
@@ -428,6 +435,24 @@
         }
 
         /**
+         * Sets the id of the user that sent this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         */
+        void setUserId(@UserIdInt int userId) {
+            mUserId = userId;
+        }
+
+        /**
+         * Returns the id of the user that sent this request.
+         * @hide
+         */
+        @UserIdInt
+        public int getUserId() {
+            return mUserId;
+        }
+
+        /**
          * Returns the extended data related to this request.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index 9ae0c65..ae9d5c6 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -19,8 +19,10 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.view.textclassifier.TextClassifier.EntityType;
 import android.view.textclassifier.TextClassifier.WidgetType;
 
@@ -127,6 +129,7 @@
     private String mWidgetType = TextClassifier.WIDGET_TYPE_UNKNOWN;
     private @InvocationMethod int mInvocationMethod;
     @Nullable private String mWidgetVersion;
+    private @UserIdInt int mUserId = UserHandle.USER_NULL;
     @Nullable private String mResultId;
     private long mEventTime;
     private long mDurationSinceSessionStart;
@@ -171,6 +174,7 @@
         mEnd = in.readInt();
         mSmartStart = in.readInt();
         mSmartEnd = in.readInt();
+        mUserId = in.readInt();
     }
 
     @Override
@@ -199,6 +203,7 @@
         dest.writeInt(mEnd);
         dest.writeInt(mSmartStart);
         dest.writeInt(mSmartEnd);
+        dest.writeInt(mUserId);
     }
 
     @Override
@@ -401,6 +406,24 @@
     }
 
     /**
+     * Sets the id of this event's user.
+     * <p>
+     * Package-private for SystemTextClassifier's use.
+     */
+    void setUserId(@UserIdInt int userId) {
+        mUserId = userId;
+    }
+
+    /**
+     * Returns the id of this event's user.
+     * @hide
+     */
+    @UserIdInt
+    public int getUserId() {
+        return mUserId;
+    }
+
+    /**
      * Returns the type of widget that was involved in triggering this event.
      */
     @WidgetType
@@ -426,6 +449,7 @@
         mPackageName = context.getPackageName();
         mWidgetType = context.getWidgetType();
         mWidgetVersion = context.getWidgetVersion();
+        mUserId = context.getUserId();
     }
 
     /**
@@ -612,7 +636,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
-                mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, mResultId,
+                mWidgetVersion, mPackageName, mUserId, mWidgetType, mInvocationMethod, mResultId,
                 mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
                 mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
     }
@@ -633,6 +657,7 @@
                 && Objects.equals(mEntityType, other.mEntityType)
                 && Objects.equals(mWidgetVersion, other.mWidgetVersion)
                 && Objects.equals(mPackageName, other.mPackageName)
+                && mUserId == other.mUserId
                 && Objects.equals(mWidgetType, other.mWidgetType)
                 && mInvocationMethod == other.mInvocationMethod
                 && Objects.equals(mResultId, other.mResultId)
@@ -652,12 +677,12 @@
         return String.format(Locale.US,
                 "SelectionEvent {absoluteStart=%d, absoluteEnd=%d, eventType=%d, entityType=%s, "
                         + "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, "
-                        + "resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
+                        + "userId=%d, resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
                         + "durationSincePreviousEvent=%d, eventIndex=%d,"
                         + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d}",
                 mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
                 mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod,
-                mResultId, mEventTime, mDurationSinceSessionStart,
+                mUserId, mResultId, mEventTime, mDurationSinceSessionStart,
                 mDurationSincePreviousEvent, mEventIndex,
                 mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
     }
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 8f8766e..a97c330 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.content.Context;
 import android.os.Bundle;
@@ -50,6 +51,10 @@
     private final TextClassificationConstants mSettings;
     private final TextClassifier mFallback;
     private final String mPackageName;
+    // NOTE: Always set this before sending a request to the manager service otherwise the manager
+    // service will throw a remote exception.
+    @UserIdInt
+    private final int mUserId;
     private TextClassificationSessionId mSessionId;
 
     public SystemTextClassifier(Context context, TextClassificationConstants settings)
@@ -60,6 +65,7 @@
         mFallback = context.getSystemService(TextClassificationManager.class)
                 .getTextClassifier(TextClassifier.LOCAL);
         mPackageName = Preconditions.checkNotNull(context.getOpPackageName());
+        mUserId = context.getUserId();
     }
 
     /**
@@ -72,6 +78,7 @@
         Utils.checkMainThread();
         try {
             request.setCallingPackageName(mPackageName);
+            request.setUserId(mUserId);
             final BlockingCallback<TextSelection> callback =
                     new BlockingCallback<>("textselection");
             mManagerService.onSuggestSelection(mSessionId, request, callback);
@@ -95,6 +102,7 @@
         Utils.checkMainThread();
         try {
             request.setCallingPackageName(mPackageName);
+            request.setUserId(mUserId);
             final BlockingCallback<TextClassification> callback =
                     new BlockingCallback<>("textclassification");
             mManagerService.onClassifyText(mSessionId, request, callback);
@@ -123,6 +131,7 @@
 
         try {
             request.setCallingPackageName(mPackageName);
+            request.setUserId(mUserId);
             final BlockingCallback<TextLinks> callback =
                     new BlockingCallback<>("textlinks");
             mManagerService.onGenerateLinks(mSessionId, request, callback);
@@ -142,6 +151,7 @@
         Utils.checkMainThread();
 
         try {
+            event.setUserId(mUserId);
             mManagerService.onSelectionEvent(mSessionId, event);
         } catch (RemoteException e) {
             Log.e(LOG_TAG, "Error reporting selection event.", e);
@@ -154,6 +164,12 @@
         Utils.checkMainThread();
 
         try {
+            final TextClassificationContext tcContext = event.getEventContext() == null
+                    ? new TextClassificationContext.Builder(mPackageName, WIDGET_TYPE_UNKNOWN)
+                            .build()
+                    : event.getEventContext();
+            tcContext.setUserId(mUserId);
+            event.setEventContext(tcContext);
             mManagerService.onTextClassifierEvent(mSessionId, event);
         } catch (RemoteException e) {
             Log.e(LOG_TAG, "Error reporting textclassifier event.", e);
@@ -167,6 +183,7 @@
 
         try {
             request.setCallingPackageName(mPackageName);
+            request.setUserId(mUserId);
             final BlockingCallback<TextLanguage> callback =
                     new BlockingCallback<>("textlanguage");
             mManagerService.onDetectLanguage(mSessionId, request, callback);
@@ -187,6 +204,7 @@
 
         try {
             request.setCallingPackageName(mPackageName);
+            request.setUserId(mUserId);
             final BlockingCallback<ConversationActions> callback =
                     new BlockingCallback<>("conversation-actions");
             mManagerService.onSuggestConversationActions(mSessionId, request, callback);
@@ -228,6 +246,7 @@
         printWriter.printPair("mFallback", mFallback);
         printWriter.printPair("mPackageName", mPackageName);
         printWriter.printPair("mSessionId", mSessionId);
+        printWriter.printPair("mUserId", mUserId);
         printWriter.decreaseIndent();
         printWriter.println();
     }
@@ -243,6 +262,7 @@
             @NonNull TextClassificationSessionId sessionId) {
         mSessionId = Preconditions.checkNotNull(sessionId);
         try {
+            classificationContext.setUserId(mUserId);
             mManagerService.onCreateTextClassificationSession(classificationContext, mSessionId);
         } catch (RemoteException e) {
             Log.e(LOG_TAG, "Error starting a new classification session.", e);
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 6321051..975f3ba 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -21,6 +21,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
 import android.content.Context;
@@ -35,6 +36,7 @@
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.SpannedString;
 import android.util.ArrayMap;
 import android.view.View.OnClickListener;
@@ -551,6 +553,8 @@
         @Nullable private final ZonedDateTime mReferenceTime;
         @NonNull private final Bundle mExtras;
         @Nullable private String mCallingPackageName;
+        @UserIdInt
+        private int mUserId = UserHandle.USER_NULL;
 
         private Request(
                 CharSequence text,
@@ -631,6 +635,24 @@
         }
 
         /**
+         * Sets the id of the user that sent this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         */
+        void setUserId(@UserIdInt int userId) {
+            mUserId = userId;
+        }
+
+        /**
+         * Returns the id of the user that sent this request.
+         * @hide
+         */
+        @UserIdInt
+        public int getUserId() {
+            return mUserId;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
@@ -730,6 +752,7 @@
             dest.writeParcelable(mDefaultLocales, flags);
             dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
             dest.writeString(mCallingPackageName);
+            dest.writeInt(mUserId);
             dest.writeBundle(mExtras);
         }
 
@@ -742,11 +765,13 @@
             final ZonedDateTime referenceTime = referenceTimeString == null
                     ? null : ZonedDateTime.parse(referenceTimeString);
             final String callingPackageName = in.readString();
+            final int userId = in.readInt();
             final Bundle extras = in.readBundle();
 
             final Request request = new Request(text, startIndex, endIndex,
                     defaultLocales, referenceTime, extras);
             request.setCallingPackageName(callingPackageName);
+            request.setUserId(userId);
             return request;
         }
 
diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java
index 3bf8e9b..db07685 100644
--- a/core/java/android/view/textclassifier/TextClassificationContext.java
+++ b/core/java/android/view/textclassifier/TextClassificationContext.java
@@ -18,8 +18,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.view.textclassifier.TextClassifier.WidgetType;
 
 import com.android.internal.util.Preconditions;
@@ -35,6 +37,8 @@
     private final String mPackageName;
     private final String mWidgetType;
     @Nullable private final String mWidgetVersion;
+    @UserIdInt
+    private int mUserId = UserHandle.USER_NULL;
 
     private TextClassificationContext(
             String packageName,
@@ -54,6 +58,24 @@
     }
 
     /**
+     * Sets the id of this context's user.
+     * <p>
+     * Package-private for SystemTextClassifier's use.
+     */
+    void setUserId(@UserIdInt int userId) {
+        mUserId = userId;
+    }
+
+    /**
+     * Returns the id of this context's user.
+     * @hide
+     */
+    @UserIdInt
+    public int getUserId() {
+        return mUserId;
+    }
+
+    /**
      * Returns the widget type for this classification context.
      */
     @NonNull
@@ -75,8 +97,8 @@
     @Override
     public String toString() {
         return String.format(Locale.US, "TextClassificationContext{"
-                + "packageName=%s, widgetType=%s, widgetVersion=%s}",
-                mPackageName, mWidgetType, mWidgetVersion);
+                + "packageName=%s, widgetType=%s, widgetVersion=%s, userId=%d}",
+                mPackageName, mWidgetType, mWidgetVersion, mUserId);
     }
 
     /**
@@ -133,12 +155,14 @@
         parcel.writeString(mPackageName);
         parcel.writeString(mWidgetType);
         parcel.writeString(mWidgetVersion);
+        parcel.writeInt(mUserId);
     }
 
     private TextClassificationContext(Parcel in) {
         mPackageName = in.readString();
         mWidgetType = in.readString();
         mWidgetVersion = in.readString();
+        mUserId = in.readInt();
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationContext> CREATOR =
diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java
index 57da829..a041296 100644
--- a/core/java/android/view/textclassifier/TextClassifierEvent.java
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.java
@@ -139,7 +139,7 @@
     @Nullable
     private final String[] mEntityTypes;
     @Nullable
-    private final TextClassificationContext mEventContext;
+    private TextClassificationContext mEventContext;
     @Nullable
     private final String mResultId;
     private final int mEventIndex;
@@ -289,6 +289,15 @@
     }
 
     /**
+     * Sets the event context.
+     * <p>
+     * Package-private for SystemTextClassifier's use.
+     */
+    void setEventContext(@Nullable TextClassificationContext eventContext) {
+        mEventContext = eventContext;
+    }
+
+    /**
      * Returns the id of the text classifier result related to this event.
      */
     @Nullable
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index 6c75ffb..3e3dc72 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -20,10 +20,12 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.icu.util.ULocale;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -226,6 +228,8 @@
         private final CharSequence mText;
         private final Bundle mExtra;
         @Nullable private String mCallingPackageName;
+        @UserIdInt
+        private int mUserId = UserHandle.USER_NULL;
 
         private Request(CharSequence text, Bundle bundle) {
             mText = text;
@@ -260,6 +264,24 @@
         }
 
         /**
+         * Sets the id of the user that sent this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         */
+        void setUserId(@UserIdInt int userId) {
+            mUserId = userId;
+        }
+
+        /**
+         * Returns the id of the user that sent this request.
+         * @hide
+         */
+        @UserIdInt
+        public int getUserId() {
+            return mUserId;
+        }
+
+        /**
          * Returns a bundle containing non-structured extra information about this request.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
@@ -278,16 +300,19 @@
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeCharSequence(mText);
             dest.writeString(mCallingPackageName);
+            dest.writeInt(mUserId);
             dest.writeBundle(mExtra);
         }
 
         private static Request readFromParcel(Parcel in) {
             final CharSequence text = in.readCharSequence();
             final String callingPackageName = in.readString();
+            final int userId = in.readInt();
             final Bundle extra = in.readBundle();
 
             final Request request = new Request(text, extra);
             request.setCallingPackageName(callingPackageName);
+            request.setUserId(userId);
             return request;
         }
 
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index f3e0dc1..d7ac524 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -20,11 +20,13 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.Spannable;
 import android.text.method.MovementMethod;
 import android.text.style.ClickableSpan;
@@ -339,6 +341,8 @@
         private final boolean mLegacyFallback;
         @Nullable private String mCallingPackageName;
         private final Bundle mExtras;
+        @UserIdInt
+        private int mUserId = UserHandle.USER_NULL;
 
         private Request(
                 CharSequence text,
@@ -410,6 +414,24 @@
         }
 
         /**
+         * Sets the id of the user that sent this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         */
+        void setUserId(@UserIdInt int userId) {
+            mUserId = userId;
+        }
+
+        /**
+         * Returns the id of the user that sent this request.
+         * @hide
+         */
+        @UserIdInt
+        public int getUserId() {
+            return mUserId;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
@@ -509,6 +531,7 @@
             dest.writeParcelable(mDefaultLocales, flags);
             dest.writeParcelable(mEntityConfig, flags);
             dest.writeString(mCallingPackageName);
+            dest.writeInt(mUserId);
             dest.writeBundle(mExtras);
         }
 
@@ -517,11 +540,13 @@
             final LocaleList defaultLocales = in.readParcelable(null);
             final EntityConfig entityConfig = in.readParcelable(null);
             final String callingPackageName = in.readString();
+            final int userId = in.readInt();
             final Bundle extras = in.readBundle();
 
             final Request request = new Request(text, defaultLocales, entityConfig,
                     /* legacyFallback= */ true, extras);
             request.setCallingPackageName(callingPackageName);
+            request.setUserId(userId);
             return request;
         }
 
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 75c27bd..94e0bc3 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -20,10 +20,12 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.SpannedString;
 import android.util.ArrayMap;
 import android.view.textclassifier.TextClassifier.EntityType;
@@ -211,6 +213,8 @@
         private final boolean mDarkLaunchAllowed;
         private final Bundle mExtras;
         @Nullable private String mCallingPackageName;
+        @UserIdInt
+        private int mUserId = UserHandle.USER_NULL;
 
         private Request(
                 CharSequence text,
@@ -292,6 +296,24 @@
         }
 
         /**
+         * Sets the id of the user that sent this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         */
+        void setUserId(@UserIdInt int userId) {
+            mUserId = userId;
+        }
+
+        /**
+         * Returns the id of the user that sent this request.
+         * @hide
+         */
+        @UserIdInt
+        public int getUserId() {
+            return mUserId;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
@@ -394,6 +416,7 @@
             dest.writeInt(mEndIndex);
             dest.writeParcelable(mDefaultLocales, flags);
             dest.writeString(mCallingPackageName);
+            dest.writeInt(mUserId);
             dest.writeBundle(mExtras);
         }
 
@@ -403,11 +426,13 @@
             final int endIndex = in.readInt();
             final LocaleList defaultLocales = in.readParcelable(null);
             final String callingPackageName = in.readString();
+            final int userId = in.readInt();
             final Bundle extras = in.readBundle();
 
             final Request request = new Request(text, startIndex, endIndex, defaultLocales,
                     /* darkLaunchAllowed= */ false, extras);
             request.setCallingPackageName(callingPackageName);
+            request.setUserId(userId);
             return request;
         }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6f697cd..65711db 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -11283,6 +11283,12 @@
     }
 
     @Nullable
+    final TextClassificationManager getTextClassificationManagerForUser() {
+        return getServiceManagerForUser(
+                getContext().getPackageName(), TextClassificationManager.class);
+    }
+
+    @Nullable
     final <T> T getServiceManagerForUser(String packageName, Class<T> managerClazz) {
         if (mTextOperationUser == null) {
             return getContext().getSystemService(managerClazz);
@@ -12383,8 +12389,7 @@
     @NonNull
     public TextClassifier getTextClassifier() {
         if (mTextClassifier == null) {
-            final TextClassificationManager tcm =
-                    mContext.getSystemService(TextClassificationManager.class);
+            final TextClassificationManager tcm = getTextClassificationManagerForUser();
             if (tcm != null) {
                 return tcm.getTextClassifier();
             }
@@ -12400,8 +12405,7 @@
     @NonNull
     TextClassifier getTextClassificationSession() {
         if (mTextClassificationSession == null || mTextClassificationSession.isDestroyed()) {
-            final TextClassificationManager tcm =
-                    mContext.getSystemService(TextClassificationManager.class);
+            final TextClassificationManager tcm = getTextClassificationManagerForUser();
             if (tcm != null) {
                 final String widgetType;
                 if (isTextEditable()) {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 0aa0c5a..5b80af5 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -250,9 +250,11 @@
 static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
 
 // Feature flag name for running the JIT in Zygote experiment, b/119800099.
-static const char* ENABLE_APEX_IMAGE = "enable_apex_image";
-// Flag to pass to the runtime when using the apex image.
-static const char* kApexImageOption = "-Ximage:/system/framework/apex.art";
+// TODO: Rename the server-level flag or remove.
+static const char* ENABLE_JITZYGOTE_IMAGE = "enable_apex_image";
+// Flag to pass to the runtime when using the JIT Zygote image.
+static const char* kJitZygoteImageOption =
+        "-Ximage:boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof";
 
 // Feature flag name for disabling lock profiling.
 static const char* DISABLE_LOCK_PROFILING = "disable_lock_profiling";
@@ -730,16 +732,16 @@
         addOption("-Xjitsaveprofilinginfo");
     }
 
-    std::string use_apex_image_flag =
-        server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
-                                                             ENABLE_APEX_IMAGE,
-                                                             /*default_value=*/ "");
+    std::string use_jitzygote_image_flag =
+            server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
+                                                                 ENABLE_JITZYGOTE_IMAGE,
+                                                                 /*default_value=*/"");
     // Use the APEX boot image for boot class path profiling to get JIT samples on BCP methods.
     // Also use the APEX boot image if it's explicitly enabled via configuration flag.
-    const bool use_apex_image = profile_boot_class_path || (use_apex_image_flag == "true");
+    const bool use_apex_image = profile_boot_class_path || (use_jitzygote_image_flag == "true");
     if (use_apex_image) {
-        addOption(kApexImageOption);
-        ALOGI("Using Apex boot image: '%s'\n", kApexImageOption);
+        ALOGI("Using JIT Zygote image: '%s'\n", kJitZygoteImageOption);
+        addOption(kJitZygoteImageOption);
     } else if (parseRuntimeOption("dalvik.vm.boot-image", bootImageBuf, "-Ximage:")) {
         ALOGI("Using dalvik.vm.boot-image: '%s'\n", bootImageBuf);
     } else {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e7539ba..f482a7f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3647,6 +3647,13 @@
     <permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE"
         android:protectionLevel="signature" />
 
+    <!-- Allows an application to control the lights on the device.
+         @hide
+         @SystemApi
+         @TestApi -->
+    <permission android:name="android.permission.CONTROL_DEVICE_LIGHTS"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Allows an application to control the color saturation of the display.
          @hide
          @SystemApi -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 204d827..0541955 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3528,8 +3528,8 @@
     <!-- True if home app should be pinned via Pinner Service -->
     <bool name="config_pinnerHomeApp">false</bool>
 
-    <!-- List of files pinned by the Pinner Service with the apex boot image b/119800099 -->
-    <string-array translatable="false" name="config_apexBootImagePinnerServiceFiles">
+    <!-- List of files pinned by the Pinner Service with the JIT Zygote boot image b/119800099 -->
+    <string-array translatable="false" name="config_jitzygoteBootImagePinnerServiceFiles">
     </string-array>
 
     <!-- Number of days preloaded file cache should be preserved on a device before it can be
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 76d5715..d1e449e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3117,7 +3117,7 @@
   <java-symbol type="array" name="config_defaultPinnerServiceFiles" />
   <java-symbol type="bool" name="config_pinnerCameraApp" />
   <java-symbol type="bool" name="config_pinnerHomeApp" />
-  <java-symbol type="array" name="config_apexBootImagePinnerServiceFiles" />
+  <java-symbol type="array" name="config_jitzygoteBootImagePinnerServiceFiles" />
 
   <java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
 
diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
index 49fb75b..b8dbfd3 100644
--- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
@@ -466,179 +466,7 @@
             // expected
         }
     }
-    
-    @MediumTest
-    public void testTokenize() throws Exception {
-        Cursor c;
-        mDatabase.execSQL("CREATE TABLE tokens (" +
-                "token TEXT COLLATE unicode," +
-                "source INTEGER," +
-                "token_index INTEGER," +
-                "tag TEXT" +
-                ");");
-        mDatabase.execSQL("CREATE TABLE tokens_no_index (" +
-                "token TEXT COLLATE unicode," +
-                "source INTEGER" +
-                ");");
-        
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE(NULL, NULL, NULL, NULL)", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT _TOKENIZE('tokens', NULL, NULL, NULL)", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 10, NULL, NULL)", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 10, 'some string', NULL)", null));
-     
-        Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 11, 'some string ok', ' ', 1, 'foo')", null));
-        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT _TOKENIZE('tokens', 11, 'second field', ' ', 1, 'bar')", null));
 
-        Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT _TOKENIZE('tokens_no_index', 20, 'some string ok', ' ')", null));
-        Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT _TOKENIZE('tokens_no_index', 21, 'foo bar baz', ' ', 0)", null));
-
-        // test Chinese
-        String chinese = new String("\u4eac\u4ec5 \u5c3d\u5f84\u60ca"); 
-        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 12,'" + chinese + "', ' ', 1)", null));
-        
-        String icustr = new String("Fr\u00e9d\u00e9ric Hj\u00f8nnev\u00e5g");
-        
-        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 13, '" + icustr + "', ' ', 1)", null));
-        
-        Assert.assertEquals(9, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens;", null));      
-
-        String key = DatabaseUtils.getHexCollationKey("Frederic Hjonneva");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));      
-        Assert.assertEquals(13, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        key = DatabaseUtils.getHexCollationKey("Hjonneva");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(13, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        
-        key = DatabaseUtils.getHexCollationKey("some string ok");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-        key = DatabaseUtils.getHexCollationKey("string");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-        key = DatabaseUtils.getHexCollationKey("ok");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-
-        key = DatabaseUtils.getHexCollationKey("second field");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("bar", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-        key = DatabaseUtils.getHexCollationKey("field");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("bar", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-
-        key = DatabaseUtils.getHexCollationKey(chinese);
-        String[] a = new String[1];
-        a[0] = key;
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token= ?", a));
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token= ?", a));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token= ?", a));
-        a[0] += "*";
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-             "SELECT count(*) from tokens where token GLOB ?", a));        
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB ?", a));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB ?", a));
-
-       Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token= '" + key + "'", null));
-       Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-               "SELECT source from tokens where token= '" + key + "'", null));
-       Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-               "SELECT token_index from tokens where token= '" + key + "'", null));
-        
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));        
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        
-        key = DatabaseUtils.getHexCollationKey("\u4eac\u4ec5");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        
-        key = DatabaseUtils.getHexCollationKey("\u5c3d\u5f84\u60ca");
-        Log.d("DatabaseGeneralTest", "key = " + key);
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB 'ab*'", null));        
-
-        key = DatabaseUtils.getHexCollationKey("some string ok");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens_no_index where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(20, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens_no_index where token GLOB '" + key + "*'", null));
-
-        key = DatabaseUtils.getHexCollationKey("bar");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens_no_index where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(21, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens_no_index where token GLOB '" + key + "*'", null));
-    }
-    
     @MediumTest
     public void testTransactions() throws Exception {
         mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index b417927..aea7f33 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1345,7 +1345,9 @@
     private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private boolean mHasThumbnail;
-    // The following values used for indicating a thumbnail position.
+    private boolean mHasThumbnailStrips;
+    private boolean mAreThumbnailStripsConsecutive;
+    // Used to indicate the position of the thumbnail (includes offset to EXIF data segment).
     private int mThumbnailOffset;
     private int mThumbnailLength;
     private byte[] mThumbnailBytes;
@@ -2043,10 +2045,12 @@
 
     /**
      * Returns the offset and length of thumbnail inside the image file, or
-     * {@code null} if there is no thumbnail.
+     * {@code null} if either there is no thumbnail or the thumbnail bytes are stored
+     * non-consecutively.
      *
      * @return two-element array, the offset in the first value, and length in
-     *         the second, or {@code null} if no thumbnail was found.
+     *         the second, or {@code null} if no thumbnail was found or the thumbnail strips are
+     *         not placed consecutively.
      * @throws IllegalStateException if {@link #saveAttributes()} has been
      *             called since the underlying file was initially parsed, since
      *             that means offsets may have changed.
@@ -2058,10 +2062,12 @@
         }
 
         if (mHasThumbnail) {
+            if (mHasThumbnailStrips && !mAreThumbnailStripsConsecutive) {
+                return null;
+            }
             return new long[] { mThumbnailOffset, mThumbnailLength };
-        } else {
-            return null;
         }
+        return null;
     }
 
     /**
@@ -2536,10 +2542,9 @@
                         final byte[] value = Arrays.copyOfRange(bytes,
                                 IDENTIFIER_EXIF_APP1.length, bytes.length);
 
-                        readExifSegment(value, imageType);
-
                         // Save offset values for createJpegThumbnailBitmap() function
                         mExifOffset = (int) offset;
+                        readExifSegment(value, imageType);
                     } else if (ArrayUtils.startsWith(bytes, IDENTIFIER_XMP_APP1)) {
                         // See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6
                         final long offset = start + IDENTIFIER_XMP_APP1.length;
@@ -2843,6 +2848,8 @@
                 if (in.read(bytes) != length) {
                     throw new IOException("Can't read exif");
                 }
+                // Save offset values for handling thumbnail and attribute offsets.
+                mExifOffset = offset;
                 readExifSegment(bytes, IFD_TYPE_PRIMARY);
             }
 
@@ -2988,7 +2995,7 @@
         // Write EXIF APP1 segment
         dataOutputStream.writeByte(MARKER);
         dataOutputStream.writeByte(MARKER_APP1);
-        writeExifSegment(dataOutputStream, 6);
+        writeExifSegment(dataOutputStream);
 
         byte[] bytes = new byte[4096];
 
@@ -3319,7 +3326,7 @@
                 continue;
             }
 
-            final int bytesOffset = dataInputStream.peek();
+            final int bytesOffset = dataInputStream.peek() + mExifOffset;
             final byte[] bytes = new byte[(int) byteCount];
             dataInputStream.readFully(bytes);
             ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents,
@@ -3451,31 +3458,28 @@
 
             // The following code limits the size of thumbnail size not to overflow EXIF data area.
             thumbnailLength = Math.min(thumbnailLength, in.getLength() - thumbnailOffset);
-            if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF
-                    || mMimeType == IMAGE_TYPE_RW2) {
-                thumbnailOffset += mExifOffset;
-            } else if (mMimeType == IMAGE_TYPE_ORF) {
+            if (mMimeType == IMAGE_TYPE_ORF) {
                 // Update offset value since RAF files have IFD data preceding MakerNote data.
                 thumbnailOffset += mOrfMakerNoteOffset;
             }
-            if (DEBUG) {
-                Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset
-                        + ", length: " + thumbnailLength);
-            }
             if (thumbnailOffset > 0 && thumbnailLength > 0) {
                 mHasThumbnail = true;
-                mThumbnailOffset = thumbnailOffset;
+                mThumbnailOffset = thumbnailOffset + mExifOffset;
                 mThumbnailLength = thumbnailLength;
                 mThumbnailCompression = DATA_JPEG;
 
                 if (mFilename == null && mAssetInputStream == null
                         && mSeekableFileDescriptor == null) {
                     // Save the thumbnail in memory if the input doesn't support reading again.
-                    byte[] thumbnailBytes = new byte[thumbnailLength];
-                    in.seek(thumbnailOffset);
+                    byte[] thumbnailBytes = new byte[mThumbnailLength];
+                    in.seek(mThumbnailOffset);
                     in.readFully(thumbnailBytes);
                     mThumbnailBytes = thumbnailBytes;
                 }
+                if (DEBUG) {
+                    Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset
+                            + ", length: " + thumbnailLength);
+                }
             }
         }
     }
@@ -3494,12 +3498,16 @@
             long[] stripByteCounts =
                     convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder));
 
-            if (stripOffsets == null) {
-                Log.w(TAG, "stripOffsets should not be null.");
+            if (stripOffsets == null || stripOffsets.length == 0) {
+                Log.w(TAG, "stripOffsets should not be null or have zero length.");
                 return;
             }
-            if (stripByteCounts == null) {
-                Log.w(TAG, "stripByteCounts should not be null.");
+            if (stripByteCounts == null || stripByteCounts.length == 0) {
+                Log.w(TAG, "stripByteCounts should not be null or have zero length.");
+                return;
+            }
+            if (stripOffsets.length != stripByteCounts.length) {
+                Log.w(TAG, "stripOffsets and stripByteCounts should have same length.");
                 return;
             }
 
@@ -3509,10 +3517,18 @@
 
             int bytesRead = 0;
             int bytesAdded = 0;
+            mHasThumbnail = mHasThumbnailStrips = mAreThumbnailStripsConsecutive = true;
             for (int i = 0; i < stripOffsets.length; i++) {
                 int stripOffset = (int) stripOffsets[i];
                 int stripByteCount = (int) stripByteCounts[i];
 
+                // Check if strips are consecutive
+                // TODO: Add test for non-consecutive thumbnail image
+                if (i < stripOffsets.length - 1
+                        && stripOffset + stripByteCount != stripOffsets[i + 1]) {
+                    mAreThumbnailStripsConsecutive = false;
+                }
+
                 // Skip to offset
                 int skipBytes = stripOffset - bytesRead;
                 if (skipBytes < 0) {
@@ -3531,10 +3547,13 @@
                         stripBytes.length);
                 bytesAdded += stripBytes.length;
             }
-
-            mHasThumbnail = true;
             mThumbnailBytes = totalStripBytes;
-            mThumbnailLength = totalStripBytes.length;
+
+            if (mAreThumbnailStripsConsecutive) {
+                // Need to add mExifOffset, which is the offset to the EXIF data segment
+                mThumbnailOffset = (int) stripOffsets[0] + mExifOffset;
+                mThumbnailLength = totalStripBytes.length;
+            }
         }
     }
 
@@ -3691,8 +3710,7 @@
     }
 
     // Writes an Exif segment into the given output stream.
-    private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream,
-            int exifOffsetFromBeginning) throws IOException {
+    private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream) throws IOException {
         // The following variables are for calculating each IFD tag group size in bytes.
         int[] ifdOffsets = new int[EXIF_TAGS.length];
         int[] ifdDataSizes = new int[EXIF_TAGS.length];
@@ -3751,6 +3769,8 @@
         }
 
         // Calculate IFD offsets.
+        // 8 bytes are for TIFF headers: 2 bytes (byte order) + 2 bytes (identifier) + 4 bytes
+        // (offset of IFDs)
         int position = 8;
         for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
             if (!mAttributes[ifdType].isEmpty()) {
@@ -3762,7 +3782,8 @@
             int thumbnailOffset = position;
             mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
                     ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
-            mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
+            // Need to add mExifOffset, which is the offset to the EXIF data segment
+            mThumbnailOffset = thumbnailOffset + mExifOffset;
             position += mThumbnailLength;
         }
 
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 8c2ff2c..2461cb4 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -546,6 +546,60 @@
     <string name="enable_adb_summary">Debug mode when USB is connected</string>
     <!-- Setting title to revoke secure USB debugging authorizations -->
     <string name="clear_adb_keys">Revoke USB debugging authorizations</string>
+    <!-- [CHAR LIMIT=32] Setting title for ADB wireless switch -->
+    <string name="enable_adb_wireless">Wireless debugging</string>
+    <!-- [CHAR LIMIT=NONE] Setting checkbox summary for whether to enable Wireless debugging support on the phone -->
+    <string name="enable_adb_wireless_summary">Debug mode when Wi\u2011Fi is connected</string>
+    <!-- [CHAR LIMIT=32] Summary text when ADB wireless has error -->
+    <string name="adb_wireless_error">Error</string>
+    <!-- [CHAR LIMIT=32] Setting title for ADB wireless fragment -->
+    <string name="adb_wireless_settings">Wireless debugging</string>
+    <!-- [CHAR LIMIT=NONE] Wireless debugging settings. text displayed when wireless debugging is off and network list is empty. -->
+    <string name="adb_wireless_list_empty_off">To see and use available devices, turn on wireless debugging</string>
+    <!-- [CHAR LIMIT=50] Title for adb wireless pair by QR code preference -->
+    <string name="adb_pair_method_qrcode_title">Pair device with QR code</string>
+    <!-- [CHAR LIMIT=NONE] Summary for adb wireless pair by QR code preference -->
+    <string name="adb_pair_method_qrcode_summary">Pair new devices using QR code Scanner</string>
+    <!-- [CHAR LIMIT=50] Title for adb wireless pair by pairing code preference -->
+    <string name="adb_pair_method_code_title">Pair device with pairing code</string>
+    <!-- [CHAR LIMIT=NONE] Summary for adb wireless pair by pairing code preference -->
+    <string name="adb_pair_method_code_summary">Pair new devices using six digit code</string>
+    <!-- [CHAR LIMIT=50] Title for adb wireless paired devices category -->
+    <string name="adb_paired_devices_title">Paired devices</string>
+    <!-- [CHAR LIMIT=50] Summary for adb wireless paired device preference -->
+    <string name="adb_wireless_device_connected_summary">Currently connected</string>
+    <!-- [CHAR LIMIT=50] Title for the adb device details fragment -->
+    <string name="adb_wireless_device_details_title">Device details</string>
+    <!-- [CHAR LIMIT=16] Button label to forget an adb device -->
+    <string name="adb_device_forget">Forget</string>
+    <!-- [CHAR LIMIT=50] Title format for mac address preference in adb device details fragment -->
+    <string name="adb_device_fingerprint_title_format">Device fingerprint: <xliff:g id="fingerprint_param" example="a1:b2:c3:d4:e5:f6">%1$s</xliff:g></string>
+    <!-- [CHAR LIMIT=50] Title for adb wireless connection failed dialog -->
+    <string name="adb_wireless_connection_failed_title">Connection unsuccessful</string>
+    <!-- [CHAR LIMIT=NONE] Message for adb wireless connection failed dialog -->
+    <string name="adb_wireless_connection_failed_message">Make sure <xliff:g id="device_name" example="Bob's Macbook">%1$s</xliff:g> is connected to the correct network</string>
+    <!-- [CHAR LIMIT=32] Adb wireless pairing device dialog title -->
+    <string name="adb_pairing_device_dialog_title">Pair with device</string>
+    <!-- [CHAR LIMIT=32] Adb wireless pairing device dialog pairing code label -->
+    <string name="adb_pairing_device_dialog_pairing_code_label">Wi\u2011Fi pairing code</string>
+    <!-- [CHAR LIMIT=50] Adb Wireless pairing device failed dialog title -->
+    <string name="adb_pairing_device_dialog_failed_title">Pairing unsuccessful</string>
+    <!-- [CHAR LIMIT=NONE] Adb wireless pairing device failed dialog message -->
+    <string name="adb_pairing_device_dialog_failed_msg">Make sure the device is connected to the same network.</string>
+    <!-- [CHAR LIMIT=NONE] Adb wireless qr code scanner description -->
+    <string name="adb_wireless_qrcode_summary">Pair device over Wi\u2011Fi by scanning a QR code</string>
+    <!-- [CHAR LIMIT=NONE] Adb wireless QR code pairing in progress text -->
+    <string name="adb_wireless_verifying_qrcode_text">Pairing device\u2026</string>
+    <!-- [CHAR LIMIT=NONE] Adb wireless QR code failed message -->
+    <string name="adb_qrcode_pairing_device_failed_msg">Failed to pair the device. Either the QR code was incorrect, or the device is not connected to the same network.</string>
+    <!-- [CHAR LIMIT=50] Adb Wireless ip address and port title -->
+    <string name="adb_wireless_ip_addr_preference_title">IP address \u0026 Port</string>
+    <!-- [CHAR LIMIT=NONE] Adb Wireless QR code pairing scanner title -->
+    <string name="adb_wireless_qrcode_pairing_title">Scan QR code</string>
+    <!-- [CHAR LIMIT=NONE] Adb Wireless QR code pairing description -->
+    <string name="adb_wireless_qrcode_pairing_description">Pair device over Wi\u2011Fi by scanning a QR Code</string>
+    <!--Adb wireless search Keywords [CHAR LIMIT=NONE]-->
+    <string name="keywords_adb_wireless">adb, debug, dev</string>
     <!-- [CHAR LIMIT=NONE] Setting checkbox title for Whether to include bug report item in power menu. -->
     <string name="bugreport_in_power">Bug report shortcut</string>
     <!-- [CHAR LIMIT=NONE] Setting checkbox summary for Whether to include bug report item in power -->
@@ -677,6 +731,10 @@
     <string name="adb_warning_title">Allow USB debugging?</string>
     <!-- Warning text to user about the implications of enabling USB debugging -->
     <string name="adb_warning_message">USB debugging is intended for development purposes only. Use it to copy data between your computer and your device, install apps on your device without notification, and read log data.</string>
+    <!-- Title of warning dialog about the implications of enabling USB debugging [CHAR LIMIT=NONE] -->
+    <string name="adbwifi_warning_title">Allow wireless debugging?</string>
+    <!-- Warning text to user about the implications of enabling USB debugging [CHAR LIMIT=NONE] -->
+    <string name="adbwifi_warning_message">Wireless debugging is intended for development purposes only. Use it to copy data between your computer and your device, install apps on your device without notification, and read log data.</string>
     <!-- Message of dialog confirming that user wants to revoke access to adb from all computers they have authorized -->
     <string name="adb_keys_warning_message">Revoke access to USB debugging from all computers you\u2019ve previously authorized?</string>
     <!-- Title of warning dialog about the implications of enabling developer settings -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index a37367e..da0f83d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -89,7 +89,6 @@
     private NotificationRowBinder mNotificationRowBinder;
 
     private NotificationPresenter mPresenter;
-    private NotificationListenerService.RankingMap mLatestRankingMap;
     @VisibleForTesting
     protected NotificationData mNotificationData;
 
@@ -168,8 +167,7 @@
     /** Adds a {@link NotificationLifetimeExtender}. */
     public void addNotificationLifetimeExtender(NotificationLifetimeExtender extender) {
         mNotificationLifetimeExtenders.add(extender);
-        extender.setCallback(key -> removeNotification(key, mLatestRankingMap,
-                UNDEFINED_DISMISS_REASON));
+        extender.setCallback(key -> removeNotification(key, null, UNDEFINED_DISMISS_REASON));
     }
 
     public NotificationData getNotificationData() {
@@ -307,7 +305,6 @@
             if (!forceRemove && !entryDismissed) {
                 for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
                     if (extender.shouldExtendLifetime(entry)) {
-                        mLatestRankingMap = ranking;
                         extendLifetime(entry, extender);
                         lifetimeExtended = true;
                         break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
index 0009292..4ad7487 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -201,6 +201,9 @@
             removed = mEntries.remove(key);
         }
         if (removed == null) return null;
+        // NEM may pass us a null ranking map if removing a lifetime-extended notification,
+        // so use the most recent ranking
+        if (ranking == null) ranking = mRankingMap;
         mGroupManager.onEntryRemoved(removed);
         updateRankingAndSort(ranking);
         return removed;
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index cf2b1f06..4efe934 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -28,6 +28,7 @@
         "netd_aidl_interface-unstable-java",
         "netlink-client",
         "networkstack-aidl-interfaces-unstable-java",
+        "android.hardware.tetheroffload.config-V1.0-java",
         "android.hardware.tetheroffload.control-V1.0-java",
         "net-utils-framework-common",
     ],
@@ -48,26 +49,26 @@
 // Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK).
 cc_library {
     name: "libtetherutilsjni",
+    sdk_version: "current",
     srcs: [
         "jni/android_net_util_TetheringUtils.cpp",
     ],
     shared_libs: [
-        "libcgrouprc",
-        "libnativehelper_compat_libc++",
-        "libvndksupport",
-    ],
-    static_libs: [
-        "android.hardware.tetheroffload.config@1.0",
         "liblog",
-        "libbase",
-        "libbinderthreadstate",
-        "libcutils",
-        "libhidlbase",
-        "libjsoncpp",
-        "libprocessgroup",
-        "libutils",
+        "libnativehelper_compat_libc++",
     ],
 
+    // We cannot use plain "libc++" here to link libc++ dynamically because it results in:
+    //   java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found
+    // even if "libc++" is added into jni_libs below. Adding "libc++_shared" into jni_libs doesn't
+    // build because soong complains of:
+    //   module Tethering missing dependencies: libc++_shared
+    //
+    // So, link libc++ statically. This means that we also need to ensure that all the C++ libraries
+    // we depend on do not dynamically link libc++. This is currently the case, because liblog is
+    // C-only and libnativehelper_compat_libc also uses stl: "c++_static".
+    stl: "c++_static",
+
     cflags: [
         "-Wall",
         "-Werror",
@@ -86,9 +87,8 @@
     // Build system doesn't track transitive dependeicies for jni_libs, list all the dependencies
     // explicitly.
     jni_libs: [
-        "libcgrouprc",
+        "liblog",
         "libnativehelper_compat_libc++",
-        "libvndksupport",
         "libtetherutilsjni",
     ],
     resource_dirs: [
diff --git a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
index 1cf8f98..54934406 100644
--- a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
+++ b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
@@ -16,123 +16,18 @@
 
 #include <errno.h>
 #include <error.h>
-#include <hidl/HidlSupport.h>
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
-#include <linux/netfilter/nfnetlink.h>
-#include <linux/netlink.h>
 #include <net/if.h>
 #include <netinet/icmp6.h>
 #include <sys/socket.h>
-#include <android-base/unique_fd.h>
-#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
 
 #define LOG_TAG "TetheringUtils"
-#include <utils/Log.h>
+#include <android/log.h>
 
 namespace android {
 
-using hardware::hidl_handle;
-using hardware::hidl_string;
-using hardware::tetheroffload::config::V1_0::IOffloadConfig;
-
-namespace {
-
-inline const sockaddr * asSockaddr(const sockaddr_nl *nladdr) {
-    return reinterpret_cast<const sockaddr *>(nladdr);
-}
-
-int conntrackSocket(unsigned groups) {
-    base::unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
-    if (s.get() < 0) return -errno;
-
-    const struct sockaddr_nl bind_addr = {
-        .nl_family = AF_NETLINK,
-        .nl_pad = 0,
-        .nl_pid = 0,
-        .nl_groups = groups,
-    };
-    if (bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) {
-        return -errno;
-    }
-
-    const struct sockaddr_nl kernel_addr = {
-        .nl_family = AF_NETLINK,
-        .nl_pad = 0,
-        .nl_pid = 0,
-        .nl_groups = groups,
-    };
-    if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
-        return -errno;
-    }
-
-    return s.release();
-}
-
-// Return a hidl_handle that owns the file descriptor owned by fd, and will
-// auto-close it (otherwise there would be double-close problems).
-//
-// Rely upon the compiler to eliminate the constexprs used for clarity.
-hidl_handle handleFromFileDescriptor(base::unique_fd fd) {
-    hidl_handle h;
-
-    static constexpr int kNumFds = 1;
-    static constexpr int kNumInts = 0;
-    native_handle_t *nh = native_handle_create(kNumFds, kNumInts);
-    nh->data[0] = fd.release();
-
-    static constexpr bool kTakeOwnership = true;
-    h.setTo(nh, kTakeOwnership);
-
-    return h;
-}
-
-}  // namespace
-
-static jboolean android_net_util_configOffload(
-        JNIEnv* /* env */) {
-    sp<IOffloadConfig> configInterface = IOffloadConfig::getService();
-    if (configInterface.get() == nullptr) {
-        ALOGD("Could not find IOffloadConfig service.");
-        return false;
-    }
-
-    // Per the IConfigOffload definition:
-    //
-    // fd1   A file descriptor bound to the following netlink groups
-    //       (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
-    //
-    // fd2   A file descriptor bound to the following netlink groups
-    //       (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
-    base::unique_fd
-            fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY)),
-            fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY));
-    if (fd1.get() < 0 || fd2.get() < 0) {
-        ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
-        return false;
-    }
-
-    hidl_handle h1(handleFromFileDescriptor(std::move(fd1))),
-                h2(handleFromFileDescriptor(std::move(fd2)));
-
-    bool rval(false);
-    hidl_string msg;
-    const auto status = configInterface->setHandles(h1, h2,
-            [&rval, &msg](bool success, const hidl_string& errMsg) {
-                rval = success;
-                msg = errMsg;
-            });
-    if (!status.isOk() || !rval) {
-        ALOGE("IOffloadConfig::setHandles() error: '%s' / '%s'",
-              status.description().c_str(), msg.c_str());
-        // If status is somehow not ok, make sure rval captures this too.
-        rval = false;
-    }
-
-    return rval;
-}
-
 static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
         jint ifIndex)
 {
@@ -229,7 +124,6 @@
  */
 static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
-    { "configOffload", "()Z", (void*) android_net_util_configOffload },
     { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket },
 };
 
@@ -242,7 +136,7 @@
 extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
     JNIEnv *env;
     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-        ALOGE("ERROR: GetEnv failed");
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed");
         return JNI_ERR;
     }
 
diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java
index fa543bd..5a6d5c1 100644
--- a/packages/Tethering/src/android/net/util/TetheringUtils.java
+++ b/packages/Tethering/src/android/net/util/TetheringUtils.java
@@ -24,14 +24,6 @@
  * {@hide}
  */
 public class TetheringUtils {
-
-    /**
-     * Offload management process need to know conntrack rules to support NAT, but it may not have
-     * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and
-     * share them with offload management process.
-     */
-    public static native boolean configOffload();
-
     /**
      * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
      * @param fd the socket's {@link FileDescriptor}.
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 90b9d3f..b545717 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -18,19 +18,28 @@
 
 import static android.net.util.TetheringUtils.uint16;
 
+import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
 import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
 import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
 import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
 import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
 import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
+import android.net.netlink.NetlinkSocket;
 import android.net.util.SharedLog;
-import android.net.util.TetheringUtils;
+import android.net.util.SocketUtils;
 import android.os.Handler;
+import android.os.NativeHandle;
 import android.os.RemoteException;
+import android.system.ErrnoException;
+import android.system.Os;
 import android.system.OsConstants;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.net.SocketException;
 import java.util.ArrayList;
 
 
@@ -49,6 +58,10 @@
     private static final String NO_INTERFACE_NAME = "";
     private static final String NO_IPV4_ADDRESS = "";
     private static final String NO_IPV4_GATEWAY = "";
+    // Reference kernel/uapi/linux/netfilter/nfnetlink_compat.h
+    private static final int NF_NETLINK_CONNTRACK_NEW = 1;
+    private static final int NF_NETLINK_CONNTRACK_UPDATE = 2;
+    private static final int NF_NETLINK_CONNTRACK_DESTROY = 4;
 
     private final Handler mHandler;
     private final SharedLog mLog;
@@ -121,9 +134,103 @@
         return DEFAULT_TETHER_OFFLOAD_DISABLED;
     }
 
-    /** Configure offload management process. */
+    /**
+     * Offload management process need to know conntrack rules to support NAT, but it may not have
+     * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and
+     * share them with offload management process.
+     */
     public boolean initOffloadConfig() {
-        return TetheringUtils.configOffload();
+        IOffloadConfig offloadConfig;
+        try {
+            offloadConfig = IOffloadConfig.getService();
+        } catch (RemoteException e) {
+            mLog.e("getIOffloadConfig error " + e);
+            return false;
+        }
+        if (offloadConfig == null) {
+            mLog.e("Could not find IOffloadConfig service");
+            return false;
+        }
+        // Per the IConfigOffload definition:
+        //
+        // h1    provides a file descriptor bound to the following netlink groups
+        //       (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
+        //
+        // h2    provides a file descriptor bound to the following netlink groups
+        //       (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
+        final NativeHandle h1 = createConntrackSocket(
+                NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
+        if (h1 == null) return false;
+
+        final NativeHandle h2 = createConntrackSocket(
+                NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
+        if (h2 == null) {
+            closeFdInNativeHandle(h1);
+            return false;
+        }
+
+        final CbResults results = new CbResults();
+        try {
+            offloadConfig.setHandles(h1, h2,
+                    (boolean success, String errMsg) -> {
+                        results.mSuccess = success;
+                        results.mErrMsg = errMsg;
+                    });
+        } catch (RemoteException e) {
+            record("initOffloadConfig, setHandles fail", e);
+            return false;
+        }
+        // Explicitly close FDs.
+        closeFdInNativeHandle(h1);
+        closeFdInNativeHandle(h2);
+
+        record("initOffloadConfig, setHandles results:", results);
+        return results.mSuccess;
+    }
+
+    private void closeFdInNativeHandle(final NativeHandle h) {
+        try {
+            h.close();
+        } catch (IOException | IllegalStateException e) {
+            // IllegalStateException means fd is already closed, do nothing here.
+            // Also nothing we can do if IOException.
+        }
+    }
+
+    private NativeHandle createConntrackSocket(final int groups) {
+        FileDescriptor fd;
+        try {
+            fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
+        } catch (ErrnoException e) {
+            mLog.e("Unable to create conntrack socket " + e);
+            return null;
+        }
+
+        final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
+        try {
+            Os.bind(fd, sockAddr);
+        } catch (ErrnoException | SocketException e) {
+            mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
+            try {
+                SocketUtils.closeSocket(fd);
+            } catch (IOException ie) {
+                // Nothing we can do here
+            }
+            return null;
+        }
+        try {
+            Os.connect(fd, sockAddr);
+        } catch (ErrnoException | SocketException e) {
+            mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
+            try {
+                SocketUtils.closeSocket(fd);
+            } catch (IOException ie) {
+                // Nothing we can do here
+            }
+            return null;
+        }
+
+        return new NativeHandle(fd, true);
     }
 
     /** Initialize the tethering offload HAL. */
diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java
index c67fe9f..1e8109c 100644
--- a/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java
+++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java
@@ -23,6 +23,8 @@
 public class PacNative {
     private static final String TAG = "PacProxy";
 
+    private static final PacNative sInstance = new PacNative();
+
     private String mCurrentPac;
 
     private boolean mIsActive;
@@ -39,10 +41,14 @@
         System.loadLibrary("jni_pacprocessor");
     }
 
-    PacNative() {
+    private PacNative() {
 
     }
 
+    public static PacNative getInstance() {
+        return sInstance;
+    }
+
     public synchronized boolean startPacSupport() {
         if (createV8ParserNativeLocked()) {
             Log.e(TAG, "Unable to Create v8 Proxy Parser.");
diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
index 74391eb..b006d6e 100644
--- a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
+++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
@@ -31,43 +31,27 @@
 public class PacService extends Service {
     private static final String TAG = "PacService";
 
-    private PacNative mPacNative;
-    private ProxyServiceStub mStub;
+    private PacNative mPacNative = PacNative.getInstance();
+    private ProxyServiceStub mStub = new ProxyServiceStub();
 
     @Override
     public void onCreate() {
         super.onCreate();
-        if (mPacNative == null) {
-            mPacNative = new PacNative();
-            mStub = new ProxyServiceStub(mPacNative);
-        }
+        mPacNative.startPacSupport();
     }
 
     @Override
     public void onDestroy() {
+        mPacNative.stopPacSupport();
         super.onDestroy();
-        if (mPacNative != null) {
-            mPacNative.stopPacSupport();
-            mPacNative = null;
-            mStub = null;
-        }
     }
 
     @Override
     public IBinder onBind(Intent intent) {
-        if (mPacNative == null) {
-            mPacNative = new PacNative();
-            mStub = new ProxyServiceStub(mPacNative);
-        }
         return mStub;
     }
 
-    private static class ProxyServiceStub extends IProxyService.Stub {
-        private final PacNative mPacNative;
-
-        public ProxyServiceStub(PacNative pacNative) {
-            mPacNative = pacNative;
-        }
+    private class ProxyServiceStub extends IProxyService.Stub {
 
         @Override
         public String resolvePacFile(String host, String url) throws RemoteException {
@@ -102,20 +86,12 @@
 
         @Override
         public void startPacSystem() throws RemoteException {
-            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-                Log.e(TAG, "Only system user is allowed to call startPacSystem");
-                throw new SecurityException();
-            }
-            mPacNative.startPacSupport();
+            //TODO: remove
         }
 
         @Override
         public void stopPacSystem() throws RemoteException {
-            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-                Log.e(TAG, "Only system user is allowed to call stopPacSystem");
-                throw new SecurityException();
-            }
-            mPacNative.stopPacSupport();
+            //TODO: remove
         }
     }
 }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4c569ef..649b5ea 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -43,6 +43,7 @@
         "android.hardware.broadcastradio-V2.0-java",
         "android.hardware.health-V1.0-java",
         "android.hardware.health-V2.0-java",
+        "android.hardware.light-java",
         "android.hardware.weaver-V1.0-java",
         "android.hardware.biometrics.face-V1.0-java",
         "android.hardware.biometrics.fingerprint-V2.1-java",
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index d18b4f6..0816955 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -66,8 +66,8 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.DumpUtils;
 import com.android.server.am.BatteryStatsService;
-import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
+import com.android.server.lights.LogicalLight;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -1063,7 +1063,7 @@
     }
 
     private final class Led {
-        private final Light mBatteryLight;
+        private final LogicalLight mBatteryLight;
 
         private final int mBatteryLowARGB;
         private final int mBatteryMediumARGB;
@@ -1098,7 +1098,7 @@
                     mBatteryLight.setColor(mBatteryLowARGB);
                 } else {
                     // Flash red when battery is low and not charging
-                    mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,
+                    mBatteryLight.setFlashing(mBatteryLowARGB, LogicalLight.LIGHT_FLASH_TIMED,
                             mBatteryLedOn, mBatteryLedOff);
                 }
             } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d304152..b3b1722 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3113,7 +3113,13 @@
         handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
     }
 
-    private void updateLingerState(NetworkAgentInfo nai, long now) {
+    /**
+     * Updates the linger state from the network requests inside the NAI.
+     * @param nai the agent info to update
+     * @param now the timestamp of the event causing this update
+     * @return whether the network was lingered as a result of this update
+     */
+    private boolean updateLingerState(@NonNull final NetworkAgentInfo nai, final long now) {
         // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
         // 2. If the network was lingering and there are now requests, unlinger it.
         // 3. If this network is unneeded (which implies it is not lingering), and there is at least
@@ -3124,12 +3130,15 @@
             nai.unlinger();
             logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
         } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
-            int lingerTime = (int) (nai.getLingerExpiry() - now);
-            if (DBG) log("Lingering " + nai.name() + " for " + lingerTime + "ms");
+            if (DBG) {
+                final int lingerTime = (int) (nai.getLingerExpiry() - now);
+                log("Lingering " + nai.name() + " for " + lingerTime + "ms");
+            }
             nai.linger();
             logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
-            notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
+            return true;
         }
+        return false;
     }
 
     private void handleAsyncChannelHalfConnect(Message msg) {
@@ -3473,7 +3482,10 @@
                 }
                 // If there are still lingered requests on this network, don't tear it down,
                 // but resume lingering instead.
-                updateLingerState(nai, SystemClock.elapsedRealtime());
+                final long now = SystemClock.elapsedRealtime();
+                if (updateLingerState(nai, now)) {
+                    notifyNetworkLosing(nai, now);
+                }
                 if (unneeded(nai, UnneededFor.TEARDOWN)) {
                     if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
                     teardownUnneededNetwork(nai);
@@ -6620,11 +6632,16 @@
                 }
                 if (currentNetwork == null || currentNetwork.getCurrentScore() < score) {
                     reassignedRequests.put(nri, newNetwork);
+                    changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
+                            nri, currentNetwork, newNetwork));
                 }
             } else if (newNetwork == currentNetwork) {
                 reassignedRequests.put(nri, null);
+                changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
+                        nri, currentNetwork, null));
             }
         }
+
         return reassignedRequests;
     }
 
@@ -6663,41 +6680,39 @@
             final NetworkRequestInfo nri = entry.getKey();
             final NetworkAgentInfo previousSatisfier = nri.mSatisfier;
             final NetworkAgentInfo newSatisfier = entry.getValue();
-            changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
-                    nri, previousSatisfier, newSatisfier));
-            if (newSatisfier != null) {
-                if (VDBG) log("rematch for " + newSatisfier.name());
-                if (previousSatisfier != null) {
-                    if (VDBG || DDBG) {
-                        log("   accepting network in place of " + previousSatisfier.name());
-                    }
-                    previousSatisfier.removeRequest(nri.request.requestId);
-                    previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
-                } else {
-                    if (VDBG || DDBG) log("   accepting network in place of null");
-                }
-                newSatisfier.unlingerRequest(nri.request);
-                if (!newSatisfier.addRequest(nri.request)) {
-                    Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
-                }
-            } else {
-                // If "newNetwork" is listed as satisfying "nri" but no longer satisfies "nri",
-                // mark it as no longer satisfying "nri".  Because networks are processed by
-                // rematchAllNetworksAndRequests() in descending score order, "currentNetwork" will
-                // match "newNetwork" before this loop will encounter a "currentNetwork" with higher
-                // score than "newNetwork" and where "currentNetwork" no longer satisfies "nri".
-                // This means this code doesn't have to handle the case where "currentNetwork" no
-                // longer satisfies "nri" when "currentNetwork" does not equal "newNetwork".
-                if (DBG) {
-                    log("Network " + newNetwork.name() + " stopped satisfying" +
-                            " request " + nri.request.requestId);
-                }
-                newNetwork.removeRequest(nri.request.requestId);
-            }
-            nri.mSatisfier = newSatisfier;
+            updateSatisfiersForRematchRequest(nri, previousSatisfier, newSatisfier, now);
         }
     }
 
+    private void updateSatisfiersForRematchRequest(@NonNull final NetworkRequestInfo nri,
+            @Nullable final NetworkAgentInfo previousSatisfier,
+            @Nullable final NetworkAgentInfo newSatisfier,
+            final long now) {
+        if (newSatisfier != null) {
+            if (VDBG) log("rematch for " + newSatisfier.name());
+            if (previousSatisfier != null) {
+                if (VDBG || DDBG) {
+                    log("   accepting network in place of " + previousSatisfier.name());
+                }
+                previousSatisfier.removeRequest(nri.request.requestId);
+                previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
+            } else {
+                if (VDBG || DDBG) log("   accepting network in place of null");
+            }
+            newSatisfier.unlingerRequest(nri.request);
+            if (!newSatisfier.addRequest(nri.request)) {
+                Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
+            }
+        } else {
+            if (DBG) {
+                log("Network " + previousSatisfier.name() + " stopped satisfying"
+                        + " request " + nri.request.requestId);
+            }
+            previousSatisfier.removeRequest(nri.request.requestId);
+        }
+        nri.mSatisfier = newSatisfier;
+    }
+
     /**
      * Attempt to rematch all Networks with NetworkRequests.  This may result in Networks
      * being disconnected.
@@ -6773,13 +6788,20 @@
             processNewlySatisfiedListenRequests(event.mNetwork);
         }
 
+        final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>();
         for (final NetworkAgentInfo nai : nais) {
             // Rematching may have altered the linger state of some networks, so update all linger
             // timers. updateLingerState reads the state from the network agent and does nothing
             // if the state has not changed : the source of truth is controlled with
             // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
             // called while rematching the individual networks above.
-            updateLingerState(nai, now);
+            if (updateLingerState(nai, now)) {
+                lingeredNetworks.add(nai);
+            }
+        }
+
+        for (final NetworkAgentInfo nai : lingeredNetworks) {
+            notifyNetworkLosing(nai, now);
         }
 
         updateLegacyTypeTrackerAndVpnLockdownForRematch(oldDefaultNetwork, newDefaultNetwork, nais);
@@ -6795,7 +6817,9 @@
                     // and became unneeded due to another network improving its score to the
                     // point where this network will no longer be able to satisfy any requests
                     // even if it validates.
-                    updateLingerState(nai, now);
+                    if (updateLingerState(nai, now)) {
+                        notifyNetworkLosing(nai, now);
+                    }
                 } else {
                     if (DBG) log("Reaping " + nai.name());
                     teardownUnneededNetwork(nai);
@@ -7048,6 +7072,12 @@
         callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
     }
 
+    // Notify the requests on this NAI that the network is now lingered.
+    private void notifyNetworkLosing(@NonNull final NetworkAgentInfo nai, final long now) {
+        final int lingerTime = (int) (nai.getLingerExpiry() - now);
+        notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
+    }
+
     /**
      * Notify of the blocked state apps with a registered callback matching a given NAI.
      *
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 097a7d6..ffda2f6 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -238,10 +238,11 @@
     private void handlePinOnStart() {
         final String bootImage = SystemProperties.get("dalvik.vm.boot-image", "");
         String[] filesToPin = null;
-        if (bootImage.endsWith("apex.art")) {
-            // Use the files listed for that specific boot image
+        if (bootImage.endsWith("boot-image.prof")) {
+            // Use the files listed for that specific boot image.
+            // TODO: find a better way to know we're using the JIT zygote configuration.
             filesToPin = mContext.getResources().getStringArray(
-                  com.android.internal.R.array.config_apexBootImagePinnerServiceFiles);
+                  com.android.internal.R.array.config_jitzygoteBootImagePinnerServiceFiles);
         } else {
             // Files to pin come from the overlay and can be specified per-device config
             filesToPin = mContext.getResources().getStringArray(
diff --git a/services/core/java/com/android/server/compat/TEST_MAPPING b/services/core/java/com/android/server/compat/TEST_MAPPING
new file mode 100644
index 0000000..0c30c79
--- /dev/null
+++ b/services/core/java/com/android/server/compat/TEST_MAPPING
@@ -0,0 +1,21 @@
+{
+    "presubmit": [
+        // Unit tests
+        {
+            "name": "FrameworksServicesTests",
+            "options": [
+                {
+                    "include-filter": "com.android.server.compat"
+                }
+            ]
+        },
+        // Tests for the TestRule
+        {
+            "name": "PlatformCompatGating"
+        },
+        // CTS tests
+        {
+            "name": "CtsAppCompatHostTestCases#"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index b03dc3b..46ee4ea 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -38,8 +38,8 @@
 import android.view.SurfaceControl;
 
 import com.android.server.LocalServices;
-import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
+import com.android.server.lights.LogicalLight;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -160,7 +160,7 @@
 
     private final class LocalDisplayDevice extends DisplayDevice {
         private final long mPhysicalDisplayId;
-        private final Light mBacklight;
+        private final LogicalLight mBacklight;
         private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
         private final ArrayList<Integer> mSupportedColorModes = new ArrayList<>();
         private final boolean mIsInternal;
diff --git a/services/core/java/com/android/server/lights/LightsManager.java b/services/core/java/com/android/server/lights/LightsManager.java
index be20a44..521913a 100644
--- a/services/core/java/com/android/server/lights/LightsManager.java
+++ b/services/core/java/com/android/server/lights/LightsManager.java
@@ -29,5 +29,8 @@
     public static final int LIGHT_ID_WIFI = Type.WIFI;
     public static final int LIGHT_ID_COUNT = Type.COUNT;
 
-    public abstract Light getLight(int id);
+    /**
+     * Returns the logical light with the given type.
+     */
+    public abstract LogicalLight getLight(int id);
 }
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index ac906bb..5861c9d 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -15,32 +15,223 @@
 
 package com.android.server.lights;
 
+import android.Manifest;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.Context;
+import android.hardware.light.HwLight;
+import android.hardware.light.HwLightState;
+import android.hardware.light.ILights;
+import android.hardware.lights.ILightsManager;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Message;
+import android.os.Looper;
 import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.Trace;
 import android.provider.Settings;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.SurfaceControl;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
 import com.android.server.SystemService;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 public class LightsService extends SystemService {
     static final String TAG = "LightsService";
     static final boolean DEBUG = false;
 
-    final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT];
+    private LightImpl[] mLights = null;
+    private SparseArray<LightImpl> mLightsById = null;
 
-    private final class LightImpl extends Light {
+    private ILights mVintfLights = null;
 
+    @VisibleForTesting
+    final LightsManagerBinderService mManagerService;
+
+    private Handler mH;
+
+    private final class LightsManagerBinderService extends ILightsManager.Stub {
+
+        private final class Session {
+            final IBinder mToken;
+            final SparseArray<LightState> mRequests = new SparseArray<>();
+
+            Session(IBinder token) {
+                mToken = token;
+            }
+
+            void setRequest(int lightId, LightState state) {
+                if (state != null) {
+                    mRequests.put(lightId, state);
+                } else {
+                    mRequests.remove(lightId);
+                }
+            }
+        }
+
+        @GuardedBy("LightsService.this")
+        private final List<Session> mSessions = new ArrayList<>();
+
+        /**
+         * Returns the lights available for apps to control on the device. Only lights that aren't
+         * reserved for system use are available to apps.
+         */
+        @Override
+        public List<Light> getLights() {
+            getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS,
+                    "getLights requires CONTROL_DEVICE_LIGHTS_PERMISSION");
+
+            synchronized (LightsService.this) {
+                final List<Light> lights = new ArrayList<Light>();
+                for (int i = 0; i < mLightsById.size(); i++) {
+                    HwLight hwLight = mLightsById.valueAt(i).getHwLight();
+                    if (!isSystemLight(hwLight)) {
+                        lights.add(new Light(hwLight.id, hwLight.ordinal, hwLight.type));
+                    }
+                }
+                return lights;
+            }
+        }
+
+        /**
+         * Updates the set of light requests for {@param token} with additions and removals from
+         * {@param lightIds} and {@param lightStates}.
+         *
+         * <p>Null values mean that the request should be removed, and the light turned off if it
+         * is not being used by anything else.
+         */
+        @Override
+        public void setLightStates(IBinder token, int[] lightIds, LightState[] lightStates) {
+            getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS,
+                    "setLightStates requires CONTROL_DEVICE_LIGHTS permission");
+            Preconditions.checkState(lightIds.length == lightStates.length);
+
+            synchronized (LightsService.this) {
+                Session session = getSessionLocked(Preconditions.checkNotNull(token));
+                Preconditions.checkState(session != null, "not registered");
+
+                checkRequestIsValid(lightIds);
+
+                for (int i = 0; i < lightIds.length; i++) {
+                    session.setRequest(lightIds[i], lightStates[i]);
+                }
+                invalidateLightStatesLocked();
+            }
+        }
+
+        @Override
+        public @Nullable LightState getLightState(int lightId) {
+            getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS,
+                    "getLightState(@TestApi) requires CONTROL_DEVICE_LIGHTS permission");
+
+            synchronized (LightsService.this) {
+                final LightImpl light = mLightsById.get(lightId);
+                if (light == null || isSystemLight(light.getHwLight())) {
+                    throw new IllegalArgumentException("Invalid light: " + lightId);
+                }
+                return new LightState(light.getColor());
+            }
+        }
+
+        @Override
+        public void openSession(IBinder token) {
+            getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS,
+                    "openSession requires CONTROL_DEVICE_LIGHTS permission");
+            Preconditions.checkNotNull(token);
+
+            synchronized (LightsService.this) {
+                Preconditions.checkState(getSessionLocked(token) == null, "already registered");
+                try {
+                    token.linkToDeath(() -> closeSessionInternal(token), 0);
+                    mSessions.add(new Session(token));
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Couldn't open session, client already died" , e);
+                    throw new IllegalArgumentException("Client is already dead.");
+                }
+            }
+        }
+
+        @Override
+        public void closeSession(IBinder token) {
+            getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS,
+                    "closeSession requires CONTROL_DEVICE_LIGHTS permission");
+            Preconditions.checkNotNull(token);
+            closeSessionInternal(token);
+        }
+
+        private void closeSessionInternal(IBinder token) {
+            synchronized (LightsService.this) {
+                final Session session = getSessionLocked(token);
+                if (session != null) {
+                    mSessions.remove(session);
+                    invalidateLightStatesLocked();
+                }
+            }
+        }
+
+        private void checkRequestIsValid(int[] lightIds) {
+            for (int i = 0; i < lightIds.length; i++) {
+                final LightImpl light = mLightsById.get(lightIds[i]);
+                final HwLight hwLight = light.getHwLight();
+                Preconditions.checkState(light != null && !isSystemLight(hwLight),
+                        "invalid lightId " + hwLight.id);
+            }
+        }
+
+        /**
+         * Apply light state requests for all light IDs.
+         *
+         * <p>In case of conflict, the session that started earliest wins.
+         */
+        private void invalidateLightStatesLocked() {
+            final Map<Integer, LightState> states = new HashMap<>();
+            for (int i = mSessions.size() - 1; i >= 0; i--) {
+                SparseArray<LightState> requests = mSessions.get(i).mRequests;
+                for (int j = 0; j < requests.size(); j++) {
+                    states.put(requests.keyAt(j), requests.valueAt(j));
+                }
+            }
+            for (int i = 0; i < mLightsById.size(); i++) {
+                LightImpl light = mLightsById.valueAt(i);
+                HwLight hwLight = light.getHwLight();
+                if (!isSystemLight(hwLight)) {
+                    LightState state = states.get(hwLight.id);
+                    if (state != null) {
+                        light.setColor(state.getColor());
+                    } else {
+                        light.turnOff();
+                    }
+                }
+            }
+        }
+
+        private @Nullable Session getSessionLocked(IBinder token) {
+            for (int i = 0; i < mSessions.size(); i++) {
+                if (token.equals(mSessions.get(i).mToken)) {
+                    return mSessions.get(i);
+                }
+            }
+            return null;
+        }
+    }
+
+    private final class LightImpl extends LogicalLight {
         private final IBinder mDisplayToken;
         private final int mSurfaceControlMaximumBrightness;
 
-        private LightImpl(Context context, int id) {
-            mId = id;
+        private LightImpl(Context context, HwLight hwLight) {
+            mHwLight = hwLight;
             mDisplayToken = SurfaceControl.getInternalDisplayToken();
             final boolean brightnessSupport = SurfaceControl.getDisplayBrightnessSupport(
                     mDisplayToken);
@@ -67,8 +258,8 @@
             synchronized (this) {
                 // LOW_PERSISTENCE cannot be manually set
                 if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
-                    Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId +
-                            ": brightness=0x" + Integer.toHexString(brightness));
+                    Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mHwLight.id
+                            + ": brightness=0x" + Integer.toHexString(brightness));
                     return;
                 }
                 // Ideally, we'd like to set the brightness mode through the SF/HWC as well, but
@@ -120,7 +311,7 @@
                     setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000,
                             BRIGHTNESS_MODE_USER);
                     mColor = 0;
-                    mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMS);
+                    mH.postDelayed(this::stopFlashing, onMS);
                 }
             }
         }
@@ -167,8 +358,10 @@
 
             if (!mInitialized || color != mColor || mode != mMode || onMS != mOnMS ||
                     offMS != mOffMS || mBrightnessMode != brightnessMode) {
-                if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"
-                        + Integer.toHexString(color) + ": brightnessMode=" + brightnessMode);
+                if (DEBUG) {
+                    Slog.v(TAG, "setLight #" + mHwLight.id + ": color=#"
+                            + Integer.toHexString(color) + ": brightnessMode=" + brightnessMode);
+                }
                 mInitialized = true;
                 mLastColor = mColor;
                 mColor = color;
@@ -176,10 +369,31 @@
                 mOnMS = onMS;
                 mOffMS = offMS;
                 mBrightnessMode = brightnessMode;
-                Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x"
-                        + Integer.toHexString(color) + ")");
+                setLightUnchecked(color, mode, onMS, offMS, brightnessMode);
+            }
+        }
+
+        private void setLightUnchecked(int color, int mode, int onMS, int offMS,
+                int brightnessMode) {
+            Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLightState(" + mHwLight.id + ", 0x"
+                    + Integer.toHexString(color) + ")");
+            if (mVintfLights != null) {
+                HwLightState lightState = new HwLightState();
+                lightState.color = color;
+                lightState.flashMode = (byte) mode;
+                lightState.flashOnMs = onMS;
+                lightState.flashOffMs = offMS;
+                lightState.brightnessMode = (byte) brightnessMode;
                 try {
-                    setLight_native(mId, color, mode, onMS, offMS, brightnessMode);
+                    mVintfLights.setLightState(mHwLight.id, lightState);
+                } catch (RemoteException | UnsupportedOperationException ex) {
+                    Slog.e(TAG, "Failed issuing setLightState", ex);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_POWER);
+                }
+            } else {
+                try {
+                    setLight_native(mHwLight.id, color, mode, onMS, offMS, brightnessMode);
                 } finally {
                     Trace.traceEnd(Trace.TRACE_TAG_POWER);
                 }
@@ -190,7 +404,15 @@
             return mVrModeEnabled && mUseLowPersistenceForVR;
         }
 
-        private int mId;
+        private HwLight getHwLight() {
+            return mHwLight;
+        }
+
+        private int getColor() {
+            return mColor;
+        }
+
+        private HwLight mHwLight;
         private int mColor;
         private int mMode;
         private int mOnMS;
@@ -205,16 +427,59 @@
     }
 
     public LightsService(Context context) {
-        super(context);
+        this(context,
+                ILights.Stub.asInterface(
+                        ServiceManager.getService("android.hardware.light.ILights/default")),
+                Looper.myLooper());
+    }
 
-        for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) {
-            mLights[i] = new LightImpl(context, i);
+    @VisibleForTesting
+    LightsService(Context context, ILights service, Looper looper) {
+        super(context);
+        mH = new Handler(looper);
+        mVintfLights = service;
+        mManagerService = new LightsManagerBinderService();
+        populateAvailableLights(context);
+    }
+
+    private void populateAvailableLights(Context context) {
+        mLights = new LightImpl[LightsManager.LIGHT_ID_COUNT];
+        mLightsById = new SparseArray<>();
+
+        if (mVintfLights != null) {
+            try {
+                for (HwLight availableLight : mVintfLights.getLights()) {
+                    LightImpl light = new LightImpl(context, availableLight);
+                    int type = (int) availableLight.type;
+                    if (0 <= type && type < mLights.length && mLights[type] == null) {
+                        mLights[type] = light;
+                    }
+                    mLightsById.put(availableLight.id, light);
+                }
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "Unable to get lights for initialization", ex);
+            }
+        }
+
+        // In the case where only the old HAL is available, all lights will be initialized here
+        for (int i = 0; i < mLights.length; i++) {
+            if (mLights[i] == null) {
+                // The ordinal can be anything if there is only 1 light of each type. Set it to 1.
+                HwLight light = new HwLight();
+                light.id = (byte) i;
+                light.ordinal = 1;
+                light.type = (byte) i;
+
+                mLights[i] = new LightImpl(context, light);
+                mLightsById.put(i, mLights[i]);
+            }
         }
     }
 
     @Override
     public void onStart() {
         publishLocalService(LightsManager.class, mService);
+        publishBinderService(Context.LIGHTS_SERVICE, mManagerService);
     }
 
     @Override
@@ -231,22 +496,25 @@
 
     private final LightsManager mService = new LightsManager() {
         @Override
-        public Light getLight(int id) {
-            if (0 <= id && id < LIGHT_ID_COUNT) {
-                return mLights[id];
+        public LogicalLight getLight(int lightType) {
+            if (mLights != null && 0 <= lightType && lightType < mLights.length) {
+                return mLights[lightType];
             } else {
                 return null;
             }
         }
     };
 
-    private Handler mH = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            LightImpl light = (LightImpl)msg.obj;
-            light.stopFlashing();
-        }
-    };
+    /**
+     * Returns whether a light is system-use-only or should be accessible to
+     * applications using the {@link android.hardware.lights.LightsManager} API.
+     */
+    private static boolean isSystemLight(HwLight light) {
+        // LIGHT_ID_COUNT comes from the 2.0 HIDL HAL and only contains system
+        // lights. Newly added lights will be made available via the
+        // LightsManager API.
+        return 0 <= light.type && light.type < LightsManager.LIGHT_ID_COUNT;
+    }
 
     static native void setLight_native(int light, int color, int mode,
             int onMS, int offMS, int brightnessMode);
diff --git a/services/core/java/com/android/server/lights/Light.java b/services/core/java/com/android/server/lights/LogicalLight.java
similarity index 72%
rename from services/core/java/com/android/server/lights/Light.java
rename to services/core/java/com/android/server/lights/LogicalLight.java
index 717e3da..30e2473 100644
--- a/services/core/java/com/android/server/lights/Light.java
+++ b/services/core/java/com/android/server/lights/LogicalLight.java
@@ -19,9 +19,24 @@
 import android.hardware.light.V2_0.Brightness;
 import android.hardware.light.V2_0.Flash;
 
-public abstract class Light {
+/**
+ * Allow control over a logical light of a given type. The mapping of logical lights to physical
+ * lights is HAL implementation-dependent.
+ */
+public abstract class LogicalLight {
+    /**
+     * Keep the light steady on or off.
+     */
     public static final int LIGHT_FLASH_NONE = Flash.NONE;
+
+    /**
+     * Flash the light at specified rate.
+     */
     public static final int LIGHT_FLASH_TIMED = Flash.TIMED;
+
+    /**
+     * Flash the light using hardware assist.
+     */
     public static final int LIGHT_FLASH_HARDWARE = Flash.HARDWARE;
 
     /**
@@ -49,10 +64,33 @@
      */
     public abstract void setBrightness(int brightness, int brightnessMode);
 
+    /**
+     * Set the color of a light.
+     */
     public abstract void setColor(int color);
+
+    /**
+     * Set the color of a light and control flashing.
+     */
     public abstract void setFlashing(int color, int mode, int onMS, int offMS);
+
+    /**
+     * Pulses the light.
+     */
     public abstract void pulse();
+
+    /**
+     * Pulses the light with a specified color for a specified duration.
+     */
     public abstract void pulse(int color, int onMS);
+
+    /**
+     * Turns off the light.
+     */
     public abstract void turnOff();
+
+    /**
+     * Set the VR mode of a display.
+     */
     public abstract void setVrMode(boolean enabled);
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6f45825..85ba367 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -233,8 +233,8 @@
 import com.android.server.IoThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
+import com.android.server.lights.LogicalLight;
 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
 import com.android.server.notification.ManagedServices.UserProfiles;
 import com.android.server.pm.PackageManagerService;
@@ -376,8 +376,8 @@
     private final HandlerThread mRankingThread = new HandlerThread("ranker",
             Process.THREAD_PRIORITY_BACKGROUND);
 
-    private Light mNotificationLight;
-    Light mAttentionLight;
+    private LogicalLight mNotificationLight;
+    LogicalLight mAttentionLight;
 
     private long[] mFallbackVibrationPattern;
     private boolean mUseAttentionLight;
@@ -1507,7 +1507,7 @@
     }
 
     @VisibleForTesting
-    void setLights(Light light) {
+    void setLights(LogicalLight light) {
         mNotificationLight = light;
         mAttentionLight = light;
         mNotificationPulseEnabled = true;
@@ -7085,7 +7085,7 @@
             NotificationRecord.Light light = ledNotification.getLight();
             if (light != null && mNotificationPulseEnabled) {
                 // pulse repeatedly
-                mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
+                mNotificationLight.setFlashing(light.color, LogicalLight.LIGHT_FLASH_TIMED,
                         light.onMs, light.offMs);
             }
         }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 6fced8a..d30afaa 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -48,6 +48,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.PermissionWhitelistFlags;
 import android.content.pm.PackageManagerInternal;
@@ -2614,7 +2615,7 @@
 
         // Make sure all dynamic permissions have been assigned to a package,
         // and make sure there are no dangling permissions.
-        flags = updatePermissions(changingPkgName, changingPkg, flags);
+        flags = updatePermissions(changingPkgName, changingPkg, flags, callback);
 
         synchronized (mLock) {
             if (mBackgroundPermissions == null) {
@@ -2664,7 +2665,8 @@
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
     }
 
-    private int updatePermissions(String packageName, PackageParser.Package pkg, int flags) {
+    private int updatePermissions(String packageName, PackageParser.Package pkg, int flags,
+            @Nullable PermissionCallback callback) {
         Set<BasePermission> needsUpdate = null;
         synchronized (mLock) {
             final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator();
@@ -2678,6 +2680,44 @@
                         && (pkg == null || !hasPermission(pkg, bp.getName()))) {
                         Slog.i(TAG, "Removing old permission tree: " + bp.getName()
                                 + " from package " + bp.getSourcePackageName());
+                        if (bp.isRuntime()) {
+                            final int[] userIds = mUserManagerInt.getUserIds();
+                            final int numUserIds = userIds.length;
+                            for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
+                                final int userId = userIds[userIdNum];
+
+                                mPackageManagerInt.forEachPackage((Package p) -> {
+                                    final String pName = p.packageName;
+                                    final ApplicationInfo appInfo =
+                                            mPackageManagerInt.getApplicationInfo(pName, 0,
+                                                    Process.SYSTEM_UID, UserHandle.USER_SYSTEM);
+                                    if (appInfo != null
+                                            && appInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+                                        return;
+                                    }
+
+                                    final String permissionName = bp.getName();
+                                    if (checkPermission(permissionName, pName, Process.SYSTEM_UID,
+                                            userId) == PackageManager.PERMISSION_GRANTED) {
+                                        try {
+                                            revokeRuntimePermission(
+                                                    permissionName,
+                                                    pName,
+                                                    false,
+                                                    userId,
+                                                    callback);
+                                        } catch (IllegalArgumentException e) {
+                                            Slog.e(TAG,
+                                                    "Failed to revoke "
+                                                            + permissionName
+                                                            + " from "
+                                                            + pName,
+                                                    e);
+                                        }
+                                    }
+                                });
+                            }
+                        }
                         flags |= UPDATE_PERMISSIONS_ALL;
                         it.remove();
                     }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index e1b3e4d..3a7604a 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -93,8 +93,8 @@
 import com.android.server.UiThread;
 import com.android.server.Watchdog;
 import com.android.server.am.BatteryStatsService;
-import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
+import com.android.server.lights.LogicalLight;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.power.batterysaver.BatterySaverController;
 import com.android.server.power.batterysaver.BatterySaverPolicy;
@@ -247,7 +247,7 @@
     private WirelessChargerDetector mWirelessChargerDetector;
     private SettingsObserver mSettingsObserver;
     private DreamManagerInternal mDreamManager;
-    private Light mAttentionLight;
+    private LogicalLight mAttentionLight;
 
     private final Object mLock = LockGuard.installNewLock(LockGuard.INDEX_POWER);
 
@@ -3130,7 +3130,7 @@
     }
 
     private void setAttentionLightInternal(boolean on, int color) {
-        Light light;
+        LogicalLight light;
         synchronized (mLock) {
             if (!mSystemReady) {
                 return;
@@ -3139,7 +3139,7 @@
         }
 
         // Control light outside of lock.
-        light.setFlashing(color, Light.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
+        light.setFlashing(color, LogicalLight.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
     }
 
     private void setDozeAfterScreenOffInternal(boolean on) {
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 5f00148..89a5305 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -30,6 +30,7 @@
 import android.service.textclassifier.ITextClassifierCallback;
 import android.service.textclassifier.ITextClassifierService;
 import android.service.textclassifier.TextClassifierService;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.textclassifier.ConversationActions;
@@ -54,6 +55,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
+import java.util.Map;
 import java.util.Queue;
 
 /**
@@ -119,6 +121,8 @@
     private final Object mLock;
     @GuardedBy("mLock")
     final SparseArray<UserState> mUserStates = new SparseArray<>();
+    @GuardedBy("mLock")
+    private final Map<TextClassificationSessionId, Integer> mSessionUserIds = new ArrayMap<>();
 
     private TextClassificationManagerService(Context context) {
         mContext = Preconditions.checkNotNull(context);
@@ -127,15 +131,16 @@
 
     @Override
     public void onSuggestSelection(
-            TextClassificationSessionId sessionId,
+            @Nullable TextClassificationSessionId sessionId,
             TextSelection.Request request, ITextClassifierCallback callback)
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
-        validateInput(mContext, request.getCallingPackageName());
+        final int userId = request.getUserId();
+        validateInput(mContext, request.getCallingPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
@@ -150,15 +155,16 @@
 
     @Override
     public void onClassifyText(
-            TextClassificationSessionId sessionId,
+            @Nullable TextClassificationSessionId sessionId,
             TextClassification.Request request, ITextClassifierCallback callback)
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
-        validateInput(mContext, request.getCallingPackageName());
+        final int userId = request.getUserId();
+        validateInput(mContext, request.getCallingPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
@@ -173,15 +179,16 @@
 
     @Override
     public void onGenerateLinks(
-            TextClassificationSessionId sessionId,
+            @Nullable TextClassificationSessionId sessionId,
             TextLinks.Request request, ITextClassifierCallback callback)
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
-        validateInput(mContext, request.getCallingPackageName());
+        final int userId = request.getUserId();
+        validateInput(mContext, request.getCallingPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
@@ -196,12 +203,14 @@
 
     @Override
     public void onSelectionEvent(
-            TextClassificationSessionId sessionId, SelectionEvent event) throws RemoteException {
+            @Nullable TextClassificationSessionId sessionId, SelectionEvent event)
+            throws RemoteException {
         Preconditions.checkNotNull(event);
-        validateInput(mContext, event.getPackageName());
+        final int userId = event.getUserId();
+        validateInput(mContext, event.getPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (userState.isBoundLocked()) {
                 userState.mService.onSelectionEvent(sessionId, event);
             } else {
@@ -213,16 +222,19 @@
     }
     @Override
     public void onTextClassifierEvent(
-            TextClassificationSessionId sessionId,
+            @Nullable TextClassificationSessionId sessionId,
             TextClassifierEvent event) throws RemoteException {
         Preconditions.checkNotNull(event);
         final String packageName = event.getEventContext() == null
                 ? null
                 : event.getEventContext().getPackageName();
-        validateInput(mContext, packageName);
+        final int userId = event.getEventContext() == null
+                ? UserHandle.getCallingUserId()
+                : event.getEventContext().getUserId();
+        validateInput(mContext, packageName, userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (userState.isBoundLocked()) {
                 userState.mService.onTextClassifierEvent(sessionId, event);
             } else {
@@ -235,15 +247,16 @@
 
     @Override
     public void onDetectLanguage(
-            TextClassificationSessionId sessionId,
+            @Nullable TextClassificationSessionId sessionId,
             TextLanguage.Request request,
             ITextClassifierCallback callback) throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
-        validateInput(mContext, request.getCallingPackageName());
+        final int userId = request.getUserId();
+        validateInput(mContext, request.getCallingPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
@@ -258,15 +271,16 @@
 
     @Override
     public void onSuggestConversationActions(
-            TextClassificationSessionId sessionId,
+            @Nullable TextClassificationSessionId sessionId,
             ConversationActions.Request request,
             ITextClassifierCallback callback) throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
-        validateInput(mContext, request.getCallingPackageName());
+        final int userId = request.getUserId();
+        validateInput(mContext, request.getCallingPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
@@ -285,13 +299,15 @@
             throws RemoteException {
         Preconditions.checkNotNull(sessionId);
         Preconditions.checkNotNull(classificationContext);
-        validateInput(mContext, classificationContext.getPackageName());
+        final int userId = classificationContext.getUserId();
+        validateInput(mContext, classificationContext.getPackageName(), userId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            UserState userState = getUserStateLocked(userId);
             if (userState.isBoundLocked()) {
                 userState.mService.onCreateTextClassificationSession(
                         classificationContext, sessionId);
+                mSessionUserIds.put(sessionId, userId);
             } else {
                 userState.mPendingRequests.add(new PendingRequest(
                         () -> onCreateTextClassificationSession(classificationContext, sessionId),
@@ -306,9 +322,15 @@
         Preconditions.checkNotNull(sessionId);
 
         synchronized (mLock) {
-            UserState userState = getCallingUserStateLocked();
+            final int userId = mSessionUserIds.containsKey(sessionId)
+                    ? mSessionUserIds.get(sessionId)
+                    : UserHandle.getCallingUserId();
+            validateInput(mContext, null /* packageName */, userId);
+
+            UserState userState = getUserStateLocked(userId);
             if (userState.isBoundLocked()) {
                 userState.mService.onDestroyTextClassificationSession(sessionId);
+                mSessionUserIds.remove(sessionId);
             } else {
                 userState.mPendingRequests.add(new PendingRequest(
                         () -> onDestroyTextClassificationSession(sessionId),
@@ -318,11 +340,6 @@
     }
 
     @GuardedBy("mLock")
-    private UserState getCallingUserStateLocked() {
-        return getUserStateLocked(UserHandle.getCallingUserId());
-    }
-
-    @GuardedBy("mLock")
     private UserState getUserStateLocked(int userId) {
         UserState result = mUserStates.get(userId);
         if (result == null) {
@@ -356,6 +373,7 @@
                     pw.decreaseIndent();
                 }
             }
+            pw.println("Number of active sessions: " + mSessionUserIds.size());
         }
     }
 
@@ -420,20 +438,32 @@
                 e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage()));
     }
 
-    private static void validateInput(Context context, @Nullable String packageName)
+    private static void validateInput(
+            Context context, @Nullable String packageName, @UserIdInt int userId)
             throws RemoteException {
-        if (packageName == null) return;
 
         try {
-            final int packageUid = context.getPackageManager()
-                    .getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
-            final int callingUid = Binder.getCallingUid();
-            Preconditions.checkArgument(callingUid == packageUid
-                    // Trust the system process:
-                    || callingUid == android.os.Process.SYSTEM_UID);
+            if (packageName != null) {
+                final int packageUid = context.getPackageManager()
+                        .getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
+                final int callingUid = Binder.getCallingUid();
+                Preconditions.checkArgument(callingUid == packageUid
+                        // Trust the system process:
+                        || callingUid == android.os.Process.SYSTEM_UID,
+                        "Invalid package name. Package=" + packageName
+                                + ", CallingUid=" + callingUid);
+            }
+
+            Preconditions.checkArgument(userId != UserHandle.USER_NULL, "Null userId");
+            final int callingUserId = UserHandle.getCallingUserId();
+            if (callingUserId != userId) {
+                context.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                        "Invalid userId. UserId=" + userId + ", CallingUserId=" + callingUserId);
+            }
         } catch (Exception e) {
-            throw new RemoteException(
-                    String.format("Invalid package: name=%s, error=%s", packageName, e));
+            throw new RemoteException("Invalid request: " + e.getMessage(), e,
+                    /* enableSuppression */ true, /* writableStackTrace */ true);
         }
     }
 
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 8c54fa9..88de340 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -87,11 +87,15 @@
     private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
 
     /**
-     * The number of previous telephony suggestions to keep for each ID (for use during debugging).
+     * The number of suggestions to keep. These are logged in bug reports to assist when debugging
+     * issues with detection.
      */
-    private static final int KEEP_SUGGESTION_HISTORY_SIZE = 30;
+    private static final int KEEP_SUGGESTION_HISTORY_SIZE = 10;
 
-    // A log for changes made to the system clock and why.
+    /**
+     * A log that records the decisions / decision metadata that affected the device's system clock
+     * time. This is logged in bug reports to assist with debugging issues with detection.
+     */
     @NonNull
     private final LocalLog mTimeChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
 
@@ -140,7 +144,18 @@
         if (!validateSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) {
             return;
         }
-        mLastNetworkSuggestion.set(timeSuggestion);
+
+        // The caller submits suggestions with the best available information when there are network
+        // changes. The best available information may have been cached and if they were all stored
+        // this would lead to duplicates showing up in the suggestion history. The suggestions may
+        // be made for different reasons but there is not a significant benefit to storing the same
+        // suggestion information again. doAutoTimeDetection() should still be called: this ensures
+        // the suggestion and device state are always re-evaluated, which might produce a different
+        // detected time if, for example, the age of all suggestions are considered.
+        NetworkTimeSuggestion lastNetworkSuggestion = mLastNetworkSuggestion.get();
+        if (lastNetworkSuggestion == null || !lastNetworkSuggestion.equals(timeSuggestion)) {
+            mLastNetworkSuggestion.set(timeSuggestion);
+        }
 
         // Now perform auto time detection. The new suggestion may be used to modify the system
         // clock.
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index 652dbe15..cc33fb0 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -161,16 +161,17 @@
     public static final int TELEPHONY_SCORE_USAGE_THRESHOLD = TELEPHONY_SCORE_MEDIUM;
 
     /**
-     * The number of previous telephony suggestions to keep for each ID (for use during debugging).
+     * The number of suggestions to keep. These are logged in bug reports to assist when debugging
+     * issues with detection.
      */
-    private static final int KEEP_TELEPHONY_SUGGESTION_HISTORY_SIZE = 30;
+    private static final int KEEP_SUGGESTION_HISTORY_SIZE = 10;
 
     @NonNull
     private final Callback mCallback;
 
     /**
-     * A log that records the decisions / decision metadata that affected the device's time zone
-     * (for use during debugging).
+     * A log that records the decisions / decision metadata that affected the device's time zone.
+     * This is logged in bug reports to assist with debugging issues with detection.
      */
     @NonNull
     private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
@@ -182,8 +183,7 @@
      */
     @GuardedBy("this")
     private ArrayMapWithHistory<Integer, QualifiedTelephonyTimeZoneSuggestion>
-            mSuggestionBySlotIndex =
-            new ArrayMapWithHistory<>(KEEP_TELEPHONY_SUGGESTION_HISTORY_SIZE);
+            mSuggestionBySlotIndex = new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
     /**
      * Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 99a9db3..d4a4628 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -865,6 +865,8 @@
                 if (canToastShowWhenLocked(callingPid)) {
                     attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
                 }
+                // Toasts can't be clickable
+                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
                 break;
         }
 
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index d4dd245..42859d1 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -63,7 +63,6 @@
         "libbacktrace",
         "libbase",
         "libbinder",
-        "libbinderthreadstate",
         "libc++",
         "libcutils",
         "liblog",
diff --git a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
new file mode 100644
index 0000000..b0def60
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 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.lights;
+
+import static android.hardware.lights.LightsRequest.Builder;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.hardware.light.HwLight;
+import android.hardware.light.HwLightState;
+import android.hardware.light.ILights;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+import android.hardware.lights.LightsManager;
+import android.os.Looper;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LightsServiceTest {
+
+    private final ILights mHal = new ILights.Stub() {
+        @Override
+        public void setLightState(int id, HwLightState state) {
+            return;
+        }
+
+        @Override
+        public HwLight[] getLights() {
+            return new HwLight[] {
+                fakeHwLight(101, 3, 1),
+                fakeHwLight(102, LightsManager.LIGHT_TYPE_MICROPHONE, 4),
+                fakeHwLight(103, LightsManager.LIGHT_TYPE_MICROPHONE, 3),
+                fakeHwLight(104, LightsManager.LIGHT_TYPE_MICROPHONE, 1),
+                fakeHwLight(105, LightsManager.LIGHT_TYPE_MICROPHONE, 2)
+            };
+        }
+    };
+
+    private static HwLight fakeHwLight(int id, int type, int ordinal) {
+        HwLight light = new HwLight();
+        light.id = id;
+        light.type = (byte) type;
+        light.ordinal = ordinal;
+        return light;
+    }
+
+    @Mock
+    Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testGetLights_filtersSystemLights() {
+        LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+        LightsManager manager = new LightsManager(mContext, service.mManagerService);
+
+        // When lights are listed, only the 4 MICROPHONE lights should be visible.
+        assertThat(manager.getLights().size()).isEqualTo(4);
+    }
+
+    @Test
+    public void testControlMultipleLights() {
+        LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+        LightsManager manager = new LightsManager(mContext, service.mManagerService);
+
+        // When the session requests to turn 3/4 lights on:
+        LightsManager.LightsSession session = manager.openSession();
+        session.setLights(new Builder()
+                .setLight(manager.getLights().get(0), new LightState(0xf1))
+                .setLight(manager.getLights().get(1), new LightState(0xf2))
+                .setLight(manager.getLights().get(2), new LightState(0xf3))
+                .build());
+
+        // Then all 3 should turn on.
+        assertThat(manager.getLightState(manager.getLights().get(0)).getColor()).isEqualTo(0xf1);
+        assertThat(manager.getLightState(manager.getLights().get(1)).getColor()).isEqualTo(0xf2);
+        assertThat(manager.getLightState(manager.getLights().get(2)).getColor()).isEqualTo(0xf3);
+
+        // And the 4th should remain off.
+        assertThat(manager.getLightState(manager.getLights().get(3)).getColor()).isEqualTo(0x00);
+    }
+
+    @Test
+    public void testControlLights_onlyEffectiveForLifetimeOfClient() {
+        LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+        LightsManager manager = new LightsManager(mContext, service.mManagerService);
+        Light micLight = manager.getLights().get(0);
+
+        // The light should begin by being off.
+        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0x00000000);
+
+        // When a session commits changes:
+        LightsManager.LightsSession session = manager.openSession();
+        session.setLights(new Builder().setLight(micLight, new LightState(0xff00ff00)).build());
+        // Then the light should turn on.
+        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xff00ff00);
+
+        // When the session goes away:
+        session.close();
+        // Then the light should turn off.
+        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0x00000000);
+    }
+
+    @Test
+    public void testControlLights_firstCallerWinsContention() {
+        LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+        LightsManager manager = new LightsManager(mContext, service.mManagerService);
+        Light micLight = manager.getLights().get(0);
+
+        LightsManager.LightsSession session1 = manager.openSession();
+        LightsManager.LightsSession session2 = manager.openSession();
+
+        // When session1 and session2 both request the same light:
+        session1.setLights(new Builder().setLight(micLight, new LightState(0xff0000ff)).build());
+        session2.setLights(new Builder().setLight(micLight, new LightState(0xffffffff)).build());
+        // Then session1 should win because it was created first.
+        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xff0000ff);
+
+        // When session1 goes away:
+        session1.close();
+        // Then session2 should have its request go into effect.
+        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xffffffff);
+
+        // When session2 goes away:
+        session2.close();
+        // Then the light should turn off because there are no more sessions.
+        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0);
+    }
+
+    @Test
+    public void testClearLight() {
+        LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+        LightsManager manager = new LightsManager(mContext, service.mManagerService);
+        Light micLight = manager.getLights().get(0);
+
+        // When the session turns a light on:
+        LightsManager.LightsSession session = manager.openSession();
+        session.setLights(new Builder().setLight(micLight, new LightState(0xffffffff)).build());
+
+        // And then the session clears it again:
+        session.setLights(new Builder().clearLight(micLight).build());
+
+        // Then the light should turn back off.
+        assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0);
+    }
+}
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 92198fa..6d832fc 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -46,7 +46,6 @@
         "libbacktrace",
         "libbase",
         "libbinder",
-        "libbinderthreadstate",
         "libc++",
         "libcutils",
         "liblog",
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 8c3373f..32263e1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -72,7 +72,7 @@
 
 import com.android.internal.util.IntPair;
 import com.android.server.UiServiceTestCase;
-import com.android.server.lights.Light;
+import com.android.server.lights.LogicalLight;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -89,7 +89,7 @@
     @Mock AudioManager mAudioManager;
     @Mock Vibrator mVibrator;
     @Mock android.media.IRingtonePlayer mRingtonePlayer;
-    @Mock Light mLight;
+    @Mock LogicalLight mLight;
     @Mock
     NotificationManagerService.WorkerHandler mHandler;
     @Mock
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 0fce618..f390ae6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -142,8 +142,8 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.UiServiceTestCase;
-import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
+import com.android.server.lights.LogicalLight;
 import com.android.server.notification.NotificationManagerService.NotificationAssistants;
 import com.android.server.notification.NotificationManagerService.NotificationListeners;
 import com.android.server.uri.UriGrantsManagerInternal;
@@ -351,7 +351,7 @@
                 .thenReturn(applicationInfo);
         when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid);
         final LightsManager mockLightsManager = mock(LightsManager.class);
-        when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class));
+        when(mockLightsManager.getLight(anyInt())).thenReturn(mock(LogicalLight.class));
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
         when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
diff --git a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
index cd365a1..95098e8 100644
--- a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
+++ b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
@@ -31,6 +31,7 @@
 
 import com.android.internal.telephony.HbpcdLookup.MccIdd;
 import com.android.internal.telephony.HbpcdLookup.MccLookup;
+import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -677,7 +678,7 @@
      */
     private static String secureHash(byte[] input) {
         // Refrain from logging user personal information in user build.
-        if (android.os.Build.IS_USER) {
+        if (TelephonyUtils.IS_USER) {
             return "****";
         }
 
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 2abcc76..ec1c6c9 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -28,6 +28,8 @@
 import android.os.SystemProperties;
 
 import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.List;
 import java.util.function.Supplier;
 
 /**
@@ -59,6 +61,11 @@
         return str == null ? "" : str;
     }
 
+    /** Returns an empty list if the input is {@code null}. */
+    public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) {
+        return cur == null ? Collections.emptyList() : cur;
+    }
+
     /** Throws a {@link RuntimeException} that wrapps the {@link RemoteException}. */
     public static RuntimeException rethrowAsRuntimeException(RemoteException remoteException) {
         throw new RuntimeException(remoteException);
diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java
index 90244b3..c66aceb 100644
--- a/telephony/java/android/telephony/PhoneCapability.java
+++ b/telephony/java/android/telephony/PhoneCapability.java
@@ -25,7 +25,7 @@
 import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
 import android.telephony.TelephonyManager.NetworkTypeBitMask;
 
-import com.android.internal.util.CollectionUtils;
+import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -181,13 +181,13 @@
         this.mEutranUeCategoryUl = eutranUeCategoryUl;
         this.mPsDataConnectionLingerTimeMillis = psDataConnectionLingerTimeMillis;
         this.mSupportedRats = supportedRats;
-        this.mGeranBands = CollectionUtils.emptyIfNull(geranBands);
-        this.mUtranBands = CollectionUtils.emptyIfNull(utranBands);
-        this.mEutranBands = CollectionUtils.emptyIfNull(eutranBands);
-        this.mNgranBands = CollectionUtils.emptyIfNull(ngranBands);
-        this.mLogicalModemUuids = CollectionUtils.emptyIfNull(logicalModemUuids);
-        this.mSimSlotCapabilities = CollectionUtils.emptyIfNull(simSlotCapabilities);
-        this.mConcurrentFeaturesSupport = CollectionUtils.emptyIfNull(concurrentFeaturesSupport);
+        this.mGeranBands = TelephonyUtils.emptyIfNull(geranBands);
+        this.mUtranBands = TelephonyUtils.emptyIfNull(utranBands);
+        this.mEutranBands = TelephonyUtils.emptyIfNull(eutranBands);
+        this.mNgranBands = TelephonyUtils.emptyIfNull(ngranBands);
+        this.mLogicalModemUuids = TelephonyUtils.emptyIfNull(logicalModemUuids);
+        this.mSimSlotCapabilities = TelephonyUtils.emptyIfNull(simSlotCapabilities);
+        this.mConcurrentFeaturesSupport = TelephonyUtils.emptyIfNull(concurrentFeaturesSupport);
     }
 
     private PhoneCapability(Parcel in) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ffa0810..0042ec8 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -12408,6 +12408,7 @@
     }
 
     /**
+<<<<<<< HEAD
      * Gets the voice call forwarding info {@link CallForwardingInfo}, given the call forward
      * reason.
      *
@@ -12593,15 +12594,21 @@
     }
 
     /**
-     * Set allowing mobile data during voice call.
+     * Set allowing mobile data during voice call. This is used for allowing data on the non-default
+     * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will
+     * not be able to use mobile data. By calling this API, data will be temporarily enabled on the
+     * non-default data SIM during the life cycle of the voice call.
      *
      * @param allow {@code true} if allowing using data during voice call, {@code false} if
-     * disallowed
+     * disallowed.
      *
-     * @return {@code false} if the setting is changed.
+     * @return {@code true} if operation is successful. otherwise {@code false}.
+     *
+     * @throws SecurityException if the caller doesn't have the permission.
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setDataAllowedDuringVoiceCall(boolean allow) {
         try {
@@ -12610,6 +12617,7 @@
                 return service.setDataAllowedDuringVoiceCall(getSubId(), allow);
             }
         } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
             if (!isSystemProcess()) {
                 ex.rethrowAsRuntimeException();
             }
@@ -12618,13 +12626,18 @@
     }
 
     /**
-     * Check whether data is allowed during voice call. Note this is for dual sim device that
-     * data might be disabled on non-default data subscription but explicitly turned on by settings.
+     * Check whether data is allowed during voice call. This is used for allowing data on the
+     * non-default data SIM. When a voice call is placed on the non-default data SIM on DSDS
+     * devices, users will not be able to use mobile data. By calling this API, data will be
+     * temporarily enabled on the non-default data SIM during the life cycle of the voice call.
      *
      * @return {@code true} if data is allowed during voice call.
      *
+     * @throws SecurityException if the caller doesn't have the permission.
+     *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isDataAllowedInVoiceCall() {
         try {
@@ -12633,6 +12646,7 @@
                 return service.isDataAllowedInVoiceCall(getSubId());
             }
         } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
             if (!isSystemProcess()) {
                 ex.rethrowAsRuntimeException();
             }
@@ -12665,4 +12679,108 @@
         }
         return false;
     }
+
+    /**
+     * The IccLock state or password was changed successfully.
+     * @hide
+     */
+    @SystemApi
+    public static final int CHANGE_ICC_LOCK_SUCCESS = Integer.MAX_VALUE;
+
+    /**
+     * Check whether ICC pin lock is enabled.
+     * This is a sync call which returns the cached pin enabled state.
+     *
+     * @return {@code true} if ICC lock enabled, {@code false} if ICC lock disabled.
+     *
+     * @throws SecurityException if the caller doesn't have the permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    @WorkerThread
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isIccLockEnabled() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isIccLockEnabled(getSubId());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "isIccLockEnabled RemoteException", e);
+        }
+        return false;
+    }
+
+    /**
+     * Set the ICC pin lock enabled or disabled.
+     *
+     * If enable/disable ICC pin lock successfully, a value of {@link Integer#MAX_VALUE} is
+     * returned.
+     * If an incorrect old password is specified, the return value will indicate how many more
+     * attempts the user can make to change the password before the SIM is locked.
+     * Using PUK code to unlock SIM if enter the incorrect old password 3 times.
+     *
+     * @param enabled    "true" for locked, "false" for unlocked.
+     * @param password   needed to change the ICC pin state, aka. Pin1
+     * @return an integer representing the status of IccLock enabled or disabled in the following
+     * three cases:
+     *   - {@link TelephonyManager#CHANGE_ICC_LOCK_SUCCESS} if enabled or disabled IccLock
+     *   successfully.
+     *   - Positive number and zero for remaining password attempts.
+     *   - Negative number for other failure cases (such like enabling/disabling PIN failed).
+     *
+     * @throws SecurityException if the caller doesn't have the permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public int setIccLockEnabled(boolean enabled, @NonNull String password) {
+        checkNotNull(password, "setIccLockEnabled password can't be null.");
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.setIccLockEnabled(getSubId(), enabled, password);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "setIccLockEnabled RemoteException", e);
+        }
+        return 0;
+    }
+
+    /**
+     * Change the ICC password used in ICC pin lock.
+     *
+     * If the password was changed successfully, a value of {@link Integer#MAX_VALUE} is returned.
+     * If an incorrect old password is specified, the return value will indicate how many more
+     * attempts the user can make to change the password before the SIM is locked.
+     * Using PUK code to unlock SIM if enter the incorrect old password 3 times.
+     *
+     * @param oldPassword is the old password
+     * @param newPassword is the new password
+     * @return an integer representing the status of IccLock changed in the following three cases:
+     *   - {@link TelephonyManager#CHANGE_ICC_LOCK_SUCCESS} if changed IccLock successfully.
+     *   - Positive number and zero for remaining password attempts.
+     *   - Negative number for other failure cases (such like enabling/disabling PIN failed).
+     *
+     * @throws SecurityException if the caller doesn't have the permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public int changeIccLockPassword(@NonNull String oldPassword, @NonNull String newPassword) {
+        checkNotNull(oldPassword, "changeIccLockPassword oldPassword can't be null.");
+        checkNotNull(newPassword, "changeIccLockPassword newPassword can't be null.");
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.changeIccLockPassword(getSubId(), oldPassword, newPassword);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "changeIccLockPassword RemoteException", e);
+        }
+        return 0;
+    }
 }
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index 994c49c..e52be44 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.service.euicc.EuiccProfileInfo;
@@ -165,7 +166,12 @@
                     new IGetAllProfilesCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo[] profiles) {
-                            executor.execute(() -> callback.onComplete(resultCode, profiles));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profiles));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -189,7 +195,12 @@
                     new IGetProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo profile) {
-                            executor.execute(() -> callback.onComplete(resultCode, profile));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profile));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -214,7 +225,12 @@
                     refresh, new IDisableProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -240,7 +256,12 @@
                     refresh, new ISwitchToProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo profile) {
-                            executor.execute(() -> callback.onComplete(resultCode, profile));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profile));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -265,7 +286,12 @@
                     nickname, new ISetNicknameCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -289,7 +315,12 @@
                     new IDeleteProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -314,7 +345,12 @@
                     new IResetMemoryCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -337,7 +373,12 @@
                     new IGetDefaultSmdpAddressCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, String address) {
-                            executor.execute(() -> callback.onComplete(resultCode, address));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, address));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -360,7 +401,12 @@
                     new IGetSmdsAddressCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, String address) {
-                            executor.execute(() -> callback.onComplete(resultCode, address));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, address));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -385,7 +431,12 @@
                     new ISetDefaultSmdpAddressCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -408,7 +459,12 @@
                     new IGetRulesAuthTableCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccRulesAuthTable rat) {
-                            executor.execute(() -> callback.onComplete(resultCode, rat));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, rat));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -431,7 +487,12 @@
                     new IGetEuiccChallengeCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] challenge) {
-                            executor.execute(() -> callback.onComplete(resultCode, challenge));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, challenge));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -454,7 +515,12 @@
                     new IGetEuiccInfo1Callback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] info) {
-                            executor.execute(() -> callback.onComplete(resultCode, info));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, info));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -477,7 +543,12 @@
                     new IGetEuiccInfo2Callback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] info) {
-                            executor.execute(() -> callback.onComplete(resultCode, info));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, info));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -519,7 +590,12 @@
                     new IAuthenticateServerCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            executor.execute(() -> callback.onComplete(resultCode, response));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -558,7 +634,12 @@
                     new IPrepareDownloadCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            executor.execute(() -> callback.onComplete(resultCode, response));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -586,7 +667,12 @@
                     new ILoadBoundProfilePackageCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            executor.execute(() -> callback.onComplete(resultCode, response));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -616,7 +702,12 @@
                     new ICancelSessionCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            executor.execute(() -> callback.onComplete(resultCode, response));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -640,7 +731,13 @@
                     new IListNotificationsCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification[] notifications) {
-                            executor.execute(() -> callback.onComplete(resultCode, notifications));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(
+                                        resultCode, notifications));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -664,7 +761,13 @@
                     events, new IRetrieveNotificationListCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification[] notifications) {
-                            executor.execute(() -> callback.onComplete(resultCode, notifications));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(
+                                        resultCode, notifications));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -688,7 +791,13 @@
                     seqNumber, new IRetrieveNotificationCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification notification) {
-                            executor.execute(() -> callback.onComplete(resultCode, notification));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(
+                                        resultCode, notification));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -715,7 +824,12 @@
                     new IRemoveNotificationFromListCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 3a6269b..5c18cdd 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2254,4 +2254,10 @@
      * Notify Rcs auto config received.
      */
     void notifyRcsAutoConfigurationReceived(int subId, in byte[] config, boolean isCompressed);
+
+    boolean isIccLockEnabled(int subId);
+
+    int setIccLockEnabled(int subId, boolean enabled, String password);
+
+    int changeIccLockPassword(int subId, String oldPassword, String newPassword);
 }
diff --git a/tests/BootImageProfileTest/OWNERS b/tests/BootImageProfileTest/OWNERS
new file mode 100644
index 0000000..657b3f2
--- /dev/null
+++ b/tests/BootImageProfileTest/OWNERS
@@ -0,0 +1,4 @@
+mathieuc@google.com
+calin@google.com
+yawanng@google.com
+sehr@google.com
diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp
index 74dfde8..342c47d 100644
--- a/tests/PlatformCompatGating/Android.bp
+++ b/tests/PlatformCompatGating/Android.bp
@@ -18,6 +18,7 @@
     name: "PlatformCompatGating",
     // Only compile source java files in this apk.
     srcs: ["src/**/*.java"],
+    test_suites: ["device-tests"],
     static_libs: [
         "junit",
         "androidx.test.runner",
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index b2f384a..124b660 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -8,7 +8,6 @@
         "libbacktrace",
         "libbase",
         "libbinder",
-        "libbinderthreadstate",
         "libbpf",
         "libbpf_android",
         "libc++",